mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-02-28 18:53:37 +00:00
* configuration for the game world server to connect to clients across a number of socket-port connections following a simple numerical load balancing policy * combining port management into the sector pane * mostly spelling issues
327 lines
7.7 KiB
Scala
327 lines
7.7 KiB
Scala
package net.psforever.util
|
|
|
|
import java.nio.file.Paths
|
|
import com.typesafe.config.{Config => TypesafeConfig}
|
|
import enumeratum.{Enum, EnumEntry}
|
|
import enumeratum.values.{IntEnum, IntEnumEntry}
|
|
import net.psforever.objects.avatar.{BattleRank, Certification, CommandRank}
|
|
import net.psforever.packet.game.ServerType
|
|
import net.psforever.types.ChatMessageType
|
|
import pureconfig.ConfigConvert.viaNonEmptyStringOpt
|
|
import pureconfig.{ConfigConvert, ConfigSource}
|
|
import scala.concurrent.duration._
|
|
import scala.reflect.ClassTag
|
|
import pureconfig.generic.auto._ // intellij: this is not unused
|
|
|
|
object Config {
|
|
private val logger = org.log4s.getLogger
|
|
|
|
// prog.home is defined when we are running from SBT pack
|
|
val directory: String = System.getProperty("prog.home") match {
|
|
case null =>
|
|
Paths.get("config").toAbsolutePath.toString
|
|
case home =>
|
|
Paths.get(home, "config").toAbsolutePath.toString
|
|
}
|
|
|
|
implicit def enumeratumIntConfigConvert[A <: IntEnumEntry](implicit
|
|
e: IntEnum[A],
|
|
ct: ClassTag[A]
|
|
): ConfigConvert[A] =
|
|
viaNonEmptyStringOpt[A](
|
|
v =>
|
|
e.values.toList.collectFirst {
|
|
case e: ServerType if e.name == v => e.asInstanceOf[A]
|
|
case e: BattleRank if e.value.toString == v => e.asInstanceOf[A]
|
|
case e: CommandRank if e.value.toString == v => e.asInstanceOf[A]
|
|
case e: Certification if e.name == v => e.asInstanceOf[A]
|
|
},
|
|
_.value.toString
|
|
)
|
|
|
|
implicit def enumeratumConfigConvert[A <: EnumEntry](implicit
|
|
e: Enum[A],
|
|
ct: ClassTag[A]
|
|
): ConfigConvert[A] =
|
|
viaNonEmptyStringOpt[A](
|
|
v =>
|
|
e.values.toList.collectFirst {
|
|
case e if e.toString.toLowerCase == v.toLowerCase => e
|
|
},
|
|
_.toString
|
|
)
|
|
|
|
private val source = {
|
|
val configFile = Paths.get(directory, "psforever.conf").toFile
|
|
if (configFile.exists)
|
|
ConfigSource.file(configFile).withFallback(ConfigSource.default)
|
|
else
|
|
ConfigSource.default
|
|
}
|
|
|
|
// Raw config object - prefer app when possible
|
|
lazy val config: TypesafeConfig = source.config() match {
|
|
case Right(config) => config
|
|
case Left(failures) =>
|
|
logger.error("Loading config failed")
|
|
failures.toList.foreach { failure =>
|
|
logger.error(failure.toString)
|
|
}
|
|
sys.exit(1)
|
|
}
|
|
|
|
// Typed config object
|
|
lazy val app: AppConfig = source.load[AppConfig] match {
|
|
case Right(config) => config
|
|
case Left(failures) =>
|
|
logger.error("Loading config failed")
|
|
failures.toList.foreach { failure =>
|
|
logger.error(failure.toString)
|
|
}
|
|
sys.exit(1)
|
|
}
|
|
}
|
|
|
|
case class AppConfig(
|
|
bind: String,
|
|
public: String,
|
|
login: LoginConfig,
|
|
world: WorldConfig,
|
|
admin: AdminConfig,
|
|
database: DatabaseConfig,
|
|
game: GameConfig,
|
|
antiCheat: AntiCheatConfig,
|
|
network: NetworkConfig,
|
|
development: DevelopmentConfig,
|
|
kamon: KamonConfig,
|
|
sentry: SentryConfig
|
|
)
|
|
|
|
case class LoginConfig(
|
|
port: Int,
|
|
createMissingAccounts: Boolean
|
|
)
|
|
|
|
case class WorldConfig(
|
|
port: Int,
|
|
ports: Seq[Int],
|
|
serverName: String,
|
|
serverType: ServerType
|
|
)
|
|
|
|
case class AdminConfig(
|
|
port: Int,
|
|
bind: String
|
|
)
|
|
|
|
case class DatabaseConfig(
|
|
host: String,
|
|
port: Int,
|
|
username: String,
|
|
password: String,
|
|
database: String,
|
|
sslmode: String
|
|
) {
|
|
def toJdbc = s"jdbc:postgresql://$host:$port/$database"
|
|
}
|
|
|
|
case class AntiCheatConfig(
|
|
hitPositionDiscrepancyThreshold: Int
|
|
)
|
|
|
|
case class NetworkConfig(
|
|
session: SessionConfig,
|
|
middleware: MiddlewareConfig
|
|
)
|
|
|
|
case class MiddlewareConfig(
|
|
packetBundlingDelay: FiniteDuration,
|
|
packetBundlingDelayMultiplier: Float,
|
|
inReorderTimeout: FiniteDuration,
|
|
inSubslotMissingDelay: FiniteDuration,
|
|
inSubslotMissingAttempts: Int
|
|
)
|
|
|
|
case class SessionConfig(
|
|
inboundGraceTime: FiniteDuration,
|
|
outboundGraceTime: FiniteDuration
|
|
)
|
|
|
|
case class GameConfig(
|
|
instantAction: InstantActionConfig,
|
|
amenityAutorepairRate: Float,
|
|
amenityAutorepairDrainRate: Float,
|
|
newAvatar: NewAvatar,
|
|
hart: HartConfig,
|
|
sharedMaxCooldown: Boolean,
|
|
sharedBfrCooldown: Boolean,
|
|
baseCertifications: Seq[Certification],
|
|
warpGates: WarpGateConfig,
|
|
cavernRotation: CavernRotationConfig,
|
|
savedMsg: SavedMessageEvents,
|
|
playerDraw: PlayerStateDrawSettings,
|
|
doorsCanBeOpenedByMedAppFromThisDistance: Float,
|
|
experience: Experience,
|
|
maxBattleRank: Int,
|
|
promotion: PromotionSystem
|
|
)
|
|
|
|
case class InstantActionConfig(
|
|
spawnOnAms: Boolean,
|
|
thirdParty: Boolean
|
|
)
|
|
|
|
case class NewAvatar(
|
|
br: BattleRank,
|
|
cr: CommandRank
|
|
)
|
|
|
|
case class HartConfig(
|
|
inFlightDuration: Long,
|
|
boardingDuration: Long
|
|
)
|
|
|
|
case class DevelopmentConfig(
|
|
unprivilegedGmCommands: Seq[ChatMessageType],
|
|
unprivilegedGmBangCommands: Seq[String],
|
|
netSim: NetSimConfig
|
|
)
|
|
|
|
case class NetSimConfig(
|
|
enable: Boolean,
|
|
loss: Double,
|
|
delay: Duration,
|
|
reorderChance: Double,
|
|
reorderTime: Duration
|
|
)
|
|
|
|
case class KamonConfig(
|
|
enable: Boolean
|
|
)
|
|
|
|
case class SentryConfig(
|
|
enable: Boolean,
|
|
dsn: String
|
|
)
|
|
|
|
case class WarpGateConfig(
|
|
defaultToSanctuaryDestination: Boolean,
|
|
broadcastBetweenConflictedFactions: Boolean
|
|
)
|
|
|
|
case class CavernRotationConfig(
|
|
hoursBetweenRotation: Float,
|
|
simultaneousUnlockedZones: Int,
|
|
enhancedRotationOrder: Seq[Int],
|
|
forceRotationImmediately: Boolean
|
|
)
|
|
|
|
case class SavedMessageEvents(
|
|
short: SavedMessageTimings,
|
|
renewal: SavedMessageTimings,
|
|
interruptedByAction: SavedMessageTimings
|
|
)
|
|
|
|
case class SavedMessageTimings(
|
|
fixed: Long,
|
|
variable: Long
|
|
)
|
|
|
|
case class PlayerStateDrawSettings(
|
|
populationThreshold: Int,
|
|
populationStep: Int,
|
|
rangeMin: Int,
|
|
rangeMax: Int,
|
|
rangeStep: Int,
|
|
ranges: Seq[Int],
|
|
delayMax: Long,
|
|
delays: Seq[Long]
|
|
) {
|
|
assert(ranges.nonEmpty)
|
|
assert(ranges.size == delays.size)
|
|
}
|
|
|
|
case class Experience(
|
|
shortContributionTime: Long,
|
|
longContributionTime: Long,
|
|
bep: BattleExperiencePoints,
|
|
sep: SupportExperiencePoints,
|
|
cep: CommandExperiencePoints,
|
|
facilityCaptureRate: Float
|
|
) {
|
|
assert(shortContributionTime < longContributionTime)
|
|
}
|
|
|
|
case class ThreatAssessment(
|
|
id: Int,
|
|
value: Float
|
|
)
|
|
|
|
case class ThreatLevel(
|
|
id: Int,
|
|
level: Long
|
|
)
|
|
|
|
case class BattleExperiencePoints(
|
|
rate: Float,
|
|
base: BattleExperiencePointsBase,
|
|
lifeSpan: BattleExperiencePointsLifespan,
|
|
revenge: BattleExperiencePointsRevenge
|
|
)
|
|
|
|
case class BattleExperiencePointsBase(
|
|
bopsMultiplier: Long,
|
|
asMax: Long,
|
|
withKills: Long,
|
|
asMounted: Long,
|
|
mature: Long,
|
|
maturityTime: Long
|
|
)
|
|
|
|
case class BattleExperiencePointsLifespan(
|
|
lifeSpanThreatRate: Float,
|
|
threatAssessmentOf: List[ThreatAssessment],
|
|
maxThreatLevel: List[ThreatLevel]
|
|
)
|
|
|
|
case class BattleExperiencePointsRevenge(
|
|
rate: Float,
|
|
defaultExperience: Long,
|
|
maxExperience: Long
|
|
)
|
|
|
|
case class SupportExperiencePoints(
|
|
rate: Float,
|
|
ntuSiloDepositReward: Long,
|
|
canNotFindEventDefaultValue: Long,
|
|
events: Seq[SupportExperienceEvent]
|
|
)
|
|
|
|
case class SupportExperienceEvent(
|
|
name: String,
|
|
base: Long,
|
|
shotsMax: Int = 50,
|
|
shotsCutoff: Int = 50,
|
|
shotsMultiplier: Float = 0f,
|
|
amountMultiplier: Float = 0f
|
|
)
|
|
|
|
case class CommandExperiencePoints(
|
|
rate: Float,
|
|
lluCarrierModifier: Float,
|
|
lluSlayerCreditDuration: Duration,
|
|
lluSlayerCredit: Long,
|
|
maximumPerSquadSize: Seq[Int],
|
|
squadSizeLimitOverflow: Int,
|
|
squadSizeLimitOverflowMultiplier: Float
|
|
)
|
|
|
|
case class PromotionSystem(
|
|
active: Boolean,
|
|
broadcastBattleRank: Int,
|
|
resetBattleRank: Int,
|
|
maxBattleRank: Int,
|
|
battleExperiencePointsModifier: Float,
|
|
supportExperiencePointsModifier: Float,
|
|
captureExperiencePointsModifier: Float
|
|
)
|