mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-07-12 06:55:10 +00:00
Reduced Upstream/Downstream Load (#1052)
* an attempt to space out the player upstream * some data sterilizing * when a player is respawning, other players do not send their own update PSM's * always display players in their proper locations when nearing their view, even if they have been stationary this whole time * fixing configuration file and warnings
This commit is contained in:
parent
24ee12294a
commit
a1cf6c2701
6 changed files with 150 additions and 36 deletions
|
|
@ -183,6 +183,41 @@ game {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# 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 = 1000
|
||||||
|
# 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]
|
||||||
|
}
|
||||||
|
|
||||||
doors-can-be-opened-by-med-app-from-this-distance = 5.05
|
doors-can-be-opened-by-med-app-from-this-distance = 5.05
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1063,6 +1063,9 @@ class AvatarActor(
|
||||||
} else {
|
} else {
|
||||||
performAvatarLogin(avatarId, account.id, replyTo)
|
performAvatarLogin(avatarId, account.id, replyTo)
|
||||||
}
|
}
|
||||||
|
case Success(_) =>
|
||||||
|
//TODO this may not be an actual failure, but don't know what to do
|
||||||
|
sessionActor ! SessionActor.SendResponse(ActionResultMessage.Fail(error = 6))
|
||||||
case Failure(e) =>
|
case Failure(e) =>
|
||||||
log.error(e)("db failure")
|
log.error(e)("db failure")
|
||||||
sessionActor ! SessionActor.SendResponse(ActionResultMessage.Fail(error = 6))
|
sessionActor ! SessionActor.SendResponse(ActionResultMessage.Fail(error = 6))
|
||||||
|
|
|
||||||
|
|
@ -70,34 +70,84 @@ class SessionAvatarHandlers(
|
||||||
jumpThrust,
|
jumpThrust,
|
||||||
isCloaking,
|
isCloaking,
|
||||||
spectating,
|
spectating,
|
||||||
_
|
canSeeReallyFar
|
||||||
) if isNotSameTarget =>
|
) if isNotSameTarget =>
|
||||||
val now = System.currentTimeMillis()
|
val pstateToSave = pstate.copy(timestamp = 0)
|
||||||
val (location, time, distanceSq): (Vector3, Long, Float) = if (spectating) {
|
val (lastMsg, lastTime, lastPosition, wasSpectating, wasVisible) = lastSeenStreamMessage(guid.guid) match {
|
||||||
val r2 = 2 + hidingPlayerRandomizer.nextInt(4000).toFloat
|
case SessionAvatarHandlers.LastUpstream(Some(msg), visible, time) => (Some(msg), time, msg.pos, msg.spectator, visible)
|
||||||
(Vector3(r2, r2, 1f), 0L, 0f)
|
case _ => (None, 0L, Vector3.Zero, false, false)
|
||||||
} else {
|
|
||||||
val before = lastSeenStreamMessage(guid.guid).time
|
|
||||||
val dist = Vector3.DistanceSquared(player.Position, pos)
|
|
||||||
(pos, now - before, dist)
|
|
||||||
}
|
}
|
||||||
if (distanceSq < 302500 || time > 5000) { // Render distance seems to be approx 525m. Reduce update rate at ~550m to be safe
|
val drawConfig = Config.app.game.playerDraw
|
||||||
|
val maxRange = drawConfig.rangeMax * drawConfig.rangeMax //sq.m
|
||||||
|
val ourPosition = player.Position
|
||||||
|
val currentDistance = Vector3.DistanceSquared(ourPosition, pos) //sq.m
|
||||||
|
val inVisibleRange = currentDistance <= maxRange
|
||||||
|
val wasInVisibleRange = Vector3.DistanceSquared(lastPosition, pos) <= maxRange
|
||||||
|
val comingIntoVisibleRange = inVisibleRange && !wasInVisibleRange
|
||||||
|
val now = System.currentTimeMillis() //ms
|
||||||
|
val durationSince = now - lastTime //ms
|
||||||
|
val released = player.isReleased
|
||||||
|
if (!released &&
|
||||||
|
!spectating &&
|
||||||
|
(comingIntoVisibleRange || (inVisibleRange && !lastMsg.contains(pstateToSave)))) {
|
||||||
|
lazy val targetDelay = {
|
||||||
|
val populationOver = math.max(
|
||||||
|
0,
|
||||||
|
continent.blockMap.sector(ourPosition, range=drawConfig.rangeMax.toFloat).livePlayerList.size - drawConfig.populationThreshold
|
||||||
|
)
|
||||||
|
val distanceAdjustment = math.pow(populationOver / drawConfig.populationStep * drawConfig.rangeStep, 2) //sq.m
|
||||||
|
val adjustedDistance = currentDistance + distanceAdjustment //sq.m
|
||||||
|
drawConfig.ranges.lastIndexWhere { dist => adjustedDistance > dist * dist } match {
|
||||||
|
case -1 => 1
|
||||||
|
case index => drawConfig.delays(index)
|
||||||
|
}
|
||||||
|
} //ms
|
||||||
|
if (comingIntoVisibleRange ||
|
||||||
|
canSeeReallyFar ||
|
||||||
|
currentDistance < drawConfig.rangeMin * drawConfig.rangeMin ||
|
||||||
|
durationSince > drawConfig.delayMax ||
|
||||||
|
sessionData.canSeeReallyFar ||
|
||||||
|
durationSince > targetDelay) {
|
||||||
|
//must draw
|
||||||
|
sendResponse(
|
||||||
|
PlayerStateMessage(
|
||||||
|
guid,
|
||||||
|
pos,
|
||||||
|
vel,
|
||||||
|
yaw,
|
||||||
|
pitch,
|
||||||
|
yawUpper,
|
||||||
|
timestamp = 0, //is this okay?
|
||||||
|
isCrouching,
|
||||||
|
isJumping,
|
||||||
|
jumpThrust,
|
||||||
|
isCloaking
|
||||||
|
)
|
||||||
|
)
|
||||||
|
lastSeenStreamMessage(guid.guid) = SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=true, now)
|
||||||
|
} else {
|
||||||
|
lastSeenStreamMessage(guid.guid) = SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=false, lastTime)
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
(!spectating && wasInVisibleRange && !inVisibleRange) ||
|
||||||
|
(inVisibleRange && !wasSpectating && spectating) ||
|
||||||
|
(wasVisible && released)
|
||||||
|
) {
|
||||||
|
//must hide
|
||||||
|
val lat = (1 + hidingPlayerRandomizer.nextInt(continent.map.scale.height.toInt)).toFloat
|
||||||
sendResponse(
|
sendResponse(
|
||||||
PlayerStateMessage(
|
PlayerStateMessage(
|
||||||
guid,
|
guid,
|
||||||
location,
|
Vector3(1f, lat, 1f),
|
||||||
vel,
|
vel=None,
|
||||||
yaw,
|
facingYaw=0f,
|
||||||
pitch,
|
facingPitch=0f,
|
||||||
yawUpper,
|
facingYawUpper=0f,
|
||||||
timestamp = 0,
|
timestamp=0, //is this okay?
|
||||||
isCrouching,
|
is_cloaked = isCloaking
|
||||||
isJumping,
|
|
||||||
jumpThrust,
|
|
||||||
isCloaking
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
lastSeenStreamMessage(guid.guid) = SessionAvatarHandlers.LastUpstream(Some(pstate), now)
|
lastSeenStreamMessage(guid.guid) = SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=false, now)
|
||||||
}
|
}
|
||||||
|
|
||||||
case AvatarResponse.ObjectHeld(slot, _)
|
case AvatarResponse.ObjectHeld(slot, _)
|
||||||
|
|
@ -500,9 +550,9 @@ class SessionAvatarHandlers(
|
||||||
}
|
}
|
||||||
|
|
||||||
object SessionAvatarHandlers {
|
object SessionAvatarHandlers {
|
||||||
private[support] case class LastUpstream(msg: Option[AvatarResponse.PlayerState], time: Long)
|
private[support] case class LastUpstream(msg: Option[AvatarResponse.PlayerState], visible: Boolean, time: Long)
|
||||||
|
|
||||||
private[support] def blankUpstreamMessages(n: Int): Array[LastUpstream] = {
|
private[support] def blankUpstreamMessages(n: Int): Array[LastUpstream] = {
|
||||||
Array.fill[SessionAvatarHandlers.LastUpstream](n)(elem = LastUpstream(None, 0L))
|
Array.fill[SessionAvatarHandlers.LastUpstream](n)(elem = LastUpstream(None, visible=false, 0L))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2711,19 +2711,28 @@ class SessionData(
|
||||||
}
|
}
|
||||||
|
|
||||||
def canSeeReallyFar: Boolean = {
|
def canSeeReallyFar: Boolean = {
|
||||||
findEquipment().exists {
|
shooting.FindContainedWeapon match {
|
||||||
case weapon: Tool
|
case (Some(_: Vehicle), weapons) if weapons.nonEmpty =>
|
||||||
if weapon.Size == EquipmentSize.Rifle &&
|
player.avatar
|
||||||
(weapon.Projectile ne GlobalDefinitions.no_projectile) &&
|
.implants
|
||||||
player.Crouching &&
|
.exists { p =>
|
||||||
player.avatar
|
p.collect { implant => implant.definition.implantType == ImplantType.RangeMagnifier && implant.active }.nonEmpty
|
||||||
.implants
|
}
|
||||||
.exists { p =>
|
case (Some(_: Player), weapons) if weapons.nonEmpty =>
|
||||||
p.collect { implant => implant.definition.implantType == ImplantType.RangeMagnifier && implant.initialized }.nonEmpty
|
val wep = weapons.head
|
||||||
} =>
|
wep.Definition == GlobalDefinitions.bolt_driver ||
|
||||||
true
|
wep.Definition == GlobalDefinitions.heavy_sniper ||
|
||||||
case item =>
|
(
|
||||||
item.Definition == GlobalDefinitions.bolt_driver || item.Definition == GlobalDefinitions.heavy_sniper
|
(wep.Projectile ne GlobalDefinitions.no_projectile) &&
|
||||||
|
player.Crouching &&
|
||||||
|
player.avatar
|
||||||
|
.implants
|
||||||
|
.exists { p =>
|
||||||
|
p.collect { implant => implant.definition.implantType == ImplantType.RangeMagnifier && implant.active }.nonEmpty
|
||||||
|
}
|
||||||
|
)
|
||||||
|
case _ =>
|
||||||
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -290,6 +290,8 @@ class InterstellarClusterService(context: ActorContext[InterstellarClusterServic
|
||||||
cavernRotation.foreach {
|
cavernRotation.foreach {
|
||||||
rotation => rotation ! rotationMsg
|
rotation => rotation ! rotationMsg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case _ => ()
|
||||||
}
|
}
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -158,6 +158,7 @@ case class GameConfig(
|
||||||
warpGates: WarpGateConfig,
|
warpGates: WarpGateConfig,
|
||||||
cavernRotation: CavernRotationConfig,
|
cavernRotation: CavernRotationConfig,
|
||||||
savedMsg: SavedMessageEvents,
|
savedMsg: SavedMessageEvents,
|
||||||
|
playerDraw: PlayerStateDrawSettings,
|
||||||
doorsCanBeOpenedByMedAppFromThisDistance: Float
|
doorsCanBeOpenedByMedAppFromThisDistance: Float
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -220,3 +221,17 @@ case class SavedMessageTimings(
|
||||||
fixed: Long,
|
fixed: Long,
|
||||||
variable: 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)
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue