Merge pull request #1054 from jgillich/dc50

50 minute disconnect fix/workaround
This commit is contained in:
Jakob Gillich 2023-04-15 21:08:48 +02:00 committed by GitHub
commit 6c3fd970c4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
89 changed files with 467978 additions and 467770 deletions

View file

@ -10,4 +10,4 @@ interface PrivacyHelper {
return new ByteString1C(array);
}
}
}

View file

@ -2,6 +2,7 @@ akka {
loggers = ["akka.event.slf4j.Slf4jLogger"]
loglevel = INFO
logging-filter = akka.event.slf4j.Slf4jLoggingFilter
log-dead-letters-during-shutdown = off
}
akka.actor.deployment {

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -4808,4 +4808,4 @@
}
]
}
]
]

View file

@ -11689,4 +11689,4 @@
}
]
}
]
]

View file

@ -7393,4 +7393,4 @@
}
]
}
]
]

View file

@ -5656,4 +5656,4 @@
}
]
}
]
]

View file

@ -4340,4 +4340,4 @@
}
]
}
]
]

View file

@ -5992,4 +5992,4 @@
}
]
}
]
]

View file

@ -45,7 +45,6 @@ class LoginActor(
class LoginActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], connectionId: String, sessionId: Long)
extends Actor
with MDCContextAware {
private[this] val log = org.log4s.getLogger
import scala.concurrent.ExecutionContext.Implicits.global
@ -100,9 +99,9 @@ class LoginActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], conne
val clientVersion = s"Client Version: $majorVersion.$minorVersion.$revision, $buildDate"
if (token.isDefined)
log.trace(s"New login UN:$username Token:${token.get}. $clientVersion")
log.debug(s"New login UN:$username Token:${token.get}. $clientVersion")
else {
log.trace(s"New login UN:$username. $clientVersion")
log.debug(s"New login UN:$username. $clientVersion")
}
accountLogin(username, password.getOrElse(""))
@ -114,7 +113,7 @@ class LoginActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], conne
middlewareActor ! MiddlewareActor.Close()
case _ =>
log.warn(s"Unhandled GamePacket $pkt")
log.warning(s"Unhandled GamePacket $pkt")
}
def accountLogin(username: String, password: String): Unit = {
@ -196,7 +195,7 @@ class LoginActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], conne
}
def loginPwdFailureResponse(username: String, newToken: String) = {
log.warn(s"Failed login to account $username")
log.warning(s"Failed login to account $username")
middlewareActor ! MiddlewareActor.Send(
LoginRespMessage(
newToken,
@ -211,7 +210,7 @@ class LoginActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], conne
}
def loginFailureResponse(username: String, newToken: String) = {
log.warn("DB problem")
log.warning("DB problem")
middlewareActor ! MiddlewareActor.Send(
LoginRespMessage(
newToken,
@ -226,7 +225,7 @@ class LoginActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], conne
}
def loginAccountFailureResponse(username: String, newToken: String) = {
log.warn(s"Account $username inactive")
log.warning(s"Account $username inactive")
middlewareActor ! MiddlewareActor.Send(
LoginRespMessage(
newToken,

View file

@ -193,10 +193,8 @@ class MiddlewareActor(
/** Queue of outgoing packets ready for sending */
val outQueueBundled: mutable.Queue[PlanetSidePacket] = mutable.Queue()
/** Latest outbound sequence number;
* the current sequence is one less than this number
*/
var outSequence = 0
/** Latest outbound sequence number */
var outSequence = -1
/**
* Increment the outbound sequence number.
@ -205,13 +203,18 @@ class MiddlewareActor(
* @return
*/
def nextSequence: Int = {
val r = outSequence
if (outSequence == 0xffff) {
outSequence = 0
} else {
outSequence += 1
if (outSequence >= 0xffff) {
// TODO resetting the sequence to 0 causes a client crash
// but that does not happen when we always send the same number
// the solution is most likely to send the proper ResetSequence payload
// send(ResetSequence(), None, crypto)
// outSequence = -1
// return nextSequence
return outSequence
}
r
outSequence += 1
outSequence
}
/** Latest outbound subslot number;
@ -302,14 +305,14 @@ class MiddlewareActor(
Unknown30 is used to reuse an existing crypto session when switching from login to world
When not handling it, it appears that the client will fall back to using ClientStart
Do we need to implement this?
*/
*/
connectionClose()
case (ConnectionClose(), _) =>
/*
indicates the user has willingly quit the game world
we do not need to implement this
*/
*/
Behaviors.same
// TODO ResetSequence
@ -454,6 +457,11 @@ class MiddlewareActor(
case Successful((packet, Some(sequence))) =>
activeSequenceFunc(packet, sequence)
case Successful((packet, None)) =>
packet match {
case _: PlanetSideResetSequencePacket =>
log.info(s"ResetSequence: ${msg.toHex}, inSeq: ${inSequence}, outSeq: ${outSequence}")
case _ => ()
}
in(packet)
case Failure(e) =>
log.error(s"Could not decode $connectionId's packet: $e")
@ -569,9 +577,14 @@ class MiddlewareActor(
log.error(s"Unexpected crypto packet '$packet'")
Behaviors.same
case _: PlanetSideResetSequencePacket =>
log.debug("Received sequence reset request from client; complying")
outSequence = 0
case packet: PlanetSideResetSequencePacket =>
// TODO This is wrong
// I suspect ResetSequence is a notification that the remote sequence has been reset
// rather than a request to reset our outgoing sequence number
// Resetting it this way causes a client crash, see nextSequence
// log.debug(s"Received sequence reset request from client: $packet.}")
// outSequence = 0
Behaviors.same
}
}

View file

@ -257,26 +257,26 @@ object AvatarActor {
}
/**
* Transform from encoded inventory data as a CLOB - character large object - into individual items.
* Install those items into positions in a target container
* in the same positions in which they were previously recorded.<br>
* <br>
* There is no guarantee that the structure of the retained container data encoded in the CLOB
* will fit the current dimensions of the container.
* No tests are performed.
* A partial decompression of the CLOB may occur.
* @param container the container in which to place the pieces of equipment produced from the CLOB
* @param clob the inventory data in string form
* @param log a reference to a logging context
* @param restoreAmmo by default, when `false`, use the maximum ammunition for all ammunition boixes and for all tools;
* if `true`, load the last saved ammunition count for all ammunition boxes and for all tools
*/
* Transform from encoded inventory data as a CLOB - character large object - into individual items.
* Install those items into positions in a target container
* in the same positions in which they were previously recorded.<br>
* <br>
* There is no guarantee that the structure of the retained container data encoded in the CLOB
* will fit the current dimensions of the container.
* No tests are performed.
* A partial decompression of the CLOB may occur.
* @param container the container in which to place the pieces of equipment produced from the CLOB
* @param clob the inventory data in string form
* @param log a reference to a logging context
* @param restoreAmmo by default, when `false`, use the maximum ammunition for all ammunition boixes and for all tools;
* if `true`, load the last saved ammunition count for all ammunition boxes and for all tools
*/
def buildContainedEquipmentFromClob(
container: Container,
clob: String,
log: org.log4s.Logger,
restoreAmmo: Boolean = false
): Unit = {
container: Container,
clob: String,
log: org.log4s.Logger,
restoreAmmo: Boolean = false
): Unit = {
clob.split("/").filter(_.trim.nonEmpty).foreach { value =>
val (objectType, objectIndex, objectId, ammoData) = value.split(",") match {
case Array(a, b: String, c: String) => (a, b.toInt, c.toInt, None)
@ -293,8 +293,8 @@ object AvatarActor {
ammoData foreach { toolAmmo =>
toolAmmo.split("_").drop(1).foreach { value =>
val (ammoSlots, ammoTypeIndex, ammoBoxDefinition, ammoCount) = value.split("-") match {
case Array(a: String, b: String, c: String) => (a.toInt, b.toInt, c.toInt, None)
case Array(a: String, b: String, c: String, d:String) => (a.toInt, b.toInt, c.toInt, Some(d.toInt))
case Array(a: String, b: String, c: String) => (a.toInt, b.toInt, c.toInt, None)
case Array(a: String, b: String, c: String, d: String) => (a.toInt, b.toInt, c.toInt, Some(d.toInt))
}
val fireMode = tool.AmmoSlots(ammoSlots)
fireMode.AmmoTypeIndex = ammoTypeIndex
@ -340,11 +340,11 @@ object AvatarActor {
* @return the resulting text data that represents object to time mappings
*/
def buildCooldownsFromClob(
clob: String,
cooldownDurations: Map[BasicDefinition,FiniteDuration],
log: org.log4s.Logger
): Map[String, LocalDateTime] = {
val now = LocalDateTime.now()
clob: String,
cooldownDurations: Map[BasicDefinition, FiniteDuration],
log: org.log4s.Logger
): Map[String, LocalDateTime] = {
val now = LocalDateTime.now()
val cooldowns: mutable.Map[String, LocalDateTime] = mutable.Map()
clob.split("/").filter(_.trim.nonEmpty).foreach { value =>
value.split(",") match {
@ -385,7 +385,7 @@ object AvatarActor {
val factionName: String = faction.toString.toLowerCase
val name = item match {
case GlobalDefinitions.trhev_dualcycler | GlobalDefinitions.nchev_scattercannon |
GlobalDefinitions.vshev_quasar =>
GlobalDefinitions.vshev_quasar =>
s"${factionName}hev_antipersonnel"
case GlobalDefinitions.trhev_pounder | GlobalDefinitions.nchev_falcon | GlobalDefinitions.vshev_comet =>
s"${factionName}hev_antivehicular"
@ -402,12 +402,12 @@ object AvatarActor {
if (name.matches("(tr|nc|vs)hev_.+") && Config.app.game.sharedMaxCooldown) {
val faction = name.take(2)
(if (faction.equals("nc")) {
Seq(GlobalDefinitions.nchev_scattercannon, GlobalDefinitions.nchev_falcon, GlobalDefinitions.nchev_sparrow)
} else if (faction.equals("vs")) {
Seq(GlobalDefinitions.vshev_quasar, GlobalDefinitions.vshev_comet, GlobalDefinitions.vshev_starfire)
} else {
Seq(GlobalDefinitions.trhev_dualcycler, GlobalDefinitions.trhev_pounder, GlobalDefinitions.trhev_burster)
}).zip(
Seq(GlobalDefinitions.nchev_scattercannon, GlobalDefinitions.nchev_falcon, GlobalDefinitions.nchev_sparrow)
} else if (faction.equals("vs")) {
Seq(GlobalDefinitions.vshev_quasar, GlobalDefinitions.vshev_comet, GlobalDefinitions.vshev_starfire)
} else {
Seq(GlobalDefinitions.trhev_dualcycler, GlobalDefinitions.trhev_pounder, GlobalDefinitions.trhev_burster)
}).zip(
Seq(s"${faction}hev_antipersonnel", s"${faction}hev_antivehicular", s"${faction}hev_antiaircraft")
)
} else {
@ -461,7 +461,6 @@ object AvatarActor {
}
}
def displayLookingForSquad(session: Session, state: Int): Unit = {
val player = session.player
session.zone.AvatarEvents ! AvatarServiceMessage(
@ -477,7 +476,10 @@ object AvatarActor {
* @param func functionality that is called upon discovery of the character
* @return if found, the discovered avatar, the avatar's account id, and the avatar's faction affiliation
*/
def getLiveAvatarForFunc(name: String, func: (Long,String,Int)=>Unit): Option[(Avatar, Long, PlanetSideEmpire.Value)] = {
def getLiveAvatarForFunc(
name: String,
func: (Long, String, Int) => Unit
): Option[(Avatar, Long, PlanetSideEmpire.Value)] = {
if (name.nonEmpty) {
LivePlayerList.WorldPopulation({ case (_, a) => a.name.equals(name) }).headOption match {
case Some(otherAvatar) =>
@ -500,7 +502,10 @@ object AvatarActor {
* otherwise, always returns `None` as if no avatar was discovered
* (the query is probably still in progress)
*/
def getAvatarForFunc(name: String, func: (Long,String,Int)=>Unit): Option[(Avatar, Long, PlanetSideEmpire.Value)] = {
def getAvatarForFunc(
name: String,
func: (Long, String, Int) => Unit
): Option[(Avatar, Long, PlanetSideEmpire.Value)] = {
getLiveAvatarForFunc(name, func).orElse {
if (name.nonEmpty) {
import ctx._
@ -527,7 +532,7 @@ object AvatarActor {
* @param name unique character name
* @param faction the faction affiliation
*/
def formatForOtherFunc(func: (Long,String)=>Unit)(charId: Long, name: String, faction: Int): Unit = {
def formatForOtherFunc(func: (Long, String) => Unit)(charId: Long, name: String, faction: Int): Unit = {
func(charId, name)
}
@ -540,9 +545,11 @@ object AvatarActor {
*/
def onlineIfNotIgnored(onlinePlayerName: String, observerName: String): Boolean = {
val onlinePlayerNameLower = onlinePlayerName.toLowerCase()
LivePlayerList.WorldPopulation({ case (_, a) => a.name.toLowerCase().equals(onlinePlayerNameLower) }).headOption match {
LivePlayerList
.WorldPopulation({ case (_, a) => a.name.toLowerCase().equals(onlinePlayerNameLower) })
.headOption match {
case Some(onlinePlayer) => onlineIfNotIgnored(onlinePlayer, observerName)
case _ => false
case _ => false
}
}
@ -556,9 +563,9 @@ object AvatarActor {
*/
def onlineIfNotIgnoredEitherWay(observer: Avatar, onlinePlayerName: String): Boolean = {
LivePlayerList.WorldPopulation({ case (_, a) => a.name.equals(onlinePlayerName) }) match {
case Nil => false //weird case, but ...
case Nil => false //weird case, but ...
case onlinePlayer :: Nil => onlineIfNotIgnoredEitherWay(onlinePlayer, observer)
case _ => throw new Exception("only trying to find two players, but too many matching search results!")
case _ => throw new Exception("only trying to find two players, but too many matching search results!")
}
}
@ -598,24 +605,25 @@ object AvatarActor {
import ctx._
import scala.concurrent.ExecutionContext.Implicits.global
val out: Promise[persistence.Savedplayer] = Promise()
val queryResult = ctx.run(query[persistence.Savedplayer].filter { _.avatarId == lift(avatarId) })
val queryResult = ctx.run(query[persistence.Savedplayer].filter { _.avatarId == lift(avatarId) })
queryResult.onComplete {
case Success(data) if data.nonEmpty =>
out.completeWith(Future(data.head))
case _ =>
ctx.run(query[persistence.Savedplayer]
.insert(
_.avatarId -> lift(avatarId),
_.px -> lift(0),
_.py -> lift(0),
_.pz -> lift(0),
_.orientation -> lift(0),
_.zoneNum -> lift(0),
_.health -> lift(0),
_.armor -> lift(0),
_.exosuitNum -> lift(0),
_.loadout -> lift("")
)
ctx.run(
query[persistence.Savedplayer]
.insert(
_.avatarId -> lift(avatarId),
_.px -> lift(0),
_.py -> lift(0),
_.pz -> lift(0),
_.orientation -> lift(0),
_.zoneNum -> lift(0),
_.health -> lift(0),
_.armor -> lift(0),
_.exosuitNum -> lift(0),
_.loadout -> lift("")
)
)
out.completeWith(Future(persistence.Savedplayer(avatarId, 0, 0, 0, 0, 0, 0, 0, 0, "")))
}
@ -684,24 +692,25 @@ object AvatarActor {
import ctx._
import scala.concurrent.ExecutionContext.Implicits.global
val out: Promise[Int] = Promise()
val avatarId = player.avatar.id
val position = player.Position
val queryResult = ctx.run(query[persistence.Savedplayer].filter { _.avatarId == lift(avatarId) })
val avatarId = player.avatar.id
val position = player.Position
val queryResult = ctx.run(query[persistence.Savedplayer].filter { _.avatarId == lift(avatarId) })
queryResult.onComplete {
case Success(results) if results.nonEmpty =>
ctx.run(query[persistence.Savedplayer]
.filter { _.avatarId == lift(avatarId) }
.update(
_.px -> lift((position.x * 1000).toInt),
_.py -> lift((position.y * 1000).toInt),
_.pz -> lift((position.z * 1000).toInt),
_.orientation -> lift((player.Orientation.z * 1000).toInt),
_.zoneNum -> lift(player.Zone.Number),
_.health -> lift(health),
_.armor -> lift(player.Armor),
_.exosuitNum -> lift(player.ExoSuit.id),
_.loadout -> lift(buildClobFromPlayerLoadout(player))
)
ctx.run(
query[persistence.Savedplayer]
.filter { _.avatarId == lift(avatarId) }
.update(
_.px -> lift((position.x * 1000).toInt),
_.py -> lift((position.y * 1000).toInt),
_.pz -> lift((position.z * 1000).toInt),
_.orientation -> lift((player.Orientation.z * 1000).toInt),
_.zoneNum -> lift(player.Zone.Number),
_.health -> lift(health),
_.armor -> lift(player.Armor),
_.exosuitNum -> lift(player.ExoSuit.id),
_.loadout -> lift(buildClobFromPlayerLoadout(player))
)
)
out.completeWith(Future(1))
case _ =>
@ -722,20 +731,21 @@ object AvatarActor {
import ctx._
import scala.concurrent.ExecutionContext.Implicits.global
val out: Promise[Int] = Promise()
val avatarId = player.avatar.id
val position = player.Position
val queryResult = ctx.run(query[persistence.Savedplayer].filter { _.avatarId == lift(avatarId) })
val avatarId = player.avatar.id
val position = player.Position
val queryResult = ctx.run(query[persistence.Savedplayer].filter { _.avatarId == lift(avatarId) })
queryResult.onComplete {
case Success(results) if results.nonEmpty =>
ctx.run(query[persistence.Savedplayer]
.filter { _.avatarId == lift(avatarId) }
.update(
_.px -> lift((position.x * 1000).toInt),
_.py -> lift((position.y * 1000).toInt),
_.pz -> lift((position.z * 1000).toInt),
_.orientation -> lift((player.Orientation.z * 1000).toInt),
_.zoneNum -> lift(player.Zone.Number)
)
ctx.run(
query[persistence.Savedplayer]
.filter { _.avatarId == lift(avatarId) }
.update(
_.px -> lift((position.x * 1000).toInt),
_.py -> lift((position.y * 1000).toInt),
_.pz -> lift((position.z * 1000).toInt),
_.orientation -> lift((player.Orientation.z * 1000).toInt),
_.zoneNum -> lift(player.Zone.Number)
)
)
out.completeWith(Future(1))
case _ =>
@ -757,19 +767,20 @@ object AvatarActor {
import ctx._
import scala.concurrent.ExecutionContext.Implicits.global
val out: Promise[persistence.Savedavatar] = Promise()
val queryResult = ctx.run(query[persistence.Savedavatar].filter { _.avatarId == lift(avatarId) })
val queryResult = ctx.run(query[persistence.Savedavatar].filter { _.avatarId == lift(avatarId) })
queryResult.onComplete {
case Success(data) if data.nonEmpty =>
out.completeWith(Future(data.head))
case _ =>
val now = LocalDateTime.now()
ctx.run(query[persistence.Savedavatar]
.insert(
_.avatarId -> lift(avatarId),
_.forgetCooldown -> lift(now),
_.purchaseCooldowns -> lift(""),
_.useCooldowns -> lift("")
)
ctx.run(
query[persistence.Savedavatar]
.insert(
_.avatarId -> lift(avatarId),
_.forgetCooldown -> lift(now),
_.purchaseCooldowns -> lift(""),
_.useCooldowns -> lift("")
)
)
out.completeWith(Future(persistence.Savedavatar(avatarId, now, "", "")))
}
@ -788,16 +799,17 @@ object AvatarActor {
import ctx._
import scala.concurrent.ExecutionContext.Implicits.global
val out: Promise[Int] = Promise()
val avatarId = avatar.id
val queryResult = ctx.run(query[persistence.Savedavatar].filter { _.avatarId == lift(avatarId) })
val avatarId = avatar.id
val queryResult = ctx.run(query[persistence.Savedavatar].filter { _.avatarId == lift(avatarId) })
queryResult.onComplete {
case Success(results) if results.nonEmpty =>
ctx.run(query[persistence.Savedavatar]
.filter { _.avatarId == lift(avatarId) }
.update(
_.purchaseCooldowns -> lift(buildClobfromCooldowns(avatar.cooldowns.purchase)),
_.useCooldowns -> lift(buildClobfromCooldowns(avatar.cooldowns.use))
)
ctx.run(
query[persistence.Savedavatar]
.filter { _.avatarId == lift(avatarId) }
.update(
_.purchaseCooldowns -> lift(buildClobfromCooldowns(avatar.cooldowns.purchase)),
_.useCooldowns -> lift(buildClobfromCooldowns(avatar.cooldowns.use))
)
)
out.completeWith(Future(1))
case _ =>
@ -959,12 +971,13 @@ class AvatarActor(
deleted.headOption match {
case Some(a) if !a.deleted =>
val flagDeletion = for {
_ <- ctx.run(query[persistence.Avatar]
.filter(_.id == lift(id))
.update(
_.deleted -> lift(true),
_.lastModified -> lift(LocalDateTime.now())
)
_ <- ctx.run(
query[persistence.Avatar]
.filter(_.id == lift(id))
.update(
_.deleted -> lift(true),
_.lastModified -> lift(LocalDateTime.now())
)
)
} yield ()
flagDeletion.onComplete {
@ -1008,11 +1021,12 @@ class AvatarActor(
case LoginAvatar(replyTo) =>
import ctx._
val avatarId = avatar.id
ctx.run(
query[persistence.Avatar]
.filter(_.id == lift(avatarId))
.map { c => (c.created, c.lastLogin) }
)
ctx
.run(
query[persistence.Avatar]
.filter(_.id == lift(avatarId))
.map { c => (c.created, c.lastLogin) }
)
.onComplete {
case Success(value) if value.nonEmpty =>
val (created, lastLogin) = value.head
@ -1031,12 +1045,12 @@ class AvatarActor(
persistence.Certification(Certification.ATV.value, avatarId),
persistence.Certification(Certification.Harasser.value, avatarId)
)
).foreach(c => query[persistence.Certification].insert(c))
).foreach(c => query[persistence.Certification].insertValue(c))
)
_ <- ctx.run(
liftQuery(
List(persistence.Shortcut(avatarId, 0, 0, "medkit"))
).foreach(c => query[persistence.Shortcut].insert(c))
).foreach(c => query[persistence.Shortcut].insertValue(c))
)
} yield true
inits.onComplete {
@ -1109,13 +1123,14 @@ class AvatarActor(
val replace = certification.replaces.intersect(avatar.certifications)
Future
.sequence(replace.map(cert => {
ctx.run(
query[persistence.Certification]
.filter(_.avatarId == lift(avatar.id))
.filter(_.id == lift(cert.value))
.delete
)
.map(_ => cert)
ctx
.run(
query[persistence.Certification]
.filter(_.avatarId == lift(avatar.id))
.filter(_.id == lift(cert.value))
.delete
)
.map(_ => cert)
}))
.onComplete {
case Failure(exception) =>
@ -1129,10 +1144,11 @@ class AvatarActor(
PlanetsideAttributeMessage(session.get.player.GUID, 25, cert.value)
)
}
ctx.run(
query[persistence.Certification]
.insert(_.id -> lift(certification.value), _.avatarId -> lift(avatar.id))
)
ctx
.run(
query[persistence.Certification]
.insert(_.id -> lift(certification.value), _.avatarId -> lift(avatar.id))
)
.onComplete {
case Failure(exception) =>
log.error(exception)("db failure")
@ -1180,13 +1196,14 @@ class AvatarActor(
avatar.certifications
.intersect(requiredByCert)
.map(cert => {
ctx.run(
query[persistence.Certification]
.filter(_.avatarId == lift(avatar.id))
.filter(_.id == lift(cert.value))
.delete
)
.map(_ => cert)
ctx
.run(
query[persistence.Certification]
.filter(_.avatarId == lift(avatar.id))
.filter(_.id == lift(cert.value))
.delete
)
.map(_ => cert)
})
)
.onComplete {
@ -1329,25 +1346,26 @@ class AvatarActor(
index match {
case Some(_index) =>
import ctx._
ctx.run(
query[persistence.Implant]
.filter(_.name == lift(definition.Name))
.filter(_.avatarId == lift(avatar.id))
.delete
)
.onComplete {
case Success(_) =>
replaceAvatar(avatar.copy(implants = avatar.implants.updated(_index, None)))
sessionActor ! SessionActor.SendResponse(
AvatarImplantMessage(session.get.player.GUID, ImplantAction.Remove, _index, 0)
)
sessionActor ! SessionActor.SendResponse(
ItemTransactionResultMessage(terminalGuid, TransactionType.Sell, success = true)
)
context.self ! ResetImplants()
sessionActor ! SessionActor.CharSaved
case Failure(exception) => log.error(exception)("db failure")
}
ctx
.run(
query[persistence.Implant]
.filter(_.name == lift(definition.Name))
.filter(_.avatarId == lift(avatar.id))
.delete
)
.onComplete {
case Success(_) =>
replaceAvatar(avatar.copy(implants = avatar.implants.updated(_index, None)))
sessionActor ! SessionActor.SendResponse(
AvatarImplantMessage(session.get.player.GUID, ImplantAction.Remove, _index, 0)
)
sessionActor ! SessionActor.SendResponse(
ItemTransactionResultMessage(terminalGuid, TransactionType.Sell, success = true)
)
context.self ! ResetImplants()
sessionActor ! SessionActor.CharSaved
case Failure(exception) => log.error(exception)("db failure")
}
case None =>
log.warn("attempted to sell implant but could not find slot")
@ -1462,23 +1480,25 @@ class AvatarActor(
case UpdatePurchaseTime(definition, time) =>
var newTimes = avatar.cooldowns.purchase
AvatarActor.resolveSharedPurchaseTimeNames(AvatarActor.resolvePurchaseTimeName(avatar.faction, definition)).foreach {
case (item, name) =>
Avatar.purchaseCooldowns.get(item) match {
case Some(cooldown) =>
//only send for items with cooldowns
newTimes = newTimes.updated(name, time)
updatePurchaseTimer(
name,
cooldown.toSeconds,
item match {
case _: KitDefinition => false
case _ => true
}
)
case _ => ;
}
}
AvatarActor
.resolveSharedPurchaseTimeNames(AvatarActor.resolvePurchaseTimeName(avatar.faction, definition))
.foreach {
case (item, name) =>
Avatar.purchaseCooldowns.get(item) match {
case Some(cooldown) =>
//only send for items with cooldowns
newTimes = newTimes.updated(name, time)
updatePurchaseTimer(
name,
cooldown.toSeconds,
item match {
case _: KitDefinition => false
case _ => true
}
)
case _ => ;
}
}
avatarCopy(avatar.copy(cooldowns = avatar.cooldowns.copy(purchase = newTimes)))
Behaviors.same
@ -1675,14 +1695,16 @@ class AvatarActor(
Behaviors.same
case SetRibbon(ribbon, bar) =>
val decor = avatar.decoration
val decor = avatar.decoration
val previousRibbonBars = decor.ribbonBars
val useRibbonBars = Seq(previousRibbonBars.upper, previousRibbonBars.middle, previousRibbonBars.lower)
.indexWhere { _ == ribbon } match {
case -1 => previousRibbonBars
case n => AvatarActor.changeRibbons(previousRibbonBars, MeritCommendation.None, RibbonBarSlot(n))
}
replaceAvatar(avatar.copy(decoration = decor.copy(ribbonBars = AvatarActor.changeRibbons(useRibbonBars, ribbon, bar))))
replaceAvatar(
avatar.copy(decoration = decor.copy(ribbonBars = AvatarActor.changeRibbons(useRibbonBars, ribbon, bar)))
)
val player = session.get.player
val zone = player.Zone
zone.AvatarEvents ! AvatarServiceMessage(
@ -1706,9 +1728,11 @@ class AvatarActor(
case _ => false
})
if (isDifferentShortcut) {
if (!isMacroShortcut && avatar.shortcuts.flatten.exists {
a => AvatarShortcut.equals(shortcut, a)
}) {
if (
!isMacroShortcut && avatar.shortcuts.flatten.exists { a =>
AvatarShortcut.equals(shortcut, a)
}
) {
//duplicate implant or medkit found
if (shortcut.isInstanceOf[Shortcut.Implant]) {
//duplicate implant
@ -1716,11 +1740,17 @@ class AvatarActor(
case Some(existingShortcut: AvatarShortcut) =>
//redraw redundant shortcut slot with existing shortcut
sessionActor ! SessionActor.SendResponse(
CreateShortcutMessage(session.get.player.GUID, slot + 1, Some(AvatarShortcut.convert(existingShortcut)))
CreateShortcutMessage(
session.get.player.GUID,
slot + 1,
Some(AvatarShortcut.convert(existingShortcut))
)
)
case _ =>
//blank shortcut slot
sessionActor ! SessionActor.SendResponse(CreateShortcutMessage(session.get.player.GUID, slot + 1, None))
sessionActor ! SessionActor.SendResponse(
CreateShortcutMessage(session.get.player.GUID, slot + 1, None)
)
}
}
} else {
@ -1743,7 +1773,7 @@ class AvatarActor(
.filter(_.slot == lift(slot))
.update(
_.purpose -> lift(shortcut.code),
_.tile -> lift(shortcut.tile),
_.tile -> lift(shortcut.tile),
_.effect1 -> Option(lift(optEffect1)),
_.effect2 -> Option(lift(optEffect2))
)
@ -1752,11 +1782,11 @@ class AvatarActor(
ctx.run(
query[persistence.Shortcut].insert(
_.avatarId -> lift(avatar.id.toLong),
_.slot -> lift(slot),
_.purpose -> lift(shortcut.code),
_.tile -> lift(shortcut.tile),
_.effect1 -> Option(lift(optEffect1)),
_.effect2 -> Option(lift(optEffect2))
_.slot -> lift(slot),
_.purpose -> lift(shortcut.code),
_.tile -> lift(shortcut.tile),
_.effect1 -> Option(lift(optEffect1)),
_.effect2 -> Option(lift(optEffect2))
)
)
}
@ -1771,10 +1801,11 @@ class AvatarActor(
avatar.shortcuts.lift(slot).flatten match {
case None => ;
case Some(_) =>
ctx.run(query[persistence.Shortcut]
.filter(_.avatarId == lift(avatar.id.toLong))
.filter(_.slot == lift(slot))
.delete
ctx.run(
query[persistence.Shortcut]
.filter(_.avatarId == lift(avatar.id.toLong))
.filter(_.slot == lift(slot))
.delete
)
avatar.shortcuts.update(slot, None)
}
@ -1803,12 +1834,16 @@ class AvatarActor(
val result = for {
//log this login
_ <- ctx.run(query[persistence.Avatar].filter(_.id == lift(avatarId))
.update(_.lastLogin -> lift(LocalDateTime.now()))
_ <- ctx.run(
query[persistence.Avatar]
.filter(_.id == lift(avatarId))
.update(_.lastLogin -> lift(LocalDateTime.now()))
)
//log this choice of faction (no empire switching)
_ <- ctx.run(query[persistence.Account].filter(_.id == lift(accountId))
.update(_.lastFactionId -> lift(avatar.faction.id))
_ <- ctx.run(
query[persistence.Account]
.filter(_.id == lift(accountId))
.update(_.lastFactionId -> lift(avatar.faction.id))
)
//retrieve avatar data
loadouts <- initializeAllLoadouts()
@ -1887,11 +1922,12 @@ class AvatarActor(
val p = Promise[Unit]()
import ctx._
ctx.run(
query[persistence.Avatar]
.filter(_.id == lift(avatar.id))
.update(_.cosmetics -> lift(Some(Cosmetic.valuesToObjectCreateValue(cosmetics)): Option[Int]))
)
ctx
.run(
query[persistence.Avatar]
.filter(_.id == lift(avatar.id))
.update(_.cosmetics -> lift(Some(Cosmetic.valuesToObjectCreateValue(cosmetics)): Option[Int]))
)
.onComplete {
case Success(_) =>
val zone = session.get.zone
@ -2177,6 +2213,7 @@ class AvatarActor(
secondsSinceLastLogin
)
)
/** After the user has selected a character to load from the "character select screen,"
* the temporary global unique identifiers used for that screen are stripped from the underlying `Player` object that was selected.
* Characters that were not selected may be destroyed along with their temporary GUIDs.
@ -2471,42 +2508,45 @@ class AvatarActor(
val locker = Avatar.makeLocker()
saveLockerFunc = storeLocker
val out = Promise[LockerContainer]()
ctx.run(query[persistence.Locker].filter(_.avatarId == lift(charId)))
ctx
.run(query[persistence.Locker].filter(_.avatarId == lift(charId)))
.onComplete {
case Success(entry) if entry.nonEmpty =>
AvatarActor.buildContainedEquipmentFromClob(locker, entry.head.items, log, restoreAmmo = true)
out.completeWith(Future(locker))
case Success(_) =>
//no locker, or maybe default empty locker?
ctx.run(query[persistence.Locker].insert(_.avatarId -> lift(avatar.id), _.items -> lift("")))
.onComplete {
_ => out.completeWith(Future(locker))
}
case Failure(e) =>
saveLockerFunc = doNotStoreLocker
log.error(e)("db failure")
out.tryFailure(e)
}
case Success(entry) if entry.nonEmpty =>
AvatarActor.buildContainedEquipmentFromClob(locker, entry.head.items, log, restoreAmmo = true)
out.completeWith(Future(locker))
case Success(_) =>
//no locker, or maybe default empty locker?
ctx
.run(query[persistence.Locker].insert(_.avatarId -> lift(avatar.id), _.items -> lift("")))
.onComplete { _ =>
out.completeWith(Future(locker))
}
case Failure(e) =>
saveLockerFunc = doNotStoreLocker
log.error(e)("db failure")
out.tryFailure(e)
}
out.future
}
def loadFriendList(avatarId: Long): Future[List[AvatarFriend]] = {
import ctx._
val out: Promise[List[AvatarFriend]] = Promise()
val queryResult = ctx.run(
query[persistence.Friend].filter { _.avatarId == lift(avatarId) }
query[persistence.Friend]
.filter { _.avatarId == lift(avatarId) }
.join(query[persistence.Avatar])
.on { case (friend, avatar) => friend.charId == avatar.id }
.map { case (_, avatar) => (avatar.id, avatar.name, avatar.factionId) }
)
queryResult.onComplete {
case Success(list) =>
out.completeWith(Future(
list.map { case (id, name, faction) => AvatarFriend(id, name, PlanetSideEmpire(faction)) }.toList
))
out.completeWith(
Future(
list.map { case (id, name, faction) => AvatarFriend(id, name, PlanetSideEmpire(faction)) }.toList
)
)
case _ =>
out.completeWith(Future(List.empty[AvatarFriend]))
}
@ -2518,16 +2558,19 @@ class AvatarActor(
val out: Promise[List[AvatarIgnored]] = Promise()
val queryResult = ctx.run(
query[persistence.Ignored].filter { _.avatarId == lift(avatarId) }
query[persistence.Ignored]
.filter { _.avatarId == lift(avatarId) }
.join(query[persistence.Avatar])
.on { case (friend, avatar) => friend.charId == avatar.id }
.map { case (_, avatar) => (avatar.id, avatar.name) }
)
queryResult.onComplete {
case Success(list) =>
out.completeWith(Future(
list.map { case (id, name) => AvatarIgnored(id, name) }.toList
))
out.completeWith(
Future(
list.map { case (id, name) => AvatarIgnored(id, name) }.toList
)
)
case _ =>
out.completeWith(Future(List.empty[AvatarIgnored]))
}
@ -2539,14 +2582,16 @@ class AvatarActor(
val out: Promise[Array[Option[AvatarShortcut]]] = Promise()
val queryResult = ctx.run(
query[persistence.Shortcut].filter { _.avatarId == lift(avatarId) }
query[persistence.Shortcut]
.filter { _.avatarId == lift(avatarId) }
.map { shortcut => (shortcut.slot, shortcut.purpose, shortcut.tile, shortcut.effect1, shortcut.effect2) }
)
val output = Array.fill[Option[AvatarShortcut]](64)(None)
queryResult.onComplete {
case Success(list) =>
list.foreach { case (slot, purpose, tile, effect1, effect2) =>
output.update(slot, Some(AvatarShortcut(purpose, tile, effect1.getOrElse(""), effect2.getOrElse(""))))
list.foreach {
case (slot, purpose, tile, effect1, effect2) =>
output.update(slot, Some(AvatarShortcut(purpose, tile, effect1.getOrElse(""), effect2.getOrElse(""))))
}
out.completeWith(Future(output))
case Failure(e) =>
@ -2597,7 +2642,7 @@ class AvatarActor(
cooldown.toSeconds - secondsSincePurchase,
obj match {
case _: KitDefinition => false
case _ => true
case _ => true
}
)
@ -2648,7 +2693,7 @@ class AvatarActor(
case MemberAction.RemoveFriend => getAvatarForFunc(name, formatForOtherFunc(memberActionRemoveFriend))
case MemberAction.AddIgnoredPlayer => getAvatarForFunc(name, memberActionAddIgnored)
case MemberAction.RemoveIgnoredPlayer => getAvatarForFunc(name, formatForOtherFunc(memberActionRemoveIgnored))
case _ => ;
case _ => ;
}
}
}
@ -2658,15 +2703,17 @@ class AvatarActor(
* @return a list of `Friends` suitable for putting into a packet
*/
def transformFriendsList(): List[GameFriend] = {
avatar.people.friend.map { f => GameFriend(f.name, f.online)}
avatar.people.friend.map { f => GameFriend(f.name, f.online) }
}
/**
* Transform the ignored players list in a list of packet entities.
* @return a list of `Friends` suitable for putting into a packet
*/
def transformIgnoredList(): List[GameFriend] = {
avatar.people.ignored.map { f => GameFriend(f.name, f.online)}
avatar.people.ignored.map { f => GameFriend(f.name, f.online) }
}
/**
* Reload the list of friend players or ignored players for the client.
* This does not update any player's online status, but merely reloads current states.
@ -2674,7 +2721,7 @@ class AvatarActor(
* (either `InitializeFriendList` or `InitializeIgnoreList`, hopefully)
* @param listFunc transformation function that produces data suitable for a game paket
*/
def memberActionListManagement(action: MemberAction.Value, listFunc: ()=>List[GameFriend]): Unit = {
def memberActionListManagement(action: MemberAction.Value, listFunc: () => List[GameFriend]): Unit = {
FriendsResponse.packetSequence(action, listFunc()).foreach { msg =>
sessionActor ! SessionActor.SendResponse(msg)
}
@ -2693,16 +2740,20 @@ class AvatarActor(
case Some(_) => ;
case None =>
import ctx._
ctx.run(query[persistence.Friend]
.insert(
_.avatarId -> lift(avatar.id.toLong),
_.charId -> lift(charId)
)
ctx.run(
query[persistence.Friend]
.insert(
_.avatarId -> lift(avatar.id.toLong),
_.charId -> lift(charId)
)
)
val isOnline = onlineIfNotIgnoredEitherWay(avatar, name)
replaceAvatar(avatar.copy(
people = people.copy(friend = people.friend :+ AvatarFriend(charId, name, PlanetSideEmpire(faction), isOnline))
))
replaceAvatar(
avatar.copy(
people =
people.copy(friend = people.friend :+ AvatarFriend(charId, name, PlanetSideEmpire(faction), isOnline))
)
)
sessionActor ! SessionActor.SendResponse(FriendsResponse(MemberAction.AddFriend, GameFriend(name, isOnline)))
sessionActor ! SessionActor.CharSaved
}
@ -2724,17 +2775,17 @@ class AvatarActor(
)
case None => ;
}
ctx.run(query[persistence.Friend]
.filter(_.avatarId == lift(avatar.id))
.filter(_.charId == lift(charId))
.delete
ctx.run(
query[persistence.Friend]
.filter(_.avatarId == lift(avatar.id))
.filter(_.charId == lift(charId))
.delete
)
sessionActor ! SessionActor.SendResponse(FriendsResponse(MemberAction.RemoveFriend, GameFriend(name)))
sessionActor ! SessionActor.CharSaved
}
/**
*
* @param name unique character name
* @return if the avatar is found, that avatar's unique identifier and the avatar's faction affiliation
*/
@ -2752,11 +2803,13 @@ class AvatarActor(
case None =>
(None, false)
}
replaceAvatar(avatar.copy(
people = people.copy(
friend = people.friend.filterNot { _.name.equals(name) } :+ otherFriend.copy(online = online)
replaceAvatar(
avatar.copy(
people = people.copy(
friend = people.friend.filterNot { _.name.equals(name) } :+ otherFriend.copy(online = online)
)
)
))
)
sessionActor ! SessionActor.SendResponse(FriendsResponse(MemberAction.UpdateFriend, GameFriend(name, online)))
out
case None =>
@ -2782,16 +2835,19 @@ class AvatarActor(
case Some(_) => ;
case None =>
import ctx._
ctx.run(query[persistence.Ignored]
.insert(
_.avatarId -> lift(avatar.id.toLong),
_.charId -> lift(charId)
)
ctx.run(
query[persistence.Ignored]
.insert(
_.avatarId -> lift(avatar.id.toLong),
_.charId -> lift(charId)
)
)
replaceAvatar(
avatar.copy(people = people.copy(ignored = people.ignored :+ AvatarIgnored(charId, name)))
)
sessionActor ! SessionActor.UpdateIgnoredPlayers(FriendsResponse(MemberAction.AddIgnoredPlayer, GameFriend(name)))
sessionActor ! SessionActor.UpdateIgnoredPlayers(
FriendsResponse(MemberAction.AddIgnoredPlayer, GameFriend(name))
)
sessionActor ! SessionActor.CharSaved
}
}
@ -2814,31 +2870,34 @@ class AvatarActor(
)
case None => ;
}
ctx.run(query[persistence.Ignored]
.filter(_.avatarId == lift(avatar.id.toLong))
.filter(_.charId == lift(charId))
.delete
ctx.run(
query[persistence.Ignored]
.filter(_.avatarId == lift(avatar.id.toLong))
.filter(_.charId == lift(charId))
.delete
)
sessionActor ! SessionActor.UpdateIgnoredPlayers(
FriendsResponse(MemberAction.RemoveIgnoredPlayer, GameFriend(name))
)
sessionActor ! SessionActor.UpdateIgnoredPlayers(FriendsResponse(MemberAction.RemoveIgnoredPlayer, GameFriend(name)))
sessionActor ! SessionActor.CharSaved
}
def setBep(bep: Long, modifier: ExperienceType): Unit = {
import ctx._
val current = BattleRank.withExperience(avatar.bep).value
val next = BattleRank.withExperience(bep).value
val current = BattleRank.withExperience(avatar.bep).value
val next = BattleRank.withExperience(bep).value
lazy val br24 = BattleRank.BR24.value
val result = for {
r <- ctx.run(query[persistence.Avatar].filter(_.id == lift(avatar.id)).update(_.bep -> lift(bep)))
} yield r
result.onComplete {
case Success(_) =>
val sess = session.get
val zone = sess.zone
val zoneId = zone.id
val events = zone.AvatarEvents
val player = sess.player
val pguid = player.GUID
val sess = session.get
val zone = sess.zone
val zoneId = zone.id
val events = zone.AvatarEvents
val player = sess.player
val pguid = player.GUID
val localModifier = modifier
sessionActor ! SessionActor.SendResponse(BattleExperienceMessage(pguid, bep, localModifier))
events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(pguid, 17, bep))
@ -2854,15 +2913,18 @@ class AvatarActor(
val implants = avatar.implants.zipWithIndex.map {
case (implant, index) =>
if (index >= BattleRank.withExperience(bep).implantSlots && implant.isDefined) {
ctx.run(
query[persistence.Implant]
.filter(_.name == lift(implant.get.definition.Name))
.filter(_.avatarId == lift(avatar.id))
.delete
)
ctx
.run(
query[persistence.Implant]
.filter(_.name == lift(implant.get.definition.Name))
.filter(_.avatarId == lift(avatar.id))
.delete
)
.onComplete {
case Success(_) =>
sessionActor ! SessionActor.SendResponse(AvatarImplantMessage(pguid, ImplantAction.Remove, index, 0))
sessionActor ! SessionActor.SendResponse(
AvatarImplantMessage(pguid, ImplantAction.Remove, index, 0)
)
case Failure(exception) =>
log.error(exception)("db failure")
}
@ -2895,22 +2957,22 @@ class AvatarActor(
def updateKillsDeathsAssists(kdaStat: KDAStat): Unit = {
avatar.scorecard.rate(kdaStat)
val exp = kdaStat.experienceEarned
val exp = kdaStat.experienceEarned
val _session = session.get
val zone = _session.zone
val player = _session.player
val zone = _session.zone
val player = _session.player
kdaStat match {
case kill: Kill =>
val _ = PlayerSource(player)
(kill.info.interaction.cause match {
case pr: ProjectileReason => pr.projectile.mounted_in.map { a => zone.GUID(a._1) }
case _ => None
case _ => None
}) match {
case Some(Some(_: Vitality)) =>
//zone.actor ! ZoneActor.RewardOurSupporters(playerSource, obj.History, kill, exp)
//zone.actor ! ZoneActor.RewardOurSupporters(playerSource, obj.History, kill, exp)
case _ => ;
}
//zone.actor ! ZoneActor.RewardOurSupporters(playerSource, player.History, kill, exp)
//zone.actor ! ZoneActor.RewardOurSupporters(playerSource, player.History, kill, exp)
case _: Death =>
player.Zone.AvatarEvents ! AvatarServiceMessage(
player.Name,

View file

@ -102,7 +102,10 @@ object SessionActor {
tickTime: Long = 250L
)
private[session] final case class AvatarAwardMessageBundle(bundle: Iterable[Iterable[PlanetSideGamePacket]], delay: Long)
private[session] final case class AvatarAwardMessageBundle(
bundle: Iterable[Iterable[PlanetSideGamePacket]],
delay: Long
)
}
class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], connectionId: String, sessionId: Long)
@ -110,9 +113,8 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
with MDCContextAware {
MDC("connectionId") = connectionId
private[this] val log = org.log4s.getLogger
private[this] val buffer: mutable.ListBuffer[Any] = new mutable.ListBuffer[Any]()
private[this] val sessionFuncs = new SessionData(middlewareActor, context)
private[this] val sessionFuncs = new SessionData(middlewareActor, context)
override val supervisorStrategy: SupervisorStrategy = sessionFuncs.sessionSupervisorStrategy
@ -195,7 +197,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
sessionFuncs.zoning.spawn.handlePlayerLoaded(tplayer)
case Zone.Population.PlayerHasLeft(zone, None) =>
log.trace(s"PlayerHasLeft: ${sessionFuncs.player.Name} does not have a body on ${zone.id}")
log.debug(s"PlayerHasLeft: ${sessionFuncs.player.Name} does not have a body on ${zone.id}")
case Zone.Population.PlayerHasLeft(zone, Some(tplayer)) =>
if (tplayer.isAlive) {
@ -203,16 +205,20 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
}
case Zone.Population.PlayerCanNotSpawn(zone, tplayer) =>
log.warn(s"${tplayer.Name} can not spawn in zone ${zone.id}; why?")
log.warning(s"${tplayer.Name} can not spawn in zone ${zone.id}; why?")
case Zone.Population.PlayerAlreadySpawned(zone, tplayer) =>
log.warn(s"${tplayer.Name} is already spawned on zone ${zone.id}; is this a clerical error?")
log.warning(s"${tplayer.Name} is already spawned on zone ${zone.id}; is this a clerical error?")
case Zone.Vehicle.CanNotSpawn(zone, vehicle, reason) =>
log.warn(s"${sessionFuncs.player.Name}'s ${vehicle.Definition.Name} can not spawn in ${zone.id} because $reason")
log.warning(
s"${sessionFuncs.player.Name}'s ${vehicle.Definition.Name} can not spawn in ${zone.id} because $reason"
)
case Zone.Vehicle.CanNotDespawn(zone, vehicle, reason) =>
log.warn(s"${sessionFuncs.player.Name}'s ${vehicle.Definition.Name} can not deconstruct in ${zone.id} because $reason")
log.warning(
s"${sessionFuncs.player.Name}'s ${vehicle.Definition.Name} can not deconstruct in ${zone.id} because $reason"
)
case ICS.ZoneResponse(Some(zone)) =>
sessionFuncs.zoning.handleZoneResponse(zone)
@ -349,7 +355,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
log.debug(s"CanNotPutItemInSlot: $msg")
case default =>
log.warn(s"Invalid packet class received: $default from ${sender()}")
log.warning(s"Invalid packet class received: $default from ${sender()}")
}
private def handleGamePkt: PlanetSideGamePacket => Unit = {
@ -363,7 +369,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
sessionFuncs.vehicles.handleDismountVehicleCargo(packet)
case packet: CharacterCreateRequestMessage =>
sessionFuncs.handleCharacterCreateRequest(packet)
sessionFuncs.handleCharacterCreateRequest(packet)
case packet: CharacterRequestMessage =>
sessionFuncs.handleCharacterRequest(packet)
@ -588,6 +594,6 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
sessionFuncs.handleHitHint(packet)
case pkt =>
log.warn(s"Unhandled GamePacket $pkt")
log.warning(s"Unhandled GamePacket $pkt")
}
}

View file

@ -256,5 +256,3 @@ object Tool {
def Definition: FireModeDefinition = fdef
}
}

View file

@ -39,4 +39,4 @@ object NonvitalDefinition {
out
}
}
}
}

View file

@ -654,4 +654,3 @@ object CarrierBehavior {
msgs
}
}

View file

@ -40,4 +40,3 @@ trait MountableWeapons
def Definition: MountableWeaponsDefinition
}

View file

@ -55,4 +55,3 @@ class AmsControl(vehicle: Vehicle)
}
}
}

View file

@ -613,7 +613,7 @@ object BfrControl {
final val Enabled = 38
final val Disabled = 39
}
private case object VehicleExplosion
val dimorphics: List[EquipmentHandiness] = {

View file

@ -59,4 +59,4 @@ object TriggerUsedReason {
ResistUsing = NoResistanceSelection
Model = SimpleResolutions.calculate
}
}
}

View file

@ -89,20 +89,20 @@ object PacketHelpers {
}
/** Create a Codec for an enumeration type that can correctly represent its value
* @param enum the enumeration type to create a codec for
* @param e the enumeration type to create a codec for
* @param storageCodec the Codec used for actually representing the value
* @tparam E The inferred type
* @return Generated codec
*/
def createEnumerationCodec[E <: Enumeration](enum: E, storageCodec: Codec[Int]): Codec[E#Value] = {
def createEnumerationCodec[E <: Enumeration](e: E, storageCodec: Codec[Int]): Codec[E#Value] = {
type Struct = Int :: HNil
val struct: Codec[Struct] = storageCodec.hlist
val primitiveLimit = Math.pow(2, storageCodec.sizeBound.exact.get.toDouble)
// Assure that the enum will always be able to fit in a N-bit int
assert(
enum.maxId <= primitiveLimit,
enum.getClass.getCanonicalName + s": maxId exceeds primitive type (limit of $primitiveLimit, maxId ${enum.maxId})"
e.maxId <= primitiveLimit,
e.getClass.getCanonicalName + s": maxId exceeds primitive type (limit of $primitiveLimit, maxId ${e.maxId})"
)
def to(pkt: E#Value): Struct = {
@ -113,13 +113,13 @@ object PacketHelpers {
struct match {
case enumVal :: HNil =>
// verify that this int can match the enum
val first = enum.values.firstKey.id
val last = enum.maxId - 1
val first = e.values.firstKey.id
val last = e.maxId - 1
if (enumVal >= first && enumVal <= last)
Attempt.successful(enum(enumVal))
Attempt.successful(e(enumVal))
else
Attempt.failure(Err(s"Expected ${enum} with ID between [${first}, ${last}], but got '${enumVal}'"))
Attempt.failure(Err(s"Expected ${e} with ID between [${first}, ${last}], but got '${enumVal}'"))
}
struct.narrow[E#Value](from, to)
@ -130,12 +130,12 @@ object PacketHelpers {
* NOTE: enumerations in scala can't be represented by more than an Int anyways, so this conversion shouldn't matter.
* This is only to overload createEnumerationCodec to work with uint32[L] codecs (which are Long)
*/
def createLongEnumerationCodec[E <: Enumeration](enum: E, storageCodec: Codec[Long]): Codec[E#Value] = {
createEnumerationCodec(enum, storageCodec.xmap[Int](_.toInt, _.toLong))
def createLongEnumerationCodec[E <: Enumeration](e: E, storageCodec: Codec[Long]): Codec[E#Value] = {
createEnumerationCodec(e, storageCodec.xmap[Int](_.toInt, _.toLong))
}
/** Create a Codec for enumeratum's IntEnum type */
def createIntEnumCodec[E <: IntEnumEntry](enum: IntEnum[E], storageCodec: Codec[Int]): Codec[E] = {
def createIntEnumCodec[E <: IntEnumEntry](e: IntEnum[E], storageCodec: Codec[Int]): Codec[E] = {
type Struct = Int :: HNil
val struct: Codec[Struct] = storageCodec.hlist
@ -146,36 +146,36 @@ object PacketHelpers {
def from(struct: Struct): Attempt[E] =
struct match {
case enumVal :: HNil =>
enum.withValueOpt(enumVal) match {
e.withValueOpt(enumVal) match {
case Some(v) => Attempt.successful(v)
case None =>
Attempt.failure(Err(s"Enum value '${enumVal}' not found in values '${enum.values.toString()}'"))
Attempt.failure(Err(s"Enum value '${enumVal}' not found in values '${e.values.toString()}'"))
}
}
struct.narrow[E](from, to)
}
def createLongIntEnumCodec[E <: IntEnumEntry](enum: IntEnum[E], storageCodec: Codec[Long]): Codec[E] = {
createIntEnumCodec(enum, storageCodec.xmap[Int](_.toInt, _.toLong))
def createLongIntEnumCodec[E <: IntEnumEntry](e: IntEnum[E], storageCodec: Codec[Long]): Codec[E] = {
createIntEnumCodec(e, storageCodec.xmap[Int](_.toInt, _.toLong))
}
/** Create a Codec for enumeratum's Enum type */
def createEnumCodec[E <: EnumEntry](enum: Enum[E], storageCodec: Codec[Int]): Codec[E] = {
def createEnumCodec[E <: EnumEntry](e: Enum[E], storageCodec: Codec[Int]): Codec[E] = {
type Struct = Int :: HNil
val struct: Codec[Struct] = storageCodec.hlist
def to(pkt: E): Struct = {
enum.indexOf(pkt) :: HNil
e.indexOf(pkt) :: HNil
}
def from(struct: Struct): Attempt[E] =
struct match {
case enumVal :: HNil =>
enum.valuesToIndex.find(_._2 == enumVal) match {
e.valuesToIndex.find(_._2 == enumVal) match {
case Some((v, _)) => Attempt.successful(v)
case None =>
Attempt.failure(Err(s"Enum index '${enumVal}' not found in values '${enum.valuesToIndex.toString()}'"))
Attempt.failure(Err(s"Enum index '${enumVal}' not found in values '${e.valuesToIndex.toString()}'"))
}
}

View file

@ -37,6 +37,7 @@ object PacketCoding {
): Attempt[BitVector] = {
val seq = packet match {
case _: PlanetSideControlPacket if crypto.isEmpty => BitVector.empty
case _: PlanetSideResetSequencePacket => BitVector.empty
case _ =>
sequence match {
case Some(_sequence) =>
@ -93,6 +94,17 @@ object PacketCoding {
)
case f @ Failure(_) => return f
}
case packet: PlanetSideResetSequencePacket =>
encodePacket(packet) match {
case Successful(_payload) =>
(
PlanetSidePacketFlags.codec
.encode(PlanetSidePacketFlags(PacketType.ResetSequence, secured = false))
.require,
_payload
)
case f @ Failure(_) => return f
}
}
Successful(flags ++ seq ++ payload)

View file

@ -15,4 +15,3 @@ final case class Ignore(data: ByteVector) extends PlanetSideCryptoPacket {
object Ignore extends Marshallable[Ignore] {
implicit val codec: Codec[Ignore] = ("data" | bytes).as[Ignore]
}

View file

@ -11,32 +11,32 @@ sealed abstract class GenericAction(val value: Int) extends IntEnumEntry
object GenericAction extends IntEnum[GenericAction] {
val values: IndexedSeq[GenericAction] = findValues
final case object ShowMosquitoRadar extends GenericAction(value = 3)
final case object HideMosquitoRadar extends GenericAction(value = 4)
final case object MissileLock extends GenericAction(value = 7)
final case object WaspMissileLock extends GenericAction(value = 8)
final case object TRekLock extends GenericAction(value = 9)
final case object DropSpecialItem extends GenericAction(value = 11)
final case object FacilityCaptureFanfare extends GenericAction(value = 12)
final case object NewCharacterBasicTrainingPrompt extends GenericAction(value = 14)
final case object MaxAnchorsExtend_RCV extends GenericAction(value = 15)
final case object MaxAnchorsRelease_RCV extends GenericAction(value = 16)
final case object MaxSpecialEffect_RCV extends GenericAction(value = 20)
final case object StopMaxSpecialEffect_RCV extends GenericAction(value = 21)
final case object CavernFacilityCapture extends GenericAction(value = 22)
final case object CavernFacilityKill extends GenericAction(value = 23)
final case object Imprinted extends GenericAction(value = 24)
final case object NoLongerImprinted extends GenericAction(value = 25)
final case object PurchaseTimersReset extends GenericAction(value = 27)
final case object LeaveWarpQueue_RCV extends GenericAction(value = 28)
final case object AwayFromKeyboard_RCV extends GenericAction(value = 29)
final case object BackInGame_RCV extends GenericAction(value = 30)
final case object FirstPersonViewWithEffect extends GenericAction(value = 31)
final case object ShowMosquitoRadar extends GenericAction(value = 3)
final case object HideMosquitoRadar extends GenericAction(value = 4)
final case object MissileLock extends GenericAction(value = 7)
final case object WaspMissileLock extends GenericAction(value = 8)
final case object TRekLock extends GenericAction(value = 9)
final case object DropSpecialItem extends GenericAction(value = 11)
final case object FacilityCaptureFanfare extends GenericAction(value = 12)
final case object NewCharacterBasicTrainingPrompt extends GenericAction(value = 14)
final case object MaxAnchorsExtend_RCV extends GenericAction(value = 15)
final case object MaxAnchorsRelease_RCV extends GenericAction(value = 16)
final case object MaxSpecialEffect_RCV extends GenericAction(value = 20)
final case object StopMaxSpecialEffect_RCV extends GenericAction(value = 21)
final case object CavernFacilityCapture extends GenericAction(value = 22)
final case object CavernFacilityKill extends GenericAction(value = 23)
final case object Imprinted extends GenericAction(value = 24)
final case object NoLongerImprinted extends GenericAction(value = 25)
final case object PurchaseTimersReset extends GenericAction(value = 27)
final case object LeaveWarpQueue_RCV extends GenericAction(value = 28)
final case object AwayFromKeyboard_RCV extends GenericAction(value = 29)
final case object BackInGame_RCV extends GenericAction(value = 30)
final case object FirstPersonViewWithEffect extends GenericAction(value = 31)
final case object FirstPersonViewFailToDeconstruct extends GenericAction(value = 32)
final case object FailToDeconstruct extends GenericAction(value = 33)
final case object LookingForSquad_RCV extends GenericAction(value = 36)
final case object NotLookingForSquad_RCV extends GenericAction(value = 37)
final case object Unknown45 extends GenericAction(value = 45)
final case object FailToDeconstruct extends GenericAction(value = 33)
final case object LookingForSquad_RCV extends GenericAction(value = 36)
final case object NotLookingForSquad_RCV extends GenericAction(value = 37)
final case object Unknown45 extends GenericAction(value = 45)
final case class Unknown(override val value: Int) extends GenericAction(value)
}
@ -55,16 +55,19 @@ object GenericActionMessage extends Marshallable[GenericActionMessage] {
def apply(i: Int): GenericActionMessage = {
GenericActionMessage(GenericAction.values.find { _.value == i } match {
case Some(enum) => enum
case None => GenericAction.Unknown(i)
case None => GenericAction.Unknown(i)
})
}
private val genericActionCodec = uint(bits = 6).xmap[GenericAction]({
i => GenericAction.values.find { _.value == i } match {
case Some(enum) => enum
case None => GenericAction.Unknown(i)
}
}, enum => enum.value)
private val genericActionCodec = uint(bits = 6).xmap[GenericAction](
{ i =>
GenericAction.values.find { _.value == i } match {
case Some(enum) => enum
case None => GenericAction.Unknown(i)
}
},
e => e.value
)
implicit val codec: Codec[GenericActionMessage] = ("action" | genericActionCodec).as[GenericActionMessage]
}

View file

@ -15,7 +15,7 @@ object TerrainCondition extends Enumeration {
type Type = Value
val Safe, Unsafe = Value
implicit val codec = PacketHelpers.createEnumerationCodec(enum = this, uint(bits = 1))
implicit val codec = PacketHelpers.createEnumerationCodec(e = this, uint(bits = 1))
}
/**
@ -28,11 +28,11 @@ object TerrainCondition extends Enumeration {
* @param pos the vehicle's current position in the game world
*/
final case class InvalidTerrainMessage(
player_guid: PlanetSideGUID,
vehicle_guid: PlanetSideGUID,
proximity_alert: TerrainCondition.Value,
pos: Vector3
) extends PlanetSideGamePacket {
player_guid: PlanetSideGUID,
vehicle_guid: PlanetSideGUID,
proximity_alert: TerrainCondition.Value,
pos: Vector3
) extends PlanetSideGamePacket {
type Packet = InvalidTerrainMessage
def opcode = GamePacketOpcode.InvalidTerrainMessage
def encode = InvalidTerrainMessage.encode(this)
@ -40,8 +40,7 @@ final case class InvalidTerrainMessage(
object InvalidTerrainMessage extends Marshallable[InvalidTerrainMessage] {
implicit val codec: Codec[InvalidTerrainMessage] = (
("player_guid" | PlanetSideGUID.codec) ::
implicit val codec: Codec[InvalidTerrainMessage] = (("player_guid" | PlanetSideGUID.codec) ::
("vehicle_guid" | PlanetSideGUID.codec) ::
("proximity_alert" | TerrainCondition.codec) ::
("pos" | floatL :: floatL :: floatL).narrow[Vector3](

View file

@ -22,7 +22,7 @@ object SquadAction {
val AnyPositions, AvailablePositions, SomeCertifications, AllCertifications = Value
implicit val codec: Codec[SearchMode.Value] = PacketHelpers.createEnumerationCodec(enum = this, uint(bits = 3))
implicit val codec: Codec[SearchMode.Value] = PacketHelpers.createEnumerationCodec(e = this, uint(bits = 3))
}
final case class DisplaySquad() extends SquadAction(code = 0)
@ -280,7 +280,7 @@ object SquadAction {
val squadListDecoratorCodec = (
SquadListDecoration.codec ::
ignore(size = 3)
ignore(size = 3)
).xmap[SquadListDecorator](
{
case value :: _ :: HNil => SquadListDecorator(value)

View file

@ -11,7 +11,7 @@ object MemberEvent extends Enumeration {
val Add, Remove, Promote, UpdateZone, Outfit = Value
implicit val codec = PacketHelpers.createEnumerationCodec(enum = this, uint(bits = 3))
implicit val codec = PacketHelpers.createEnumerationCodec(e = this, uint(bits = 3))
}
final case class SquadMemberEvent(
@ -58,13 +58,18 @@ object SquadMemberEvent extends Marshallable[SquadMemberEvent] {
("unk2" | uint16L) ::
("char_id" | uint32L) ::
("position" | uint4) ::
("player_name" | conditional(action == MemberEvent.Add, PacketHelpers.encodedWideStringAligned(adjustment = 1))) ::
("player_name" | conditional(
action == MemberEvent.Add,
PacketHelpers.encodedWideStringAligned(adjustment = 1)
)) ::
("zone_number" | conditional(action == MemberEvent.Add || action == MemberEvent.UpdateZone, uint16L)) ::
("outfit_id" | conditional(action == MemberEvent.Add || action == MemberEvent.Outfit, uint32L))
}).exmap[SquadMemberEvent](
{
case action :: unk2 :: char_id :: member_position :: player_name :: zone_number :: outfit_id :: HNil =>
Attempt.Successful(SquadMemberEvent(action, unk2, char_id, member_position, player_name, zone_number, outfit_id))
Attempt.Successful(
SquadMemberEvent(action, unk2, char_id, member_position, player_name, zone_number, outfit_id)
)
},
{
case SquadMemberEvent(

View file

@ -16,7 +16,7 @@ object WaypointEventAction extends Enumeration {
val Add, Unknown1, Remove, Unknown3 //unconfirmed
= Value
implicit val codec: Codec[WaypointEventAction.Value] = PacketHelpers.createEnumerationCodec(enum = this, uint2)
implicit val codec: Codec[WaypointEventAction.Value] = PacketHelpers.createEnumerationCodec(e = this, uint2)
}
/**

View file

@ -332,4 +332,3 @@ object MountableInventory {
}
}
}

View file

@ -549,7 +549,7 @@ class CavernRotationService(
}
/**
*
*
* @param sendToSession callback reference
*/
def sendCavernRotationUpdates(sendToSession: ActorRef): Unit = {

View file

@ -56,4 +56,4 @@ object HartTimerActions {
LocalAction.ShuttleState(shuttle.GUID, shuttle.Position, shuttle.Orientation, state)
)
}
}
}

View file

@ -11,11 +11,11 @@ import scodec.codecs.uint2L
* Blame the lack of gender dysphoria on the Terran Republic.
*/
sealed abstract class CharacterSex(
val value: Int,
val pronounSubject: String,
val pronounObject: String,
val possessive: String
) extends IntEnumEntry {
val value: Int,
val pronounSubject: String,
val pronounObject: String,
val possessive: String
) extends IntEnumEntry {
def possessiveNoObject: String = possessive
}
@ -25,21 +25,23 @@ sealed abstract class CharacterSex(
object CharacterSex extends IntEnum[CharacterSex] {
val values = findValues
case object Male extends CharacterSex(
value = 1,
pronounSubject = "he",
pronounObject = "him",
possessive = "his"
)
case object Male
extends CharacterSex(
value = 1,
pronounSubject = "he",
pronounObject = "him",
possessive = "his"
)
case object Female extends CharacterSex(
value = 2,
pronounSubject = "she",
pronounObject = "her",
possessive = "her"
) {
case object Female
extends CharacterSex(
value = 2,
pronounSubject = "she",
pronounObject = "her",
possessive = "her"
) {
override def possessiveNoObject: String = "hers"
}
implicit val codec = PacketHelpers.createIntEnumCodec(enum = this, uint2L)
implicit val codec = PacketHelpers.createIntEnumCodec(e = this, uint2L)
}

View file

@ -11,9 +11,9 @@ sealed abstract class ExperienceType(val value: Int) extends IntEnumEntry
object ExperienceType extends IntEnum[ExperienceType] {
val values: IndexedSeq[ExperienceType] = findValues
case object Normal extends ExperienceType(value = 0)
case object Support extends ExperienceType(value = 2)
case object Normal extends ExperienceType(value = 0)
case object Support extends ExperienceType(value = 2)
case object RabbitBall extends ExperienceType(value = 4)
implicit val codec: Codec[ExperienceType] = PacketHelpers.createIntEnumCodec(enum = this, uint(bits = 3))
implicit val codec: Codec[ExperienceType] = PacketHelpers.createIntEnumCodec(e = this, uint(bits = 3))
}

View file

@ -12,4 +12,4 @@ object MemberAction extends Enumeration {
RemoveIgnoredPlayer = Value
implicit val codec: Codec[MemberAction.Value] = PacketHelpers.createEnumerationCodec(this, uint(bits = 3))
}
}

View file

@ -165,8 +165,8 @@ object MeritCommendation extends Enumeration {
{
case MeritCommendation.None =>
Attempt.successful(0xffffffffL)
case enum =>
Attempt.successful(enum.id.toLong)
case e =>
Attempt.successful(e.id.toLong)
}
)
}

View file

@ -23,5 +23,5 @@ object OxygenState extends Enum[OxygenState] {
case object Recovery extends OxygenState
case object Suffocation extends OxygenState
implicit val codec: Codec[OxygenState] = PacketHelpers.createEnumCodec(enum = this, uint(bits = 1))
implicit val codec: Codec[OxygenState] = PacketHelpers.createEnumCodec(e = this, uint(bits = 1))
}

View file

@ -2,7 +2,7 @@
package net.psforever.types
import enumeratum.values.{IntEnum, IntEnumEntry}
import net.psforever.types.StatisticalElement.{AMS, ANT, AgileExoSuit, ApcNc, ApcTr, ApcVs, Aphelion, AphelionFlight, AphelionGunner, Battlewagon, Colossus, ColossusFlight, ColossusGunner, Droppod, Dropship, Flail, Fury, GalaxyGunship, ImplantTerminalMech, InfiltrationExoSuit, Liberator, Lightgunship, Lightning, Lodestar, Magrider, MechanizedAssaultExoSuit, MediumTransport, Mosquito, Peregrine, PeregrineFlight, PeregrineGunner, PhalanxTurret, Phantasm, PortableMannedTurretNc, PortableMannedTurretTr, PortableMannedTurretVs, Prowler, QuadAssault, QuadStealth, Raider, ReinforcedExoSuit, Router, Skyguard, SpitfireAA, SpitfireCloaked, SpitfireTurret, StandardExoSuit, Sunderer, Switchblade, TankTraps, ThreeManHeavyBuggy, Thunderer, TwoManAssaultBuggy, TwoManHeavyBuggy, TwoManHoverBuggy, VanSentryTurret, Vanguard, Vulture, Wasp}
import net.psforever.types.StatisticalElement.{AMS, ANT, AgileExoSuit, ApcNc, ApcTr, ApcVs, Aphelion, AphelionFlight, AphelionGunner, Battlewagon, Colossus, ColossusFlight, ColossusGunner, Dropship, Flail, Fury, GalaxyGunship, InfiltrationExoSuit, Liberator, Lightgunship, Lightning, Lodestar, Magrider, MechanizedAssaultExoSuit, MediumTransport, Mosquito, Peregrine, PeregrineFlight, PeregrineGunner, PhalanxTurret, PortableMannedTurretNc, PortableMannedTurretTr, PortableMannedTurretVs, Prowler, QuadAssault, QuadStealth, Raider, ReinforcedExoSuit, Router, Skyguard, StandardExoSuit, Sunderer, Switchblade, ThreeManHeavyBuggy, Thunderer, TwoManAssaultBuggy, TwoManHeavyBuggy, TwoManHoverBuggy, VanSentryTurret, Vanguard, Vulture, Wasp}
sealed abstract class StatisticalCategory(val value: Int) extends IntEnumEntry

View file

@ -25,12 +25,12 @@ object Config {
}
implicit def enumeratumIntConfigConvert[A <: IntEnumEntry](implicit
enum: IntEnum[A],
e: IntEnum[A],
ct: ClassTag[A]
): ConfigConvert[A] =
viaNonEmptyStringOpt[A](
v =>
enum.values.toList.collectFirst {
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]
@ -40,12 +40,12 @@ object Config {
)
implicit def enumeratumConfigConvert[A <: EnumEntry](implicit
enum: Enum[A],
e: Enum[A],
ct: ClassTag[A]
): ConfigConvert[A] =
viaNonEmptyStringOpt[A](
v =>
enum.values.toList.collectFirst {
e.values.toList.collectFirst {
case e if e.toString.toLowerCase == v.toLowerCase => e.asInstanceOf[A]
},
_.toString

View file

@ -25,7 +25,7 @@ import net.psforever.objects.serverobject.terminals.implant.ImplantTerminalMech
import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.objects.serverobject.turret.{FacilityTurret, FacilityTurretDefinition}
import net.psforever.objects.serverobject.zipline.ZipLinePath
import net.psforever.objects.sourcing.{DeployableSource, ObjectSource, PlayerSource, TurretSource, VehicleSource}
import net.psforever.objects.sourcing.{DeployableSource, PlayerSource, TurretSource, VehicleSource}
import net.psforever.objects.zones.{MapInfo, Zone, ZoneInfo, ZoneMap}
import net.psforever.types.{Angular, PlanetSideEmpire, Vector3}
import net.psforever.util.DefinitionUtil

View file

@ -45,4 +45,3 @@ class CaptureFlagUpdateMessageTest extends Specification with Debug {
pkt mustEqual stringOne
}
}

View file

@ -65,4 +65,3 @@ class ComponentDamageMessageTest extends Specification {
pkt mustEqual string_off
}
}

View file

@ -54,4 +54,3 @@ class FrameVehicleStateMessageTest extends Specification {
pkt mustEqual string
}
}

View file

@ -28,4 +28,3 @@ class GenericObjectActionAtPositionMessageTest extends Specification {
pkt mustEqual string
}
}

View file

@ -347,4 +347,3 @@ class BattleframeRoboticsTest extends Specification {
}
}
}

View file

@ -21,4 +21,4 @@ class LocalTest extends Specification {
obj.Definition.Name mustEqual "locker-equipment"
}
}
}
}