# The socket bind address for all net.psforever.services except admin. 127.0.0.1 is the
# default for local testing, for public servers use 0.0.0.0 instead.
bind = 127.0.0.1

# The public host name or IP address. Used to forward clients from the login
# server to the world server. The default value will only allow local connections.
public = 127.0.0.1

# Login server configuration
login {
  # UDP listening port
  port = 51000

  # Account usernames that don't exist yet will be automatically created in the
  # database. Useful for test servers and development testing.
  create-missing-accounts = yes
}

# World server configuration
world {
  # UDP listening port
  port = 51001
  ports = []

  # The name of the server as displayed in the server browser.
  server-name = "\\#1EACF9P\\#E87BE8S\\#F93F4E4ever"

  # How the server is displayed in the server browser.
  # One of: released beta development
  server-type = development
}

# Admin API configuration
admin {
  # TCP listening port
  port = 51002

  # The socket bind address
  bind = 127.0.0.1
}

# Database configuration
# Full reference: https://github.com/jasync-sql/jasync-sql/wiki/Configuring-and-Managing-Connections
# https://github.com/getquill/quill/blob/master/quill-jasync/src/main/scala/io/getquill/context/jasync/JAsyncContextConfig.scala
database {
  # The hostname of the PostgreSQL server.
  host = localhost

  # The TCP port to connect to the database with.
  port = 5432

  # The username to connect to the SQL server with.
  username = psforever

  # The password to connect to the SQL server with.
  password = psforever

  # The database name to use on the SQL server.
  database = psforever

  # The SSL configuration of the database connection.
  # One of: disable prefer require verify-full
  sslmode = prefer

  # The maximum number of active connections.
  maxActiveConnections = 5
}

# Enable non-standard game properties
game {
  instant-action = {
    # Allow instant action to direct a player to a hotspot-local friendly AMS
    spawn-on-ams = no

    # If no ally hotspots can be found during instant action, find a friendly spawn closest to an enemy hot spot
    third-party = no
  }

  # Modify the amount of mending per autorepair tick for facility amenities
  amenity-autorepair-rate = 1.0

  # Modify the amount of NTU drain per autorepair tick for facility amenities
  amenity-autorepair-drain-rate = 0.5

  # Purchases timers for the mechanized assault exo-suits all update at the same time when any of them would update
  shared-max-cooldown = no

  # Purchases timers for the battleframe robotics vehicles all update at the same time when either of them would update
  shared-bfr-cooldown = yes

  # How long the countdown timer is when a facility (main overworld base) is hacked
  facility-hack-time = 15.minutes

  # HART system, shuttles and facilities
  hart {
    # How long the shuttle is not boarding passengers (going through the motions)
    in-flight-duration = 225000

    # How long the shuttle allows passengers to board
    boarding-duration = 60000
  }

  new-avatar {
    # Starting battle rank
    br = 1

    # Starting command rank
    cr = 0
  }

  # Certifications to add to every character. Note that they do not become free, their cost is deducted from the
  # player's certification points. The default base certifications are 0 cost.
  # Values are `Certification` member names.
  base-certifications = [
      standard_assault,
      standard_armor,
      agile_armor
  ]

  warp-gates {
    # When a gating fails, fall back to sanctuary rather than stay in the same zone
    default-to-sanctuary-destination = yes

    # When a facility next to a warp gate is captured by one faction,
    # if the facility on the other side of the intercontinental gate pair is owned by a different faction,
    # that gate pair becomes a broadcast warp gate for those factions
    broadcast-between-conflicted-factions = yes
  }

  cavern-rotation = {
    # The number of hours between any given cavern locking and another cavern unlocking,
    # not the total number of hours between a single cavern locking and unlocking.
    hours-between-rotation = 3

    # How many caverns are unlocked at once during rotations
    # Pay attention to the logs that a corresponding combinational existence found in zonemaps/lattice.json
    # Examples:
    # [a,b,c] with 1 requires 'caverns-a' 'caverns-b' 'caverns-c'
    # [a,b,c] with 2 requires 'caverns-a-b' 'caverns-b-c' 'caverns-a-c'
    # [a,b,c] with 3 requires 'caverns-a-b-c'
    # [a,b,c,d] with 3 requires 'caverns-a-b-c' 'caverns-b-c-d' 'caverns-a-c-d' 'caverns-a-b-d'
    simultaneous-unlocked-zones = 2

    # A list of zone numbers that correspond to the caverns, in a continuous order
    # in which the caverns are locked and are unlocked.
    # When left empty, the order of the caverns is traversed as-is provided
    # For example, for cavern zones with number [23, 24, 25, 26, 27, 28],
    # the order [23, 24, 26, 23, 24, 25, 26] would eliminate #27 and #28 from the rotation
    enhanced-rotation-order = [23, 24, 25, 26, 27, 28]

    # If a cavern rotation is forced by the system,
    # the system will attempt to advance only to the first possible closing warning message at five minutes
    # When set, however, the next zone unlock is carried out regardless of the amount of time remaining
    force-rotation-immediately = false
  }

  saved-msg = {
    # A brief delay to the @charsaved message, in seconds.
    # Use this when the message should display very soon for any reason.
    short = {
      # This delay always occurs
      fixed = 5
      # This delay is applied partially - fixed + [0,delay)
      variable = 5
    }

    # A delay to the @charsaved message whenever a previous message has been displayed, in seconds.
    # Used as the default interval between messages.
    # It should provide assurance to the player even if nothing happened.
    # Actual database interaction not assured.
    renewal = {
      fixed = 300
      variable = 600
    }

    # A delay to the @charsaved message
    # whenever an action that would cause actual database interaction occurs, in seconds.
    # Actual database interaction not assured.
    # The variability, in this case, serves the purpose of hedging against other activity by the player
    # that would trigger the message again in a short amount of time.
    interrupted-by-action = {
      fixed = 15
      variable = 30
    }
  }

  # Limits the dispatch of PlayerStateMessage packets from a session to its client.
  # Specifically, a packet will only be dispatched based on whether
  # it is to be dispatched over a threshold time after the previous dispatch time.
  # The delay between packets is determined by a distance between the observer player and the target player.
  # Only affects PlayerStateMessage.
  player-draw = {
    # Minimum number of players within a given region before scaling down the range.
    # Total population - population threshold = overpopulation
    population-threshold = 20
    # Number of players over threshold before the reduction in range is scaled down.
    # Population step / overpopulation = range step factor (integer)
    population-step = 5
    # Always send packets regarding target players within the given distance.
    range-min = 50
    # Do not send packets regarding target players beyond this distance. (sq.m)
    # Treat this as an absolute render distance.
    range-max = 550
    # Number of meters reduced from range based on population count over threshold value. (m)
    # Range step * range step factor = total distance to remove from actual distance
    range-step = 25
    # The distances above min-range where different delays are allowed before a successful packet must be dispatched. [m]
    # Test distances against entries to find the last one that is not more than the sample distance.
    # Use the index of that sample distance from this sequence in the sequence `delays` below.
    ranges = [150, 300, 400]
    # The absolute time delay before a successful packet must be dispatched regardless of distance. (s)
    delay-max = 1500
    # The time delays for each distance range before a successful packet must be dispatched. [s]
    # The index for an entry in this sequence is expected to be discovered using the `ranges` sequence above.
    # Delays between packets may not be as precise as desired
    # as the heartbeat of upstream packets are measured in quanta of 250ms usually.
    # As a consequence, additional entries with proper time spacing will push back the next proper update considerably
    # while additional entries with insufficient time spacing may result in no change in behavior.
    delays = [350, 600, 800]
  }

  # Don't ask.
  doors-can-be-opened-by-med-app-from-this-distance = 5.05

  # How the experience calculates
  experience {
    # The short contribution time when events are collected and evaluated.
    short-contribution-time = 300000
    # The long contribution time when events are collected and evaluated
    # even factoring the same events from the short contribution time.
    # As a result, when comparing the two event lists, similar actors may appear
    # but their contributions may be different.
    long-contribution-time = 600000

    # Battle experience points
    # BEP is to be calculated in relation to how valuable a kill is worth.
    bep = {
      # After all calculations are complete, multiply the result by this value
      rate = 1.0
      # These numbers are to determine the starting value for a particular kill
      base = {
        # Black Ops multiplies the base value by this much
        bops-multiplier = 10.0
        # If the player who died ever utilized a mechanized assault exo-suit
        as-max = 250
        # The player who died got at least one kill
        with-kills = 150
        # The player who died was mounted in a vehicle at the time of death
        as-mounted = 100
        # The player who died after having been in the game world for a while after spawning.
        # Dying before this is often called a "spawn kill".
        mature = 50
        # How long it normally takes for a player who has respawned to naturally lose the status of "green" when being inactive.
        # See `base.nature`.
        maturity-time = 30000
      }
      life-span = {
        # The experience value of a player's lifespan is measured in intervals.
        # Per interval, after all calculations are complete, multiply the result by this value
        life-span-threat-rate = 1.0
        # The experience value of using certain equipment per interval of time during playtime (consider to be per second).
        # (key, value) where key is technically the index of an ExoSuitType or an object class id and value is the growth
        threat-assessment-of = [
          {
            id = 0
            value = 1.25
          },
          {
            id = 1
            value = 1.5
          },
          {
            id = 2
            value = 2.15
          },
          {
            id = 3
            value = 1.25
          },
          {
            id = 4
            value = 1.0
          },
          {
            id = 258
            value = 10.0
          },
          {
            id = 410
            value = 0
          },
          {
            id = 608
            value = 0
          }
        ]
        # The maximum experience ceiling during playtime based on the use of certain equipment.
        # (key, value) where key is technically the index of an ExoSuitType or an object class id and value is the maximum
        max-threat-level = [
          {
            id = 0
            level = 2000
          },
          {
            id = 1
            level = 2000
          },
          {
            id = 2
            level = 5000
          },
          {
            id = 3
            level = 2000
          },
          {
            id = 4
            level = 900
          },
          {
            id = 258
            level = 0
          },
          {
            id = 410
            level = 0
          },
          {
            id = 608
            level = 0
          }
        ]
      }
      revenge = {
        # If player A kills another player B who killed player A just previously, this is the percentage of experience to deposit.
        # The kill event must have been the exact previous life's death after revive or respawn.
        # This only applies if experience is set to 0.
        # Set to zero and experience = 0 to ignore revenge.
        rate = 0.05
        # If player A kills another player B who killed player A just previously, deposit this experience.
        # The kill event must have been the exact previous life's death after revive or respawn.
        # Set to zero to reuse the experience value from the previous kill event.
        default-experience = 0
        # When using the experience from the previous kill for revenge bonus, cap the reward at this value.
        max-experience = 350
      }
    }

    # Support experience points
    # The events from which support experience rises are numerous.
    # Calculation is determined by the selection of an "event" that decides how the values are combined.
    sep = {
      # After all calculations are complete, multiply the result by this value
      rate = 1.0
      # When using an advanced nanite transport to deposit into the resource silo of a major facility,
      # for reaching the maximum amount of a single deposit,
      # reward the user with this amount of support experience points.
      # Small deposits reward only a percentage of this value.
      ntu-silo-deposit-reward = 100
      # When the event can not be found, this flat sum is rewarded.
      # This should not be treated as a feature.
      # It is a bug.
      # Check your event label calls.
      can-not-find-event-default-value = 15
      # The events by which support experience calculation occurs.
      # Events can be composed of three parts: a base value, a per-use (shots) value, and an active amount value.
      # "Per-use" relies on knowledge from the server about the number of times this exact action occurred before the event.
      # "Active amount" relies on knowledge from the server about how much of the changes for this event are still valid.
      # Some changes can be undone by other events or other behavior.
      #
      # name - label by which this event is organized
      # base - whole number value
      # shots-min - lower limit of use count
      #           - minimum amount of shots required before applying multiplier
      # shots-max - upper limit of use count
      #           - cap the count here, if higher
      # shots-cutoff - if the use count exceeds this number, the event no longer applies
      #              - a hard limit that should zero the contribution reward
      # shots-multiplier - whether use count matters for this event
      #                  - when set to 0.0 (default), it does not
      # amount-multiplier - whether active amount matters for this event
      #                   - when set to 0.0 (default), it does not
      events = [
        {
          name = "support-heal"
          base = 10
          shots-multiplier = 5.0
          shots-max = 100
          amount-multiplier = 2.0
        }
        {
          name = "support-repair"
          base = 10
          shots-multiplier = 5.0
          shots-max = 100
        }
        {
          name = "support-repair-terminal"
          base = 10
          shots-multiplier = 5.0
          shots-max = 100
        }
        {
          name = "support-repair-turret"
          base = 10
          shots-multiplier = 5.0
          shots-max = 100
        }
        {
          name = "mounted-kill"
          base = 25
        }
        {
          name = "router-driver"
          base = 15
          shots-multiplier = 1.0
        }
        {
          name = "telepad-use"
          base = 20
          shots-multiplier = 1.0
        }
        {
          name = "hotdrop"
          base = 25
        }
        {
          name = "hack"
          base = 5
          amount-multiplier = 5.0
        }
        {
          name = "ams-resupply"
          base = 15
          shots-multiplier = 1.0
        }
        {
          name = "lodestar-repair"
          base = 10
          shots-multiplier = 1.0
          shots-max = 100
          amount-multiplier = 1.0
        }
        {
          name = "lodestar-rearm"
          base = 10
          shots-multiplier = 1.0
        }
        {
          name = "revival"
          base = 0
          shots-multiplier = 15.0
          shots-cutoff = 10
        }
      ]
    }

    # Command experience points
    cep = {
      # After all calculations are complete, multiply the result by this value
      rate = 1.0
      # When command experience points are rewarded to the lattice link unit carrier,
      # modify the original value by this modifier.
      llu-carrier-modifier = 0.5
      # If a player died while carrying an lattice logic unit,
      # award the player who is accredited with the kill command experience as long as the time it had been carried longer than this duration.
      # Can set to Duration.Inf to never pass.
      llu-slayer-credit-duration = 30 seconds
      # If a player died while carrying an lattice logic unit,
      # and satisfies the carrying duration,
      # award the player who is accredited with the kill command experience.
      llu-slayer-credit = 200
      # The maximum command experience that can be earned in a facility capture based on squad size
      maximum-per-squad-size = [990, 1980, 3466, 4950, 6436, 7920, 9406, 10890, 12376, 13860]
      # When the cep has to be capped for squad size, add a small value to the capped value
      # This is that value
      # -1 reuses the cep before being capped
      squad-size-limit-overflow = -1
      # When the cep has to be capped for squad size, calculate a small amount to add to the capped value
      squad-size-limit-overflow-multiplier = 0.2
    }
    # When summing bep to produce facility capture base rewards, multiply the result by this value
    facility-capture-rate = 0.5
  }

  # The game's official maximum battle rank is 40.
  # This is an artificial cap that attempts to stop advancement long before that.
  # After becoming this battle rank, battle experience points gain will be locked.
  # In our case, we're imposing this because character features can be unstable when above BR24.
  max-battle-rank = 24

  promotion {
    # Whether promotion versus play is offered at battle rank 1.
    # Anyone who is currently enrolled in the promotion system remains enrolled during normal game play.
    # Relenting on the promotion debt back to the reset battle rank is still possible.
    active = true
    # This battle rank and any battle ranks of ordinal decrement that allow opt-in to the progression system.
    broadcast-battle-rank = 1
    # This is the minimum battle rank that can be set as part of the promotion system.
    # Used to escape debt and return to normal play.
    reset-battle-rank = 5
    # This is the maximum battle rank that can be set as part of the promotion system.
    max-battle-rank = 13
    # How much direct combat contributes to paying back promotion debt.
    # Typically, it does not contribute.
    battle-experience-points-modifier = 0f
    support-experience-points-modifier = 3f
    capture-experience-points-modifier = 1f
    # Don't forget to pay back that debt.
  }
}

anti-cheat {
  # The distance (squared) threshold that triggers if the reported hit location
  # of a shot does not match the object being hit's location on the server
  # One of 1-1000000 (sqrt 10000 = ~100 ingame units)
  hit-position-discrepancy-threshold = 10000
}

network {
  middleware {
    # How often between executions of the outbound bundling process
    # Used for outbound packet arrival triggers
    packet-bundling-delay = 40 milliseconds

    # How often between executions of the outbound bundling process
    # Affects the base value on the timer
    packet-bundling-delay-multiplier = 1.25

    # Pause inbound packet transmission towards the network if the sequence number is out of order
    # Packets are put aside until the sequence is restored, or this timeout passes
    in-reorder-timeout = 50 milliseconds

    # Wait on inbound packets if that packet is a SlottedMetaPacket and the next subslot number is greater than expected
    # Does not stop the transmission of packets to the server
    # but dictates how long between requests to the network (client) for missing packets with anticipated subslot numbers
    in-subslot-missing-delay = 50 milliseconds

    # How many attempts at resolving missing packets with anticipated subslot numbers
    in-subslot-missing-attempts = 10
  }

  session {
    # The maximum amount of time since the last inbound packet from a UDP session
    # before it is dropped.
    inbound-grace-time = 1 minute

    # The maximum amount of time since the last outbound packet for a UDP session
    # before it is dropped. Can be used as a watchdog for hung server sessions.
    outbound-grace-time = 1 minute
  }
}

development {
  # List of GM commands made available to everyone
  # Values are `ChatMessageType` members, for example: [CMT_ADDBATTLEEXPERIENCE, CMT_CAPTUREBASE]
  unprivileged-gm-commands = []
  # List of GM bang commands made available to everyone
  # Since the commands come in as plain chat preceded by a bang (!) character, values are the names of the commands
  unprivileged-gm-bang-commands = []

  net-sim {
    # Enable artificial packet unreliability. Used for development testing.
    # Active equally on upstream and downstream packets.
    enable = no

    # The percentage of outgoing and incoming packets that are dropped.
    loss = 0.02

    # The time a packet is buffered before being delivered to simulate delay.
    # The artificial delay is in addition to any real network latency.
    delay = 150 milliseconds

    # The percentage chance that a packet will be ordered randomly in the delay
    # buffer. If the delay is too small then packets won't be reordered.
    reorder-chance = 0.005

    # If a packet is reordered, the maximum time in the future or the past where
    # it will randomly appear.
    reorder-time = 150 milliseconds
  }
}

kamon {
  # Enables reporting of metrics to Kamon.io
  enable = no

  environment.service = "PSForever"
  apm.api-key = ""
}

sentry {
  # Enables submission of warnings and errors to Sentry
  enable = no

  # Sentry DSN (Data Source Name)
  dsn = ""
}

include "akka.conf"
include "dispatchers.conf"
