Merge branch 'master' into cooldown-reset

This commit is contained in:
Fate-JH 2021-04-18 09:14:58 -04:00 committed by GitHub
commit f11bf70af7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 1125 additions and 948 deletions

View file

@ -40,16 +40,16 @@ lazy val psforeverSettings = Seq(
classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.Flat,
resolvers += "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots",
libraryDependencies ++= Seq(
"com.typesafe.akka" %% "akka-actor" % "2.6.13",
"com.typesafe.akka" %% "akka-slf4j" % "2.6.13",
"com.typesafe.akka" %% "akka-protobuf-v3" % "2.6.13",
"com.typesafe.akka" %% "akka-stream" % "2.6.13",
"com.typesafe.akka" %% "akka-testkit" % "2.6.13" % "test",
"com.typesafe.akka" %% "akka-actor-typed" % "2.6.13",
"com.typesafe.akka" %% "akka-cluster-typed" % "2.6.13",
"com.typesafe.akka" %% "akka-coordination" % "2.6.13",
"com.typesafe.akka" %% "akka-cluster-tools" % "2.6.13",
"com.typesafe.akka" %% "akka-slf4j" % "2.6.13",
"com.typesafe.akka" %% "akka-actor" % "2.6.14",
"com.typesafe.akka" %% "akka-slf4j" % "2.6.14",
"com.typesafe.akka" %% "akka-protobuf-v3" % "2.6.14",
"com.typesafe.akka" %% "akka-stream" % "2.6.14",
"com.typesafe.akka" %% "akka-testkit" % "2.6.14" % "test",
"com.typesafe.akka" %% "akka-actor-typed" % "2.6.14",
"com.typesafe.akka" %% "akka-cluster-typed" % "2.6.14",
"com.typesafe.akka" %% "akka-coordination" % "2.6.14",
"com.typesafe.akka" %% "akka-cluster-tools" % "2.6.14",
"com.typesafe.akka" %% "akka-slf4j" % "2.6.14",
"com.typesafe.akka" %% "akka-http" % "10.2.4",
"com.typesafe.scala-logging" %% "scala-logging" % "3.9.3",
"org.specs2" %% "specs2-core" % "4.10.6" % "test",
@ -66,10 +66,10 @@ lazy val psforeverSettings = Seq(
"io.kamon" %% "kamon-apm-reporter" % "2.1.15",
"org.json4s" %% "json4s-native" % "3.6.11",
"io.getquill" %% "quill-jasync-postgres" % "3.7.0",
"org.flywaydb" % "flyway-core" % "7.7.3",
"org.flywaydb" % "flyway-core" % "7.8.1",
"org.postgresql" % "postgresql" % "42.2.19",
"com.typesafe" % "config" % "1.4.1",
"com.github.pureconfig" %% "pureconfig" % "0.14.1",
"com.github.pureconfig" %% "pureconfig" % "0.15.0",
"com.beachape" %% "enumeratum" % "1.6.1",
"joda-time" % "joda-time" % "2.10.10",
"commons-io" % "commons-io" % "2.8.0",

View file

@ -129,7 +129,7 @@ object MiddlewareActor {
* Do nothing.
* Wait to be told to do something.
*/
private def doNothing(): Unit = { }
private def doNothing(): Unit = {}
}
/**
@ -182,7 +182,8 @@ class MiddlewareActor(
val outQueueBundled: mutable.Queue[PlanetSidePacket] = mutable.Queue()
/** Latest outbound sequence number;
* the current sequence is one less than this number */
* the current sequence is one less than this number
*/
var outSequence = 0
/**
@ -202,7 +203,8 @@ class MiddlewareActor(
}
/** Latest outbound subslot number;
* the current subslot is one less than this number */
* the current subslot is one less than this number
*/
var outSubslot = 0
/**
@ -224,12 +226,13 @@ class MiddlewareActor(
/**
* Do not bundle these packets together with other packets
*/
val packetsBundledByThemselves: List[PlanetSidePacket=>Boolean] = List(
val packetsBundledByThemselves: List[PlanetSidePacket => Boolean] = List(
MiddlewareActor.keepAliveMessageGuard,
MiddlewareActor.characterInfoMessageGuard
)
val smpHistoryLength: Int = 100
/** History of created `SlottedMetaPacket`s.
* In case the client does not register receiving a packet by checking against packet subslot index numbers,
* it will dispatch a `RelatedA` packet,
@ -239,24 +242,32 @@ class MiddlewareActor(
* The client and server supposedly maintain reciprocating mechanisms.
*/
val preparedSlottedMetaPackets: Array[SlottedMetaPacket] = new Array[SlottedMetaPacket](smpHistoryLength)
var nextSmpIndex: Int = 0
var acceptedSmpSubslot: Int = 0
var nextSmpIndex: Int = 0
var acceptedSmpSubslot: Int = 0
/** end of life stat */
var timesInReorderQueue: Int = 0
/** end of life stat */
var timesSubslotMissing: Int = 0
/** Delay between runs of the packet bundler/resolver timer (ms);
* 250ms per network update (client upstream), so 10 runs of this bundling code every update */
* 250ms per network update (client upstream), so 10 runs of this bundling code every update
*/
val packetProcessorDelay = Config.app.network.middleware.packetBundlingDelay
/** Timer that handles the bundling and throttling of outgoing packets and resolves disorganized inbound packets */
var packetProcessor: Cancellable = Default.Cancellable
/** how long packets that are out of sequential order wait for the missing sequence before being expedited (ms) */
val inReorderTimeout = Config.app.network.middleware.inReorderTimeout
/** Timer that handles the bundling and throttling of outgoing packets requesting packets with known subslot numbers */
var subslotMissingProcessor: Cancellable = Default.Cancellable
/** how long to wait between repeated requests for packets with known missing subslot numbers (ms) */
val inSubslotMissingDelay = Config.app.network.middleware.inSubslotMissingDelay
/** how many time to repeat the request for a packet with a known missing subslot number */
val inSubslotMissingNumberOfAttempts = Config.app.network.middleware.inSubslotMissingAttempts
@ -274,12 +285,19 @@ class MiddlewareActor(
send(ServerStart(nonce, serverNonce), None, None)
cryptoSetup()
/** 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
* TODO implement this
*/
case (Unknown30(nonce), _) =>
connectionClose()
// TODO ResetSequence
case _ =>
log.warn(s"Unexpected packet type $packet in start (before crypto)")
Behaviors.same
}
case Failure(_) =>
case Failure(unmarshalError) =>
// There is a special case where no crypto is being used.
// The only packet coming through looks like PingMsg. This is a hardcoded
// feature of the client @ 0x005FD618
@ -300,8 +318,8 @@ class MiddlewareActor(
log.error(s"Unexpected non-crypto packet type $packet in start")
Behaviors.same
}
case Failure(e) =>
log.error(s"Could not decode packet in start: $e")
case Failure(decodeError) =>
log.error(s"Could not decode packet in start: '$unmarshalError' / '$decodeError'")
Behaviors.same
}
}
@ -356,17 +374,17 @@ class MiddlewareActor(
packet match {
case (ClientFinished(clientPubKey, _), Some(_)) =>
serverMACBuffer ++= msg.drop(3)
val agreedKey = dh.agree(clientPubKey.toArray)
val agreedKey = dh.agree(clientPubKey.toArray)
val agreedMessage = ByteVector("master secret".getBytes) ++ clientChallenge ++
hex"00000000" ++ serverChallenge ++ hex"00000000"
val masterSecret = new Md5Mac(ByteVector.view(agreedKey)).updateFinal(agreedMessage)
val mac = new Md5Mac(masterSecret)
val masterSecret = new Md5Mac(ByteVector.view(agreedKey)).updateFinal(agreedMessage)
val mac = new Md5Mac(masterSecret)
//TODO verify client challenge?
val serverChallengeResult = mac
.updateFinal(ByteVector("server finished".getBytes) ++ serverMACBuffer ++ hex"01", 0xc)
val encExpansion = ByteVector.view("server expansion".getBytes) ++ hex"0000" ++ serverChallenge ++
val encExpansion = ByteVector.view("server expansion".getBytes) ++ hex"0000" ++ serverChallenge ++
hex"00000000" ++ clientChallenge ++ hex"00000000"
val decExpansion = ByteVector.view("client expansion".getBytes) ++ hex"0000" ++ serverChallenge ++
val decExpansion = ByteVector.view("client expansion".getBytes) ++ hex"0000" ++ serverChallenge ++
hex"00000000" ++ clientChallenge ++ hex"00000000"
val expandedEncKey = mac.updateFinal(encExpansion, 64)
val expandedDecKey = mac.updateFinal(decExpansion, 64)
@ -381,13 +399,12 @@ class MiddlewareActor(
)
send(ServerFinished(serverChallengeResult))
//start the queue processor loop
packetProcessor =
context.system.scheduler.scheduleWithFixedDelay(
packetProcessorDelay,
packetProcessorDelay
)(()=> {
context.self ! ProcessQueue()
})
packetProcessor = context.system.scheduler.scheduleWithFixedDelay(
packetProcessorDelay,
packetProcessorDelay
)(() => {
context.self ! ProcessQueue()
})
active()
case other =>
@ -416,7 +433,7 @@ class MiddlewareActor(
activeSequenceFunc(packet, sequence)
case Successful((packet, None)) =>
in(packet)
case Failure(e) =>
case Failure(e) =>
log.error(s"Could not decode $connectionId's packet: $e")
}
Behaviors.same
@ -446,7 +463,7 @@ class MiddlewareActor(
val onSignal: PartialFunction[(ActorContext[Command], Signal), Behavior[Command]] = {
case (_, PostStop) =>
context.stop(nextActor)
if(timesInReorderQueue > 0 || timesSubslotMissing > 0) {
if (timesInReorderQueue > 0 || timesSubslotMissing > 0) {
log.trace(s"out of sequence checks: $timesInReorderQueue, subslot missing checks: $timesSubslotMissing")
}
packetProcessor.cancel()
@ -575,31 +592,29 @@ class MiddlewareActor(
} else if (outQueue.nonEmpty) {
val bundle = {
var length = 0L
val (_, bundle) = outQueue
.dequeueWhile {
case (packet, payload) =>
// packet length + MultiPacketEx header length
val packetLength = payload.length + (
if (payload.length < 2048) { 8L } //256 * 8; 1L * 8
else if (payload.length < 524288) { 16L } //65536 * 8; 2L * 8
else { 32L } //4L * 8
)
length += packetLength
val (_, bundle) = outQueue.dequeueWhile {
case (packet, payload) =>
// packet length + MultiPacketEx header length
val packetLength = payload.length + (
if (payload.length < 2048) { 8L } //256 * 8; 1L * 8
else if (payload.length < 524288) { 16L } //65536 * 8; 2L * 8
else { 32L } //4L * 8
)
length += packetLength
if (packetsBundledByThemselves.exists { _(packet) }) {
if (length == packetLength) {
length += MTU
true //dequeue only packet
} else {
false //dequeue later
}
if (packetsBundledByThemselves.exists { _(packet) }) {
if (length == packetLength) {
length += MTU * 8
true // dequeue only packet
} else {
// Some packets may be larger than the MTU limit, in that case we dequeue anyway and split later
// We deduct some bytes to leave room for SlottedMetaPacket (4 bytes) and MultiPacketEx (2 bytes + prefix per packet)
length == packetLength || length <= (MTU - 6) * 8
false // dequeue later
}
}
.unzip
} else {
// Some packets may be larger than the MTU limit, in that case we dequeue anyway and split later
// We deduct some bytes to leave room for SlottedMetaPacket (4 bytes) and MultiPacketEx (2 bytes + prefix per packet)
length == packetLength || length <= (MTU - 6) * 8
}
}.unzip
bundle
}
@ -616,7 +631,7 @@ class MiddlewareActor(
case Successful(data) =>
outQueueBundled.enqueue(smp(slot = 0, data.bytes))
sendFirstBundle()
case Failure(cause) =>
case Failure(cause) =>
log.error(s"could not bundle $bundle: ${cause.message}")
//to avoid packets being lost, unwrap bundle and queue the packets individually
bundle.foreach { packet =>
@ -636,7 +651,7 @@ class MiddlewareActor(
* @see `activeNormal`
* @see `activeWithReordering`
*/
private var activeSequenceFunc: (PlanetSidePacket, Int)=>Unit = activeNormal
private var activeSequenceFunc: (PlanetSidePacket, Int) => Unit = activeNormal
/**
* Properly handle the newly-arrived packet based on its sequence number.
@ -651,7 +666,7 @@ class MiddlewareActor(
if (sequence == inSequence + 1) {
inSequence = sequence
in(packet)
} else if(sequence < inSequence) { //expedite this packet
} else if (sequence < inSequence) { //expedite this packet
in(packet)
} else if (sequence == inSequence) {
//do nothing?
@ -681,7 +696,7 @@ class MiddlewareActor(
inSequence = sequence
in(packet)
processInReorderQueue()
} else if(sequence < inSequence) { //expedite this packet
} else if (sequence < inSequence) { //expedite this packet
inReorderQueue.filterInPlace(_.sequence == sequence)
in(packet)
inReorderQueueFunc = inReorderQueueTest
@ -690,7 +705,7 @@ class MiddlewareActor(
//do nothing?
} else {
var insertAtIndex = 0
val length = inReorderQueue.length
val length = inReorderQueue.length
while (insertAtIndex < length && sequence >= inReorderQueue(insertAtIndex).sequence) {
insertAtIndex += 1
}
@ -704,7 +719,7 @@ class MiddlewareActor(
* @see `inReorderQueueTest`
* @see `processInReorderQueueTimeoutOnly`
*/
private var inReorderQueueFunc: ()=>Unit = doNothing
private var inReorderQueueFunc: () => Unit = doNothing
/**
* Examine inbound packets that need to be reordered by sequence number and
@ -715,9 +730,9 @@ class MiddlewareActor(
*/
def processInReorderQueue(): Unit = {
timesInReorderQueue += 1
var currentSequence = inSequence
val currentTime = System.currentTimeMillis()
val takenPackets = (inReorderQueue.indexWhere { currentTime - _.time > inReorderTimeout.toMillis } match {
var currentSequence = inSequence
val currentTime = System.currentTimeMillis()
val takenPackets = (inReorderQueue.indexWhere { currentTime - _.time > inReorderTimeout.toMillis } match {
case -1 =>
inReorderQueue
.takeWhile { entry =>
@ -731,7 +746,7 @@ class MiddlewareActor(
}
case index =>
// Forward all packets ahead of any packet that has been in the queue for 50ms
val entries = inReorderQueue.take(index + 1)
val entries = inReorderQueue.take(index + 1)
currentSequence = entries.last.sequence
entries
}).map(_.packet)
@ -757,7 +772,7 @@ class MiddlewareActor(
inReorderQueue.dropInPlace(takenPackets.length)
takenPackets.foreach { p =>
inReorderQueueFunc = inReorderQueueTest
inSequence = p.sequence
inSequence = p.sequence
in(p.packet)
}
}
@ -783,7 +798,7 @@ class MiddlewareActor(
* @see `inSubslotNotMissing`
* @see `inSubslotMissingRequests`
*/
private var activeSubslotsFunc: (Int, Int, ByteVector)=>Unit = inSubslotNotMissing
private var activeSubslotsFunc: (Int, Int, ByteVector) => Unit = inSubslotNotMissing
/**
* What to do with a `SlottedMetaPacket` control packet normally.
@ -851,7 +866,7 @@ class MiddlewareActor(
* resume normal operations when acting upon inbound `SlottedMetaPacket` packets.
* @param slot the optional slot to report the "first" `RelatedB` in a "while"
*/
def inSubslotsMissingRequestsFinished(slot: Int = 0) : Unit = {
def inSubslotsMissingRequestsFinished(slot: Int = 0): Unit = {
if (inSubslotsMissing.isEmpty) {
subslotMissingProcessor.cancel()
activeSubslotsFunc = inSubslotNotMissing
@ -870,25 +885,25 @@ class MiddlewareActor(
*/
def askForMissingSubslots(): Unit = {
if (subslotMissingProcessor.isCancelled) {
subslotMissingProcessor =
context.system.scheduler.scheduleWithFixedDelay(
initialDelay = 0.milliseconds,
inSubslotMissingDelay
)(()=> {
inSubslotsMissing.synchronized {
timesSubslotMissing += inSubslotsMissing.size
inSubslotsMissing.foreach { case (subslot, attempt) =>
subslotMissingProcessor = context.system.scheduler.scheduleWithFixedDelay(
initialDelay = 0.milliseconds,
inSubslotMissingDelay
)(() => {
inSubslotsMissing.synchronized {
timesSubslotMissing += inSubslotsMissing.size
inSubslotsMissing.foreach {
case (subslot, attempt) =>
val value = attempt - 1
if(value > 0) {
if (value > 0) {
inSubslotsMissing(subslot) = value
} else {
inSubslotsMissing.remove(subslot)
}
send(RelatedA(0, subslot))
}
inSubslotsMissingRequestsFinished()
}
})
inSubslotsMissingRequestsFinished()
}
})
}
}

View file

@ -207,6 +207,8 @@ object AvatarActor {
private case class SetStamina(stamina: Int) extends Command
private case class SetImplantInitialized(implantType: ImplantType) extends Command
final case class AvatarResponse(avatar: Avatar)
final case class AvatarLoginResponse(avatar: Avatar)
@ -486,14 +488,14 @@ class AvatarActor(
)
} else {
var requiredByCert: Set[Certification] = Set(certification)
var removeThese: Set[Certification] = Set(certification)
val allCerts: Set[Certification] = Certification.values.toSet
var removeThese: Set[Certification] = Set(certification)
val allCerts: Set[Certification] = Certification.values.toSet
do {
removeThese = allCerts.filter { testingCert =>
testingCert.requires.intersect(removeThese).nonEmpty
}
requiredByCert = requiredByCert ++ removeThese
} while(removeThese.nonEmpty)
} while (removeThese.nonEmpty)
Future
.sequence(
@ -745,13 +747,12 @@ class AvatarActor(
Behaviors.same
case ActivateImplant(implantType) =>
val res = avatar.implants.zipWithIndex.collectFirst {
avatar.implants.zipWithIndex.collectFirst {
case (Some(implant), index) if implant.definition.implantType == implantType => (implant, index)
}
res match {
} match {
case Some((implant, slot)) =>
if (!implant.initialized) {
log.error(s"requested activation of uninitialized implant $implant")
log.warn(s"requested activation of uninitialized implant $implantType")
} else if (
!consumeStamina(implant.definition.ActivationStaminaCost) ||
avatar.stamina < implant.definition.StaminaCost
@ -799,6 +800,25 @@ class AvatarActor(
}
Behaviors.same
case SetImplantInitialized(implantType) =>
avatar.implants.zipWithIndex.collectFirst {
case (Some(implant), index) if implant.definition.implantType == implantType => index
} match {
case Some(index) =>
sessionActor ! SessionActor.SendResponse(
AvatarImplantMessage(session.get.player.GUID, ImplantAction.Initialization, index, 1)
)
avatar = avatar.copy(implants = avatar.implants.map {
case Some(implant) if implant.definition.implantType == implantType =>
Some(implant.copy(initialized = true))
case other => other
})
case None => log.error(s"set initialized called for unknown implant $implantType")
}
Behaviors.same
case DeactivateImplant(implantType) =>
deactivateImplant(implantType)
Behaviors.same
@ -819,7 +839,7 @@ class AvatarActor(
val totalStamina = math.min(avatar.maxStamina, avatar.stamina + stamina)
val fatigued = if (avatar.fatigued && totalStamina >= 20) {
avatar.implants.zipWithIndex.foreach {
case (Some(implant), slot) =>
case (Some(_), slot) =>
sessionActor ! SessionActor.SendResponse(
AvatarImplantMessage(session.get.player.GUID, ImplantAction.OutOfStamina, slot, 0)
)
@ -1013,28 +1033,21 @@ class AvatarActor(
)
)
implantTimers.get(slot).foreach(_.cancel())
implantTimers(slot) = context.scheduleOnce(
implant.definition.InitializationDuration.seconds,
context.self,
SetImplantInitialized(implant.definition.implantType)
)
// Start client side initialization timer, visible on the character screen
// Progress accumulates according to the client's knowledge of the implant initialization time
// What is normally a 60s timer that is set to 120s on the server will still visually update as if 60s
// What is normally a 60s timer that is set to 120s on the server will still visually update as if 60s\
session.get.zone.AvatarEvents ! AvatarServiceMessage(
avatar.name,
AvatarAction.SendResponse(Service.defaultPlayerGUID, ActionProgressMessage(slot + 6, 0))
)
implantTimers.get(slot).foreach(_.cancel())
implantTimers(slot) = context.system.scheduler.scheduleOnce(
implant.definition.InitializationDuration.seconds,
() => {
avatar = avatar.copy(implants = avatar.implants.map {
case Some(implant) => Some(implant.copy(initialized = true))
case None => None
})
sessionActor ! SessionActor.SendResponse(
AvatarImplantMessage(session.get.player.GUID, ImplantAction.Initialization, slot, 1)
)
}
)
case (None, _) => ;
}
}
@ -1052,16 +1065,15 @@ class AvatarActor(
AvatarImplantMessage(session.get.player.GUID, ImplantAction.Initialization, slot, 0)
)
)
Some(implant.copy(initialized = false))
Some(implant.copy(initialized = false, active = false))
case (None, _) => None
})
}
def deactivateImplant(implantType: ImplantType): Unit = {
val res = avatar.implants.zipWithIndex.collectFirst {
avatar.implants.zipWithIndex.collectFirst {
case (Some(implant), index) if implant.definition.implantType == implantType => (implant, index)
}
res match {
} match {
case Some((implant, slot)) =>
implantTimers(slot).cancel()
avatar = avatar.copy(

View file

@ -363,7 +363,7 @@ class ChatActor(
session.zone.Buildings.values
})
.flatMap { building => building.Amenities.filter { _.isInstanceOf[ResourceSilo] } }
if(silos.isEmpty) {
if (silos.isEmpty) {
sessionActor ! SessionActor.SendResponse(
ChatMsg(UNK_229, true, "Server", s"no targets for ntu found with parameters $facility", None)
)
@ -371,24 +371,29 @@ class ChatActor(
customNtuValue match {
// x = n0% of maximum capacitance
case Some(value) if value > -1 && value < 11 =>
silos.collect { case silo: ResourceSilo =>
silo.Actor ! ResourceSilo.UpdateChargeLevel(value * silo.MaxNtuCapacitor * 0.1f - silo.NtuCapacitor)
silos.collect {
case silo: ResourceSilo =>
silo.Actor ! ResourceSilo.UpdateChargeLevel(
value * silo.MaxNtuCapacitor * 0.1f - silo.NtuCapacitor
)
}
// capacitance set to x (where x > 10) exactly, within limits
case Some(value) =>
silos.collect { case silo: ResourceSilo =>
silo.Actor ! ResourceSilo.UpdateChargeLevel(value - silo.NtuCapacitor)
silos.collect {
case silo: ResourceSilo =>
silo.Actor ! ResourceSilo.UpdateChargeLevel(value - silo.NtuCapacitor)
}
case None =>
// x >= n0% of maximum capacitance and x <= maximum capacitance
val rand = new scala.util.Random
silos.collect { case silo: ResourceSilo =>
val a = 7
val b = 10 - a
val tenth = silo.MaxNtuCapacitor * 0.1f
silo.Actor ! ResourceSilo.UpdateChargeLevel(
a * tenth + rand.nextFloat() * b * tenth - silo.NtuCapacitor
)
silos.collect {
case silo: ResourceSilo =>
val a = 7
val b = 10 - a
val tenth = silo.MaxNtuCapacitor * 0.1f
silo.Actor ! ResourceSilo.UpdateChargeLevel(
a * tenth + rand.nextFloat() * b * tenth - silo.NtuCapacitor
)
}
}
@ -402,17 +407,17 @@ class ChatActor(
val (faction, factionPos): (PlanetSideEmpire.Value, Option[Int]) = args.zipWithIndex
.map { case (factionName, pos) => (factionName.toLowerCase, pos) }
.flatMap {
case ("tr", pos) => Some(PlanetSideEmpire.TR, pos)
case ("nc", pos) => Some(PlanetSideEmpire.NC, pos)
case ("vs", pos) => Some(PlanetSideEmpire.VS, pos)
case ("none", pos) => Some(PlanetSideEmpire.NEUTRAL, pos)
case ("bo", pos) => Some(PlanetSideEmpire.NEUTRAL, pos)
case ("tr", pos) => Some(PlanetSideEmpire.TR, pos)
case ("nc", pos) => Some(PlanetSideEmpire.NC, pos)
case ("vs", pos) => Some(PlanetSideEmpire.VS, pos)
case ("none", pos) => Some(PlanetSideEmpire.NEUTRAL, pos)
case ("bo", pos) => Some(PlanetSideEmpire.NEUTRAL, pos)
case ("neutral", pos) => Some(PlanetSideEmpire.NEUTRAL, pos)
case _ => None
case _ => None
}
.headOption match {
case Some((isFaction, pos)) => (isFaction, Some(pos))
case None => (session.player.Faction, None)
case None => (session.player.Faction, None)
}
val (buildingsOption, buildingPos): (Option[Seq[Building]], Option[Int]) = args.zipWithIndex.flatMap {
@ -475,14 +480,11 @@ class ChatActor(
// [all [<empire>|none]]
(Some(1) | None, Some(0), None, Some(_), None) =>
val buildings: Seq[Building] = buildingsOption.getOrElse(
session.zone.Buildings
.values
.filter { building =>
building.PlayersInSOI.exists { soiPlayer =>
session.player.CharId == soiPlayer.CharId
}
session.zone.Buildings.values.filter { building =>
building.PlayersInSOI.exists { soiPlayer =>
session.player.CharId == soiPlayer.CharId
}
.toSeq
}.toSeq
)
buildings foreach { building =>
// TODO implement timer
@ -563,12 +565,21 @@ class ChatActor(
ChatChannel.Default()
)
case (CMT_VOICE, _, _) =>
chatService ! ChatService.Message(
session,
message.copy(recipient = session.player.Name),
ChatChannel.Default()
)
case (CMT_VOICE, _, contents) =>
// SH prefix are tactical voice macros only sent to squad
if (contents.startsWith("SH")) {
channels.foreach {
case channel: ChatChannel.Squad =>
chatService ! ChatService.Message(session, message.copy(recipient = session.player.Name), channel)
case _ =>
}
} else {
chatService ! ChatService.Message(
session,
message.copy(recipient = session.player.Name),
ChatChannel.Default()
)
}
case (CMT_TELL, _, _) if !session.player.silenced =>
chatService ! ChatService.Message(
@ -675,11 +686,11 @@ class ChatActor(
case (CMT_WARP, _, contents) if gmCommandAllowed =>
val buffer = contents.toLowerCase.split("\\s+")
val (coordinates, waypoint) = (buffer.lift(0), buffer.lift(1), buffer.lift(2)) match {
case (Some(x), Some(y), Some(z)) => (Some(x, y, z), None)
case (Some("to"), Some(character), None) => (None, None) // TODO not implemented
case (Some("near"), Some(objectName), None) => (None, None) // TODO not implemented
case (Some(waypoint), None, None) if waypoint.nonEmpty => (None, Some(waypoint))
case _ => (None, None)
case (Some(x), Some(y), Some(z)) => (Some(x, y, z), None)
case (Some("to"), Some(character), None) => (None, None) // TODO not implemented
case (Some("near"), Some(objectName), None) => (None, None) // TODO not implemented
case (Some(waypoint), None, None) if waypoint.nonEmpty => (None, Some(waypoint))
case _ => (None, None)
}
(coordinates, waypoint) match {
case (Some((x, y, z)), None) if List(x, y, z).forall { str =>
@ -690,7 +701,10 @@ class ChatActor(
case (None, Some(waypoint)) if waypoint == "-list" =>
val zone = PointOfInterest.get(session.player.Zone.id)
zone match {
case Some(zone: PointOfInterest) => sessionActor ! SessionActor.SendResponse(ChatMsg(UNK_229, true, "", PointOfInterest.listAll(zone), None))
case Some(zone: PointOfInterest) =>
sessionActor ! SessionActor.SendResponse(
ChatMsg(UNK_229, true, "", PointOfInterest.listAll(zone), None)
)
case _ => ChatMsg(UNK_229, true, "", s"unknown player zone '${session.player.Zone.id}'", None)
}
case (None, Some(waypoint)) if waypoint != "-help" =>
@ -939,7 +953,8 @@ class ChatActor(
case CMT_VOICE =>
if (
session.zone == fromSession.zone &&
Vector3.Distance(session.player.Position, fromSession.player.Position) < 25
Vector3.Distance(session.player.Position, fromSession.player.Position) < 25 ||
message.contents.startsWith("SH") // tactical squad voice macro
) {
sessionActor ! SessionActor.SendResponse(message)
}

View file

@ -60,7 +60,12 @@ import net.psforever.services.local.support.{CaptureFlagManager, HackCaptureActo
import net.psforever.services.local.{LocalAction, LocalResponse, LocalServiceMessage, LocalServiceResponse}
import net.psforever.services.properties.PropertyOverrideManager
import net.psforever.services.support.SupportActor
import net.psforever.services.teamwork.{SquadResponse, SquadServiceMessage, SquadServiceResponse, SquadAction => SquadServiceAction}
import net.psforever.services.teamwork.{
SquadResponse,
SquadServiceMessage,
SquadServiceResponse,
SquadAction => SquadServiceAction
}
import net.psforever.services.hart.HartTimer
import net.psforever.services.vehicle.{VehicleAction, VehicleResponse, VehicleServiceMessage, VehicleServiceResponse}
import net.psforever.services.{RemoverActor, Service, ServiceManager, InterstellarClusterService => ICS}
@ -202,7 +207,8 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
var setupAvatarFunc: () => Unit = AvatarCreate
var setCurrentAvatarFunc: Player => Unit = SetCurrentAvatarNormally
var persist: () => Unit = NoPersistence
var specialItemSlotGuid : Option[PlanetSideGUID] = None // If a special item (e.g. LLU) has been attached to the player the GUID should be stored here, or cleared when dropped, since the drop hotkey doesn't send the GUID of the object to be dropped.
var specialItemSlotGuid: Option[PlanetSideGUID] =
None // If a special item (e.g. LLU) has been attached to the player the GUID should be stored here, or cleared when dropped, since the drop hotkey doesn't send the GUID of the object to be dropped.
/**
* used during zone transfers to maintain reference to seated vehicle (which does not yet exist in the new zone)
@ -349,14 +355,20 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
out
case None =>
//delete stale entity reference from client
log.warn(s"ValidObject - ${player.Name} has an invalid GUID ${id.get.guid}, believing it in ${player.Sex.possessive} locker")
log.warn(
s"ValidObject - ${player.Name} has an invalid GUID ${id.get.guid}, believing it in ${player.Sex.possessive} locker"
)
sendResponse(ObjectDeleteMessage(id.get, 0))
None
}
case Some(obj) if obj.HasGUID && obj.GUID != id.get =>
log.error(s"ValidObject: ${player.Name} found an object that isn't the one ${player.Sex.pronounSubject} thought it was in zone ${continent.id}")
log.debug(s"ValidObject: potentially fatal error in ${continent.id} - requested ${id.get}, got ${obj.Definition.Name} with ${obj.GUID}; GUID mismatch")
log.error(
s"ValidObject: ${player.Name} found an object that isn't the one ${player.Sex.pronounSubject} thought it was in zone ${continent.id}"
)
log.debug(
s"ValidObject: potentially fatal error in ${continent.id} - requested ${id.get}, got ${obj.Definition.Name} with ${obj.GUID}; GUID mismatch"
)
None
case out @ Some(obj) if obj.HasGUID =>
@ -570,23 +582,26 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
case Some(entry) if vehicle.Seats(entry.mount).occupant.contains(player) =>
Some(vehicle)
case Some(entry) =>
log.warn(s"TransferPassenger: $playerName tried to mount seat ${entry.mount} during summoning, but it was already occupied, and ${player.Sex.pronounSubject} was rebuked")
log.warn(
s"TransferPassenger: $playerName tried to mount seat ${entry.mount} during summoning, but it was already occupied, and ${player.Sex.pronounSubject} was rebuked"
)
None
case None =>
//log.warn(s"TransferPassenger: $playerName is missing from the manifest of a summoning ${vehicle.Definition.Name} from ${vehicle.Zone.id}")
None
}).orElse {
manifest.cargo.find { _.name.equals(playerName) } match {
case Some(entry) =>
vehicle.CargoHolds(entry.mount).occupant match {
case out @ Some(cargo) if cargo.Seats(0).occupants.exists(_.Name.equals(playerName)) =>
out
case _ =>
None
}
case None =>
None
}} match {
case Some(entry) =>
vehicle.CargoHolds(entry.mount).occupant match {
case out @ Some(cargo) if cargo.Seats(0).occupants.exists(_.Name.equals(playerName)) =>
out
case _ =>
None
}
case None =>
None
}
} match {
case Some(v: Vehicle) =>
galaxyService ! Service.Leave(Some(temp_channel)) //temporary vehicle-specific channel (see above)
deadState = DeadState.Release
@ -972,7 +987,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
case ztype =>
if (ztype != Zoning.Method.None) {
log.warn(s"SpawnPointResponse: ${player.Name}'s zoning was not in order at the time a response was received; attempting to guess what ${player.Sex.pronounSubject} wants to do")
log.warn(
s"SpawnPointResponse: ${player.Name}'s zoning was not in order at the time a response was received; attempting to guess what ${player.Sex.pronounSubject} wants to do"
)
}
val previousZoningType = zoningType
CancelZoningProcess()
@ -994,7 +1011,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
else
LoadZonePhysicalSpawnPoint(zone.id, pos, ori, CountSpawnDelay(zone.id, spawnPoint, continent.id))
case None =>
log.warn(s"SpawnPointResponse: ${player.Name} received no spawn point response when asking InterstellarClusterService; sending home")
log.warn(
s"SpawnPointResponse: ${player.Name} received no spawn point response when asking InterstellarClusterService; sending home"
)
//Thread.sleep(1000) // throttle in case of infinite loop
RequestSanctuaryZoneSpawn(player, currentZone = 0)
}
@ -1137,7 +1156,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
log.warn(
s"FinalizeDeployable: deployable ${definition.Item}@$guid not handled by specific case"
)
log.warn(s"FinalizeDeployable: deployable ${definition.Item}@$guid will be cleaned up, but may not get unregistered properly")
log.warn(
s"FinalizeDeployable: deployable ${definition.Item}@$guid will be cleaned up, but may not get unregistered properly"
)
TryDropFDU(tool, index, obj.Position)
obj.Position = Vector3.Zero
continent.Deployables ! Zone.Deployable.Dismiss(obj)
@ -1382,8 +1403,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
} else {
keepAliveFunc = GetMountableAndSeat(None, player, continent) match {
case (Some(v: Vehicle), Some(seatNumber))
if seatNumber > 0 && v.WeaponControlledFromSeat(seatNumber).isEmpty => KeepAlivePersistence
case _ => NormalKeepAlive
if seatNumber > 0 && v.WeaponControlledFromSeat(seatNumber).isEmpty =>
KeepAlivePersistence
case _ => NormalKeepAlive
}
}
//if not the condition above, player has started playing normally
@ -1784,7 +1806,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
case AvatarResponse.EnvironmentalDamage(target, source, amount) =>
CancelZoningProcessWithDescriptiveReason("cancel_dmg")
//TODO damage marker?
//TODO damage marker?
case AvatarResponse.Destroy(victim, killer, weapon, pos) =>
// guid = victim // killer = killer ;)
@ -1897,7 +1919,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
DrowningTarget(player.guid, player.progress, player.state),
vehicle match {
case Some(vinfo) => Some(DrowningTarget(vinfo.guid, vinfo.progress, vinfo.state))
case None => None
case None => None
}
)
)
@ -2331,7 +2353,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
}
case LocalResponse.SendHackMessageHackCleared(target_guid, unk1, unk2) =>
//log.trace(s"Clearing hack for $target_guid")
sendResponse(HackMessage(0, target_guid, guid, 0, unk1, HackState.HackCleared, unk2))
case LocalResponse.HackObject(target_guid, unk1, unk2) =>
HackObject(target_guid, unk1, unk2)
@ -2353,11 +2375,13 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
case LocalResponse.LluSpawned(llu) =>
// Create LLU on client
sendResponse(ObjectCreateMessage(
llu.Definition.ObjectId,
llu.GUID,
llu.Definition.Packet.ConstructorData(llu).get
))
sendResponse(
ObjectCreateMessage(
llu.Definition.ObjectId,
llu.GUID,
llu.Definition.Packet.ConstructorData(llu).get
)
)
sendResponse(TriggerSoundMessage(TriggeredSound.LLUMaterialize, llu.Position, unk = 20, 0.8000001f))
@ -2399,8 +2423,11 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
case LocalResponse.ShuttleEvent(ev) =>
val msg = OrbitalShuttleTimeMsg(
ev.u1, ev.u2,
ev.t1, ev.t2, ev.t3,
ev.u1,
ev.u2,
ev.t1,
ev.t2,
ev.t3,
ev.pairs.map { case ((a, b), c) => PadAndShuttlePair(a, b, c) }
)
sendResponse(msg)
@ -2468,8 +2495,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
MountingAction(tplayer, obj, seat_number)
keepAliveFunc = KeepAlivePersistence
case Mountable.CanMount(obj: Vehicle, seat_number, _)
if obj.Definition == GlobalDefinitions.orbital_shuttle =>
case Mountable.CanMount(obj: Vehicle, seat_number, _) if obj.Definition == GlobalDefinitions.orbital_shuttle =>
CancelZoningProcessWithDescriptiveReason("cancel_mount")
log.info(s"${player.Name} mounts the orbital shuttle")
CancelAllProximityUnits()
@ -2478,13 +2504,11 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
case Mountable.CanMount(obj: Vehicle, seat_number, _) =>
CancelZoningProcessWithDescriptiveReason("cancel_mount")
log.info(s"${player.Name} mounts the ${obj.Definition.Name} in ${
obj.SeatPermissionGroup(seat_number) match {
case Some(AccessPermissionGroup.Driver) => "the driver seat"
case Some(seatType) => s"a $seatType seat, #$seat_number"
case None => "a seat"
}
}")
log.info(s"${player.Name} mounts the ${obj.Definition.Name} in ${obj.SeatPermissionGroup(seat_number) match {
case Some(AccessPermissionGroup.Driver) => "the driver seat"
case Some(seatType) => s"a $seatType seat, #$seat_number"
case None => "a seat"
}}")
val obj_guid: PlanetSideGUID = obj.GUID
CancelAllProximityUnits()
sendResponse(PlanetsideAttributeMessage(obj_guid, 0, obj.Health))
@ -2541,12 +2565,12 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
DismountAction(tplayer, obj, seat_num)
case Mountable.CanDismount(obj: Vehicle, seat_num, mount_point)
if obj.Definition == GlobalDefinitions.orbital_shuttle =>
if obj.Definition == GlobalDefinitions.orbital_shuttle =>
val pguid = player.GUID
if (obj.MountedIn.nonEmpty) {
//dismount to hart lobby
log.info(s"${tplayer.Name} dismounts the orbital shuttle into the lobby")
val sguid = obj.GUID
val sguid = obj.GUID
val (pos, zang) = Vehicles.dismountShuttle(obj, mount_point)
tplayer.Position = pos
sendResponse(DelayedPathMountMsg(pguid, sguid, 60, true))
@ -2554,8 +2578,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
continent.id,
LocalAction.SendResponse(ObjectDetachMessage(sguid, pguid, pos, 0, 0, zang))
)
}
else {
} else {
//get ready for orbital drop
DismountAction(tplayer, obj, seat_num)
log.info(s"${player.Name} is prepped for dropping")
@ -2582,8 +2605,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
}
keepAliveFunc = NormalKeepAlive
case Mountable.CanDismount(obj: Vehicle, seat_num, _)
if obj.Definition == GlobalDefinitions.droppod =>
case Mountable.CanDismount(obj: Vehicle, seat_num, _) if obj.Definition == GlobalDefinitions.droppod =>
log.info(s"${tplayer.Name} has landed on ${continent.id}")
UnaccessContainer(obj)
DismountAction(tplayer, obj, seat_num)
@ -2945,7 +2967,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
avatarActor ! AvatarActor.UpdatePurchaseTime(vehicle.Definition)
vehicle.MountPoints.find { case (_, mp) => mp.seatIndex == 0 } match {
case Some((mountPoint, _)) => vehicle.Actor ! Mountable.TryMount(player, mountPoint)
case _ => ;
case _ => ;
}
case VehicleResponse.PlayerSeatedInVehicle(vehicle, pad) =>
@ -2991,7 +3013,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
vehicle.PassengerInSeat(player) match {
case Some(seatNum) =>
//participant: observe changes to equipment
(old_weapons ++ old_inventory).foreach { case (_, eguid) => sendResponse(ObjectDeleteMessage(eguid, 0)) }
(old_weapons ++ old_inventory).foreach {
case (_, eguid) => sendResponse(ObjectDeleteMessage(eguid, 0))
}
UpdateWeaponAtSeatPosition(vehicle, seatNum)
case None =>
//observer: observe changes to external equipment
@ -3134,7 +3158,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
player.Actor ! JammableUnit.ClearJammeredStatus()
player.Actor ! JammableUnit.ClearJammeredSound()
}
if (deadState != DeadState.Alive) {
val originalDeadState = deadState
deadState = DeadState.Alive
if (originalDeadState != DeadState.Alive) {
avatarActor ! AvatarActor.ResetImplants()
}
@ -3148,8 +3174,6 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
sendResponse(
SetChatFilterMessage(ChatChannel.Platoon, false, ChatChannel.values.toList)
) //TODO will not always be "on" like this
val originalDeadState = deadState
deadState = DeadState.Alive
sendResponse(AvatarDeadStateMessage(DeadState.Alive, 0, 0, tplayer.Position, player.Faction, true))
//looking for squad (members)
if (tplayer.avatar.lookingForSquad || lfsm) {
@ -3363,7 +3387,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
def handleGamePkt(pkt: PlanetSideGamePacket) =
pkt match {
case ConnectToWorldRequestMessage(server, token, majorVersion, minorVersion, revision, buildDate, unk) =>
log.trace(s"ConnectToWorldRequestMessage: client with versioning $majorVersion.$minorVersion.$revision, $buildDate has sent token $token to the server")
log.trace(
s"ConnectToWorldRequestMessage: client with versioning $majorVersion.$minorVersion.$revision, $buildDate has sent token $token to the server"
)
sendResponse(ChatMsg(ChatMessageType.CMT_CULLWATERMARK, false, "", "", None))
import scala.concurrent.ExecutionContext.Implicits.global
clientKeepAlive.cancel()
@ -3572,7 +3598,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
_.GUID != vehicle.GUID
}
case Some(_) =>
log.warn(s"BeginZoningMessage: ${player.Name} thought ${player.Sex.pronounSubject} was sitting in a vehicle, but it just evaporated around ${player.Sex.pronounObject}")
log.warn(
s"BeginZoningMessage: ${player.Name} thought ${player.Sex.pronounSubject} was sitting in a vehicle, but it just evaporated around ${player.Sex.pronounObject}"
)
player.VehicleSeated = None
(b, List.empty[Vehicle])
case None =>
@ -3689,7 +3717,8 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
ToggleTeleportSystem(obj, TelepadLike.AppraiseTeleportationSystem(obj, continent))
}
val name = avatar.name
serviceManager.ask(Lookup("hart"))(Timeout(2 seconds))
serviceManager
.ask(Lookup("hart"))(Timeout(2 seconds))
.onComplete {
case Success(LookupResult("hart", ref)) =>
ref ! HartTimer.Update(continentId, name)
@ -3990,7 +4019,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
}
case msg @ VehicleSubStateMessage(vehicle_guid, player_guid, vehicle_pos, vehicle_ang, vel, unk1, unk2) =>
log.debug(s"VehicleSubState: $vehicle_guid, ${player.Name}_guid, $vehicle_pos, $vehicle_ang, $vel, $unk1, $unk2")
log.debug(
s"VehicleSubState: $vehicle_guid, ${player.Name}_guid, $vehicle_pos, $vehicle_ang, $vel, $unk1, $unk2"
)
case msg @ ProjectileStateMessage(projectile_guid, shot_pos, shot_vel, shot_orient, seq, end, target_guid) =>
val index = projectile_guid.guid - Projectile.baseUID
@ -4044,19 +4075,19 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
log.warn(s"SpawnRequestMessage: request consumed because ${player.Name} is already respawning ...")
}
case _ : SetChatFilterMessage => //msg @ SetChatFilterMessage(send_channel, origin, whitelist) => ;
case _: SetChatFilterMessage => //msg @ SetChatFilterMessage(send_channel, origin, whitelist) => ;
case msg : ChatMsg =>
case msg: ChatMsg =>
chatActor ! ChatActor.Message(msg)
case _ : VoiceHostRequest =>
case _: VoiceHostRequest =>
log.trace(s"VoiceHostRequest: ${player.Name} requested in-game voice chat.")
sendResponse(VoiceHostKill())
sendResponse(
ChatMsg(ChatMessageType.CMT_OPEN, false, "", "Try our Discord at https://discord.gg/0nRe5TNbTYoUruA4", None)
)
case _ : VoiceHostInfo =>
case _: VoiceHostInfo =>
sendResponse(VoiceHostKill())
case msg @ ChangeAmmoMessage(item_guid, unk1) =>
@ -4182,7 +4213,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
)
Some(tool)
case _ =>
log.warn(s"ChangeFireState_Stop: ${player.Name} never started firing item ${item_guid.guid} in the first place?")
log.warn(
s"ChangeFireState_Stop: ${player.Name} never started firing item ${item_guid.guid} in the first place?"
)
None
}
}
@ -4236,7 +4269,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
log.warn(s"DropItem: ${player.Name} wanted to drop a $obj, but that isn't possible")
case None =>
sendResponse(ObjectDeleteMessage(item_guid, 0)) //this is fine; item doesn't exist to the server anyway
log.warn(s"DropItem: ${player.Name} wanted to drop an item ${item_guid.guid}, but it was nowhere to be found")
log.warn(
s"DropItem: ${player.Name} wanted to drop an item ${item_guid.guid}, but it was nowhere to be found"
)
}
case msg @ PickupItemMessage(item_guid, player_guid, unk1, unk2) =>
@ -4287,7 +4322,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
reloadValue
}
val finalReloadValue = actualReloadValue + currentMagazine
log.info(s"${player.Name} successfully reloaded $reloadValue ${tool.AmmoType} into ${tool.Definition.Name}")
log.info(
s"${player.Name} successfully reloaded $reloadValue ${tool.AmmoType} into ${tool.Definition.Name}"
)
tool.Magazine = finalReloadValue
sendResponse(ReloadMessage(item_guid, finalReloadValue, unk1))
continent.AvatarEvents ! AvatarServiceMessage(
@ -4296,7 +4333,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
)
}
} else {
log.warn(s"ReloadMessage: the ${tool.Definition.Name} under ${player.Name}'s control can not reload (full=$magazineSize, want=$reloadValue)")
log.warn(
s"ReloadMessage: the ${tool.Definition.Name} under ${player.Name}'s control can not reload (full=$magazineSize, want=$reloadValue)"
)
}
case (_, Some(_)) =>
log.warn(s"ReloadMessage: the object that was found for $item_guid was not a Tool")
@ -4394,8 +4433,8 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
(session.account.gm ||
(player.avatar.vehicle.contains(object_guid) && vehicle.Owner.contains(player.GUID)) ||
(player.Faction == vehicle.Faction &&
(vehicle.Definition.CanBeOwned.nonEmpty &&
(vehicle.Owner.isEmpty || continent.GUID(vehicle.Owner.get).isEmpty) || vehicle.Destroyed))) &&
(vehicle.Definition.CanBeOwned.nonEmpty &&
(vehicle.Owner.isEmpty || continent.GUID(vehicle.Owner.get).isEmpty) || vehicle.Destroyed))) &&
(vehicle.MountedIn.isEmpty || !vehicle.Seats.values.exists(_.isOccupied))
) {
vehicle.Actor ! Vehicle.Deconstruct()
@ -4496,7 +4535,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
) =>
ContainableMoveItem(player.Name, source, destination, item, dest)
case (None, _, _) =>
log.error(s"MoveItem: ${player.Name} wanted to move $item_guid from $source_guid, but could not find source object")
log.error(
s"MoveItem: ${player.Name} wanted to move $item_guid from $source_guid, but could not find source object"
)
case (_, None, _) =>
log.error(
s"MoveItem: ${player.Name} wanted to move $item_guid to $destination_guid, but could not find destination object"
@ -4552,13 +4593,12 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
if (action == ImplantAction.Activation) {
CancelZoningProcessWithDescriptiveReason("cancel_implant")
avatar.implants(slot) match {
case Some(implant) if implant.initialized =>
if (!implant.active) {
case Some(implant) =>
if (status == 1) {
avatarActor ! AvatarActor.ActivateImplant(implant.definition.implantType)
} else {
avatarActor ! AvatarActor.DeactivateImplant(implant.definition.implantType)
}
case Some(implant) if !implant.initialized => ()
case _ => log.error(s"AvatarImplantMessage: ${player.Name} has an unknown implant in $slot")
}
}
@ -4813,7 +4853,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
if (llu.Target.GUID == captureTerminal.Owner.GUID) {
continent.LocalEvents ! LocalServiceMessage(continent.id, LocalAction.LluCaptured(llu))
} else {
log.info(s"LLU target is not this base. Target GUID: ${llu.Target.GUID} This base: ${captureTerminal.Owner.GUID}")
log.info(
s"LLU target is not this base. Target GUID: ${llu.Target.GUID} This base: ${captureTerminal.Owner.GUID}"
)
}
case _ => log.warn("Item in specialItemSlotGuid is not registered with continent or is not a LLU")
}
@ -4885,7 +4927,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
) {
FindLocalVehicle match {
case Some(vehicle) =>
log.info(s"${player.Name} is accessing a ${terminal.Definition.Name} for ${player.Sex.possessive} ${vehicle.Definition.Name}")
log.info(
s"${player.Name} is accessing a ${terminal.Definition.Name} for ${player.Sex.possessive} ${vehicle.Definition.Name}"
)
sendResponse(
UseItemMessage(
avatar_guid,
@ -5042,18 +5086,22 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
case None => ;
}
case Some(obj: CaptureFlag) =>
// LLU can normally only be picked up the faction that owns it
if (specialItemSlotGuid.isEmpty) {
if(obj.Faction == player.Faction) {
specialItemSlotGuid = Some(obj.GUID)
continent.LocalEvents ! CaptureFlagManager.PickupFlag(obj, player)
} else {
log.warn(s"Player ${player.toString} tried to pick up LLU ${obj.GUID} - ${obj.Faction} that doesn't belong to their faction")
case Some(obj: CaptureFlag) =>
// LLU can normally only be picked up the faction that owns it
if (specialItemSlotGuid.isEmpty) {
if (obj.Faction == player.Faction) {
specialItemSlotGuid = Some(obj.GUID)
continent.LocalEvents ! CaptureFlagManager.PickupFlag(obj, player)
} else {
log.warn(
s"Player ${player.toString} tried to pick up LLU ${obj.GUID} - ${obj.Faction} that doesn't belong to their faction"
)
}
} else if (specialItemSlotGuid.get != obj.GUID) { // Ignore duplicate pickup requests
log.warn(
s"Player ${player.toString} tried to pick up LLU ${obj.GUID} - ${obj.Faction} but their special slot already contains $specialItemSlotGuid"
)
}
} else if(specialItemSlotGuid.get != obj.GUID) { // Ignore duplicate pickup requests
log.warn(s"Player ${player.toString} tried to pick up LLU ${obj.GUID} - ${obj.Faction} but their special slot already contains $specialItemSlotGuid")
}
case Some(obj) =>
CancelZoningProcessWithDescriptiveReason("cancel_use")
@ -5214,7 +5262,8 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
player.Faction match {
case PlanetSideEmpire.NC =>
ToggleMaxSpecialState(enable = false)
case _ => log.warn(s"GenericActionMessage: ${player.Name} tried to cancel an uncancellable MAX special ability")
case _ =>
log.warn(s"GenericActionMessage: ${player.Name} tried to cancel an uncancellable MAX special ability")
}
} else {
log.warn(s"GenericActionMessage: ${player.Name} can't handle action code 21")
@ -5267,9 +5316,10 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
CancelZoningProcessWithDescriptiveReason("cancel_use")
log.info(s"${player.Name} wishes to load a saved favorite loadout")
action match {
case FavoritesAction.Save => avatarActor ! AvatarActor.SaveLoadout(player, loadoutType, label, line)
case FavoritesAction.Delete => avatarActor ! AvatarActor.DeleteLoadout(player, loadoutType, line)
case FavoritesAction.Unknown => log.warn(s"FavoritesRequest: ${player.Name} requested an unknown favorites action")
case FavoritesAction.Save => avatarActor ! AvatarActor.SaveLoadout(player, loadoutType, label, line)
case FavoritesAction.Delete => avatarActor ! AvatarActor.DeleteLoadout(player, loadoutType, line)
case FavoritesAction.Unknown =>
log.warn(s"FavoritesRequest: ${player.Name} requested an unknown favorites action")
}
case msg @ WeaponDelayFireMessage(seq_time, weapon_guid) => ;
@ -5282,7 +5332,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
AvatarAction.WeaponDryFire(player.GUID, weapon_guid)
)
case _ =>
log.warn(s"WeaponDryFire: ${player.Name}'s weapon ${weapon_guid.guid} is either not a weapon or does not exist")
log.warn(
s"WeaponDryFire: ${player.Name}'s weapon ${weapon_guid.guid} is either not a weapon or does not exist"
)
}
case msg @ WeaponFireMessage(
@ -5564,9 +5616,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
//todo: kick cargo passengers out. To be added after PR #216 is merged
obj match {
case v: Vehicle
if bailType == BailType.Bailed &&
v.SeatPermissionGroup(seat_num).contains(AccessPermissionGroup.Driver) &&
v.isFlying =>
if bailType == BailType.Bailed &&
v.SeatPermissionGroup(seat_num).contains(AccessPermissionGroup.Driver) &&
v.isFlying =>
v.Actor ! Vehicle.Deconstruct(None) //immediate deconstruction
case _ => ;
}
@ -5667,7 +5719,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
log.debug(s"$msg")
case msg @ BindPlayerMessage(action, bindDesc, unk1, logging, unk2, unk3, unk4, pos) =>
//log.info("BindPlayerMessage: " + msg)
//log.info("BindPlayerMessage: " + msg)
case msg @ PlanetsideAttributeMessage(object_guid, attribute_type, attribute_value) =>
ValidObject(object_guid) match {
@ -5684,41 +5736,45 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
)
//kick players who should not be seated in the vehicle due to permission changes
if (allow == VehicleLockState.Locked) { //TODO only important permission atm
vehicle.Seats.foreach { case (seatIndex, seat) =>
seat.occupant match {
case Some(tplayer : Player) =>
if (
vehicle.SeatPermissionGroup(seatIndex).contains(group) && tplayer != player
) { //can not kick self
seat.unmount(tplayer)
tplayer.VehicleSeated = None
continent.VehicleEvents ! VehicleServiceMessage(
continent.id,
VehicleAction.KickPassenger(tplayer.GUID, 4, false, object_guid)
)
}
case _ => ; // No player seated
}
vehicle.Seats.foreach {
case (seatIndex, seat) =>
seat.occupant match {
case Some(tplayer: Player) =>
if (vehicle.SeatPermissionGroup(seatIndex).contains(group) && tplayer != player) { //can not kick self
seat.unmount(tplayer)
tplayer.VehicleSeated = None
continent.VehicleEvents ! VehicleServiceMessage(
continent.id,
VehicleAction.KickPassenger(tplayer.GUID, 4, false, object_guid)
)
}
case _ => ; // No player seated
}
}
vehicle.CargoHolds.foreach { case (cargoIndex, hold) =>
hold.occupant match {
case Some(cargo) =>
if (vehicle.SeatPermissionGroup(cargoIndex).contains(group)) {
//todo: this probably doesn't work for passengers within the cargo vehicle
// Instruct client to start bail dismount procedure
self ! DismountVehicleCargoMsg(player.GUID, cargo.GUID, true, false, false)
}
case None => ; // No vehicle in cargo
}
vehicle.CargoHolds.foreach {
case (cargoIndex, hold) =>
hold.occupant match {
case Some(cargo) =>
if (vehicle.SeatPermissionGroup(cargoIndex).contains(group)) {
//todo: this probably doesn't work for passengers within the cargo vehicle
// Instruct client to start bail dismount procedure
self ! DismountVehicleCargoMsg(player.GUID, cargo.GUID, true, false, false)
}
case None => ; // No vehicle in cargo
}
}
}
case None => ;
}
} else {
log.warn(s"PlanetsideAttribute: vehicle attributes - unsupported change on vehicle $object_guid - $attribute_type, ${player.Name}")
log.warn(
s"PlanetsideAttribute: vehicle attributes - unsupported change on vehicle $object_guid - $attribute_type, ${player.Name}"
)
}
} else {
log.warn(s"PlanetsideAttribute: vehicle attributes - ${player.Name} does not own vehicle ${vehicle.GUID} and can not change it")
log.warn(
s"PlanetsideAttribute: vehicle attributes - ${player.Name} does not own vehicle ${vehicle.GUID} and can not change it"
)
}
// Cosmetics options
@ -5771,7 +5827,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
Some(TargetInfo(player.GUID, health, armor))
case _ =>
log.warn(s"TargetingImplantRequest: the info that ${player.Name} requested for target ${x.target_guid} is not for a player")
log.warn(
s"TargetingImplantRequest: the info that ${player.Name} requested for target ${x.target_guid} is not for a player"
)
None
}
})
@ -6507,16 +6565,16 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
//handle inventory contents
box.Capacity = if (sumReloadValue <= fullMagazine) {
sumReloadValue
} else {
val splitReloadAmmo: Int = sumReloadValue - fullMagazine
log.trace(
s"PerformToolAmmoChange: ${player.Name} takes ${originalBoxCapacity - splitReloadAmmo} from a box of $originalBoxCapacity $requestedAmmoType ammo"
)
val boxForInventory = AmmoBox(box.Definition, splitReloadAmmo)
continent.tasks ! stowNewFunc(boxForInventory)
fullMagazine
}
sumReloadValue
} else {
val splitReloadAmmo: Int = sumReloadValue - fullMagazine
log.trace(
s"PerformToolAmmoChange: ${player.Name} takes ${originalBoxCapacity - splitReloadAmmo} from a box of $originalBoxCapacity $requestedAmmoType ammo"
)
val boxForInventory = AmmoBox(box.Definition, splitReloadAmmo)
continent.tasks ! stowNewFunc(boxForInventory)
fullMagazine
}
sendResponse(
InventoryStateMessage(box.GUID, tool.GUID, box.Capacity)
) //should work for both players and vehicles
@ -6630,12 +6688,12 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
else { xs.map(_.obj.asInstanceOf[Tool].Magazine).sum }
val sumReloadValue: Int = box.Magazine + tailReloadValue
val actualReloadValue = if (sumReloadValue <= 3) {
RemoveOldEquipmentFromInventory(player)(x.obj)
sumReloadValue
} else {
ModifyAmmunition(player)(box.AmmoSlot.Box, 3 - tailReloadValue)
3
}
RemoveOldEquipmentFromInventory(player)(x.obj)
sumReloadValue
} else {
ModifyAmmunition(player)(box.AmmoSlot.Box, 3 - tailReloadValue)
3
}
log.info(s"${player.Name} found $actualReloadValue more $ammoType grenades to throw")
ModifyAmmunition(player)(
tool.AmmoSlot.Box,
@ -6852,27 +6910,33 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
case obj: Hackable if obj.HackedBy.nonEmpty =>
//sync hack state
amenity.Definition match {
case GlobalDefinitions.capture_terminal =>
SendPlanetsideAttributeMessage(
amenity.GUID,
PlanetsideAttributeEnum.ControlConsoleHackUpdate,
HackCaptureActor.GetHackUpdateAttributeValue(amenity.asInstanceOf[CaptureTerminal], isResecured = false))
case _ =>
HackObject(amenity.GUID, 1114636288L, 8L) //generic hackable object
}
case GlobalDefinitions.capture_terminal =>
SendPlanetsideAttributeMessage(
amenity.GUID,
PlanetsideAttributeEnum.ControlConsoleHackUpdate,
HackCaptureActor.GetHackUpdateAttributeValue(amenity.asInstanceOf[CaptureTerminal], isResecured = false)
)
case _ =>
HackObject(amenity.GUID, 1114636288L, 8L) //generic hackable object
}
// sync capture flags
case llu: CaptureFlag =>
// Create LLU
sendResponse(ObjectCreateMessage(
llu.Definition.ObjectId,
llu.GUID,
llu.Definition.Packet.ConstructorData(llu).get
))
sendResponse(
ObjectCreateMessage(
llu.Definition.ObjectId,
llu.GUID,
llu.Definition.Packet.ConstructorData(llu).get
)
)
// Attach it to a player if it has a carrier
if (llu.Carrier.nonEmpty) {
continent.LocalEvents ! LocalServiceMessage(continent.id, LocalAction.SendPacket(ObjectAttachMessage(llu.Carrier.get.GUID, llu.GUID, 252)))
continent.LocalEvents ! LocalServiceMessage(
continent.id,
LocalAction.SendPacket(ObjectAttachMessage(llu.Carrier.get.GUID, llu.GUID, 252))
)
}
case _ => ;
}
@ -6919,7 +6983,11 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
* @param attribute_number The attribute number
* @param attribute_value The attribute value
*/
def SendPlanetsideAttributeMessage(target_guid: PlanetSideGUID, attribute_number: PlanetsideAttributeEnum, attribute_value: Long): Unit = {
def SendPlanetsideAttributeMessage(
target_guid: PlanetSideGUID,
attribute_number: PlanetsideAttributeEnum,
attribute_value: Long
): Unit = {
sendResponse(PlanetsideAttributeMessage(target_guid, attribute_number, attribute_value))
}
@ -7099,8 +7167,6 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
log.trace(s"AvatarCreate: ${player.Name} - $guid -> $data")
}
continent.Population ! Zone.Population.Spawn(avatar, player, avatarActor)
//cautious redundancy
deadState = DeadState.Alive
avatarActor ! AvatarActor.RefreshPurchaseTimes()
//begin looking for conditions to set the avatar
context.system.scheduler.scheduleOnce(delay = 250 millisecond, self, SetCurrentAvatar(player, 200))
@ -7287,8 +7353,6 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
sendResponse(ObjectCreateDetailedMessage(ObjectClass.avatar, guid, data))
log.debug(s"AvatarRejoin: ${player.Name} - $guid -> $data")
}
//cautious redundancy
deadState = DeadState.Alive
avatarActor ! AvatarActor.RefreshPurchaseTimes()
setupAvatarFunc = AvatarCreate
//begin looking for conditions to set the avatar
@ -7459,7 +7523,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
sendResponse(DisconnectMessage("RequestSanctuaryZoneSpawn: player is already in sanctuary."))
} else {
continent.GUID(player.VehicleSeated) match {
case Some(obj : Vehicle) if !obj.Destroyed =>
case Some(obj: Vehicle) if !obj.Destroyed =>
cluster ! ICS.GetRandomSpawnPoint(
Zones.sanctuaryZoneNumber(player.Faction),
player.Faction,
@ -7525,7 +7589,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
case _: Vehicle =>
terminal.Actor ! CommonMessages.Use(player, Some((target, continent.VehicleEvents)))
case _ =>
log.error(s"StartUsingProximityUnit: ${player.Name}, this ${terminal.Definition.Name} can not deal with target $target")
log.error(
s"StartUsingProximityUnit: ${player.Name}, this ${terminal.Definition.Name} can not deal with target $target"
)
}
terminal.Definition match {
case GlobalDefinitions.adv_med_terminal | GlobalDefinitions.medical_terminal =>
@ -7558,7 +7624,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
*/
def StopUsingProximityUnit(terminal: Terminal with ProximityUnit): Unit = {
val term_guid = terminal.GUID
val targets = FindProximityUnitTargetsInScope(terminal)
val targets = FindProximityUnitTargetsInScope(terminal)
if (targets.nonEmpty) {
if (usingMedicalTerminal.contains(term_guid)) {
usingMedicalTerminal = None
@ -7799,10 +7865,10 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
* @return the projectile
*/
def ResolveProjectileInteraction(
projectile_guid: PlanetSideGUID,
resolution: DamageResolution.Value,
target: PlanetSideGameObject with FactionAffinity with Vitality,
pos: Vector3
projectile_guid: PlanetSideGUID,
resolution: DamageResolution.Value,
target: PlanetSideGameObject with FactionAffinity with Vitality,
pos: Vector3
): Option[DamageInteraction] = {
FindProjectileEntry(projectile_guid) match {
case Some(projectile) =>
@ -7821,11 +7887,11 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
* @return a copy of the projectile
*/
def ResolveProjectileInteraction(
projectile: Projectile,
index: Int,
resolution: DamageResolution.Value,
target: PlanetSideGameObject with FactionAffinity with Vitality,
pos: Vector3
projectile: Projectile,
index: Int,
resolution: DamageResolution.Value,
target: PlanetSideGameObject with FactionAffinity with Vitality,
pos: Vector3
): Option[DamageInteraction] = {
if (!projectiles(index).contains(projectile)) {
log.error(s"expected projectile could not be found at $index; can not resolve")
@ -7842,10 +7908,10 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
* @return a copy of the projectile
*/
def ResolveProjectileInteraction(
projectile: Projectile,
resolution: DamageResolution.Value,
target: PlanetSideGameObject with FactionAffinity with Vitality,
pos: Vector3
projectile: Projectile,
resolution: DamageResolution.Value,
target: PlanetSideGameObject with FactionAffinity with Vitality,
pos: Vector3
): Option[DamageInteraction] = {
if (projectile.isMiss) {
log.warn("expected projectile was already counted as a missed shot; can not resolve any further")
@ -8245,7 +8311,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
} else {
player.Find(tool) match {
case Some(newIndex) =>
log.warn(s"$logDecorator: ${player.Name} was looking for an item in his hand $index, but item was found at $newIndex instead")
log.warn(
s"$logDecorator: ${player.Name} was looking for an item in his hand $index, but item was found at $newIndex instead"
)
player.Slot(newIndex).Equipment = None
true
case None =>
@ -8430,16 +8498,17 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
val events = continent.AvatarEvents
val zoneId = continent.id
(player.Inventory.Items ++ player.HolsterItems())
.collect { case InventoryItem(obj: BoomerTrigger, index) =>
player.Slot(index).Equipment = None
sendResponse(ObjectDeleteMessage(obj.GUID, 0))
if (player.HasGUID && player.VisibleSlots.contains(index)) {
events ! AvatarServiceMessage(
zoneId,
AvatarAction.ObjectDelete(player.GUID, obj.GUID)
)
}
obj
.collect {
case InventoryItem(obj: BoomerTrigger, index) =>
player.Slot(index).Equipment = None
sendResponse(ObjectDeleteMessage(obj.GUID, 0))
if (player.HasGUID && player.VisibleSlots.contains(index)) {
events ! AvatarServiceMessage(
zoneId,
AvatarAction.ObjectDelete(player.GUID, obj.GUID)
)
}
obj
}
}
@ -8623,7 +8692,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
val msg: String = s"${player.Name} is driving a ${vehicle.Definition.Name}"
log.info(msg)
log.debug(s"LoadZoneInVehicleAsDriver: $msg")
val manifest = vehicle.PrepareGatingManifest()
val manifest = vehicle.PrepareGatingManifest()
val pguid = player.GUID
val toChannel = manifest.file
val topLevel = interstellarFerryTopLevelGUID.getOrElse(vehicle.GUID)
@ -8634,7 +8703,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
manifest.cargo.foreach {
case ManifestPassengerEntry("MISSING_DRIVER", index) =>
val cargo = vehicle.CargoHolds(index).occupant.get
log.warn(s"LoadZoneInVehicleAsDriver: ${player.Name} must eject cargo in hold $index; vehicle is missing driver")
log.warn(
s"LoadZoneInVehicleAsDriver: ${player.Name} must eject cargo in hold $index; vehicle is missing driver"
)
CargoBehavior.HandleVehicleCargoDismount(cargo.GUID, cargo, vehicle.GUID, vehicle, false, false, true)
case entry =>
val cargo = vehicle.CargoHolds(entry.mount).occupant.get
@ -8755,14 +8826,16 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
case hold if hold.isOccupied =>
val cargo = hold.occupant.get
cargo.Continent = toZoneId
//point to the cargo vehicle to instigate cargo vehicle driver transportation
//point to the cargo vehicle to instigate cargo vehicle driver transportation
// galaxyService ! GalaxyServiceMessage(
// toChannel,
// GalaxyAction.TransferPassenger(player_guid, toChannel, vehicle, topLevel, manifest)
// )
}
case None =>
log.error(s"LoadZoneTransferPassengerMessages: ${player.Name} expected a manifest for zone transfer; got nothing")
log.error(
s"LoadZoneTransferPassengerMessages: ${player.Name} expected a manifest for zone transfer; got nothing"
)
}
}
@ -9519,18 +9592,18 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
s"WeaponFireMessage: ${player.Name}'s ${projectile_info.Name} is a remote projectile"
)
continent.tasks ! (if (projectile.HasGUID) {
continent.AvatarEvents ! AvatarServiceMessage(
continent.id,
AvatarAction.ProjectileExplodes(
player.GUID,
projectile.GUID,
projectile
)
)
ReregisterProjectile(projectile)
} else {
RegisterProjectile(projectile)
})
continent.AvatarEvents ! AvatarServiceMessage(
continent.id,
AvatarAction.ProjectileExplodes(
player.GUID,
projectile.GUID,
projectile
)
)
ReregisterProjectile(projectile)
} else {
RegisterProjectile(projectile)
})
}
projectilesToCleanUp(projectileIndex) = false

File diff suppressed because it is too large Load diff

View file

@ -12,23 +12,44 @@ object ControlPacketOpcode extends Enumeration {
type Type = Value
val
// OPCODES 0x00-0f
HandleGamePacket, // a whoopsi case: not actually a control packet, but a game packet
ClientStart, // first packet ever sent during client connection
ServerStart, // second packet sent in response to ClientStart
MultiPacket, // used to send multiple packets with one UDP message (subpackets limited to <= 255)
Unknown4, TeardownConnection, Unknown6, ControlSync, // sent to the server from the client
HandleGamePacket, // a whoopsi case: not actually a control packet, but a game packet
ClientStart, // first packet ever sent during client connection
ServerStart, // second packet sent in response to ClientStart
MultiPacket, // used to send multiple packets with one UDP message (subpackets limited to <= 255)
Unknown4, //
TeardownConnection, //
Unknown6, //
ControlSync, // sent to the server from the client
// 0x08
ControlSyncResp, // the response generated by the server
SlottedMetaPacket0, SlottedMetaPacket1, SlottedMetaPacket2, SlottedMetaPacket3, SlottedMetaPacket4,
SlottedMetaPacket5, SlottedMetaPacket6,
ControlSyncResp, // the response generated by the server
SlottedMetaPacket0, //
SlottedMetaPacket1, //
SlottedMetaPacket2, //
SlottedMetaPacket3, //
SlottedMetaPacket4, //
SlottedMetaPacket5, //
SlottedMetaPacket6, //
// OPCODES 0x10-1f
SlottedMetaPacket7, RelatedA0, RelatedA1, RelatedA2, RelatedA3, RelatedB0, RelatedB1, RelatedB2,
SlottedMetaPacket7, //
RelatedA0, //
RelatedA1, //
RelatedA2, //
RelatedA3, //
RelatedB0, //
RelatedB1, //
RelatedB2, //
// 0x18
RelatedB3, MultiPacketEx, // same as MultiPacket, but with the ability to send extended length packets
Unknown26, Unknown27, Unknown28, ConnectionClose, Unknown30 = Value
RelatedB3, //
MultiPacketEx, // same as MultiPacket, but with the ability to send extended length packets
Unknown26, //
Unknown27, //
Unknown28, //
ConnectionClose, //
Unknown30 // Probably a more lightweight variant of ClientStart, containing only the client nonce
= Value
private def noDecoder(opcode: ControlPacketOpcode.Type) =
(_: BitVector) => Attempt.failure(Err(s"Could not find a marshaller for control packet $opcode"))
(bits: BitVector) => Attempt.failure(Err(s"Could not find a marshaller for control packet $opcode (${bits.toHex})"))
def getPacketDecoder(
opcode: ControlPacketOpcode.Type
@ -69,7 +90,7 @@ object ControlPacketOpcode extends Enumeration {
case 0x1b => noDecoder(Unknown27)
case 0x1c => noDecoder(Unknown28)
case 0x1d => control.ConnectionClose.decode
case 0x1e => noDecoder(Unknown30)
case 0x1e => control.Unknown30.decode
case _ => noDecoder(opcode)
}

View file

@ -18,284 +18,284 @@ object GamePacketOpcode extends Enumeration {
type Type = Value
val
// OPCODES 0x00-0f
Unknown0, // PPT_NULL in beta client
LoginMessage,
LoginRespMessage,
Unknown0, // PPT_NULL in beta client
LoginMessage, //
LoginRespMessage, //
ConnectToWorldRequestMessage, // found by searching for 83 F8 03 89 in IDA
ConnectToWorldMessage,
VNLWorldStatusMessage,
UnknownMessage6, // PPT_TRANSFERTOWORLDREQUEST
UnknownMessage7, // PPT_TRANSFERTOWORLDRESPONSE
ConnectToWorldMessage, //
VNLWorldStatusMessage, //
UnknownMessage6, // PPT_TRANSFERTOWORLDREQUEST
UnknownMessage7, // PPT_TRANSFERTOWORLDRESPONSE
// 0x08
PlayerStateMessage,
HitMessage,
HitHint,
DamageMessage,
DestroyMessage,
ReloadMessage,
MountVehicleMsg,
DismountVehicleMsg,
PlayerStateMessage, //
HitMessage, //
HitHint, //
DamageMessage, //
DestroyMessage, //
ReloadMessage, //
MountVehicleMsg, //
DismountVehicleMsg, //
// OPCODES 0x10-1f
UseItemMessage,
MoveItemMessage,
ChatMsg,
CharacterNoRecordMessage,
CharacterInfoMessage,
UnknownMessage21, // PPT_DISCONNECT
BindPlayerMessage,
UseItemMessage, //
MoveItemMessage, //
ChatMsg, //
CharacterNoRecordMessage, //
CharacterInfoMessage, //
UnknownMessage21, // PPT_DISCONNECT
BindPlayerMessage, //
ObjectCreateMessage_Duplicate, // PPT_OBJECTCREATE
// 0x18
ObjectCreateMessage, // PPT_OBJECTCREATEDETAILED
ObjectDeleteMessage,
PingMsg,
VehicleStateMessage,
FrameVehicleStateMessage,
GenericObjectStateMsg,
ChildObjectStateMessage,
ActionResultMessage,
ObjectCreateMessage, // PPT_OBJECTCREATEDETAILED
ObjectDeleteMessage, //
PingMsg, //
VehicleStateMessage, //
FrameVehicleStateMessage, //
GenericObjectStateMsg, //
ChildObjectStateMessage, //
ActionResultMessage, //
// OPCODES 0x20-2f
UnknownMessage32, // PPT_ACTIONBEGIN
ActionProgressMessage,
ActionCancelMessage,
ActionCancelAcknowledgeMessage,
SetEmpireMessage,
EmoteMsg,
UnuseItemMessage,
ObjectDetachMessage,
UnknownMessage32, // PPT_ACTIONBEGIN
ActionProgressMessage, //
ActionCancelMessage, //
ActionCancelAcknowledgeMessage, //
SetEmpireMessage, //
EmoteMsg, //
UnuseItemMessage, //
ObjectDetachMessage, //
// 0x28
CreateShortcutMessage,
ChangeShortcutBankMessage,
ObjectAttachMessage,
UnknownMessage43, // PPT_OBJECTEMPTY
PlanetsideAttributeMessage,
RequestDestroyMessage,
UnknownMessage46, // PPT_EQUIPITEM
CharacterCreateRequestMessage,
CreateShortcutMessage, //
ChangeShortcutBankMessage, //
ObjectAttachMessage, //
UnknownMessage43, // PPT_OBJECTEMPTY
PlanetsideAttributeMessage, //
RequestDestroyMessage, //
UnknownMessage46, // PPT_EQUIPITEM
CharacterCreateRequestMessage, //
// OPCODES 0x30-3f
CharacterRequestMessage,
LoadMapMessage,
SetCurrentAvatarMessage,
ObjectHeldMessage,
WeaponFireMessage,
AvatarJumpMessage,
PickupItemMessage,
DropItemMessage,
CharacterRequestMessage, //
LoadMapMessage, //
SetCurrentAvatarMessage, //
ObjectHeldMessage, //
WeaponFireMessage, //
AvatarJumpMessage, //
PickupItemMessage, //
DropItemMessage, //
// 0x38
InventoryStateMessage,
ChangeFireStateMessage_Start,
ChangeFireStateMessage_Stop,
UnknownMessage59,
GenericCollisionMsg,
QuantityUpdateMessage,
ArmorChangedMessage,
ProjectileStateMessage,
InventoryStateMessage, //
ChangeFireStateMessage_Start, //
ChangeFireStateMessage_Stop, //
UnknownMessage59, //
GenericCollisionMsg, //
QuantityUpdateMessage, //
ArmorChangedMessage, //
ProjectileStateMessage, //
// OPCODES 0x40-4f
MountVehicleCargoMsg,
DismountVehicleCargoMsg,
CargoMountPointStatusMessage,
BeginZoningMessage,
ItemTransactionMessage,
ItemTransactionResultMessage,
ChangeFireModeMessage,
ChangeAmmoMessage,
MountVehicleCargoMsg, //
DismountVehicleCargoMsg, //
CargoMountPointStatusMessage, //
BeginZoningMessage, //
ItemTransactionMessage, //
ItemTransactionResultMessage, //
ChangeFireModeMessage, //
ChangeAmmoMessage, //
// 0x48
TimeOfDayMessage,
UnknownMessage73, // PPT_PROJECTILE_EVENT_BLOCK
SpawnRequestMessage,
DeployRequestMessage,
UnknownMessage76, // PPT_BUILDINGSTATECHANGED
RepairMessage,
ServerVehicleOverrideMsg,
TimeOfDayMessage, //
UnknownMessage73, // PPT_PROJECTILE_EVENT_BLOCK
SpawnRequestMessage, //
DeployRequestMessage, //
UnknownMessage76, // PPT_BUILDINGSTATECHANGED
RepairMessage, //
ServerVehicleOverrideMsg, //
LashMessage,
// OPCODES 0x50-5f
TargetingInfoMessage,
TriggerEffectMessage,
WeaponDryFireMessage,
DroppodLaunchRequestMessage,
HackMessage,
DroppodLaunchResponseMessage,
GenericObjectActionMessage,
AvatarVehicleTimerMessage,
TargetingInfoMessage, //
TriggerEffectMessage, //
WeaponDryFireMessage, //
DroppodLaunchRequestMessage, //
HackMessage, //
DroppodLaunchResponseMessage, //
GenericObjectActionMessage, //
AvatarVehicleTimerMessage, //
// 0x58
AvatarImplantMessage,
UnknownMessage89, // PPT_SEARCHMESSAGE
DelayedPathMountMsg,
OrbitalShuttleTimeMsg,
AIDamage,
DeployObjectMessage,
FavoritesRequest,
FavoritesResponse,
AvatarImplantMessage, //
UnknownMessage89, // PPT_SEARCHMESSAGE
DelayedPathMountMsg, //
OrbitalShuttleTimeMsg, //
AIDamage, //
DeployObjectMessage, //
FavoritesRequest, //
FavoritesResponse, //
// OPCODES 0x60-6f
FavoritesMessage,
ObjectDetectedMessage,
SplashHitMessage,
SetChatFilterMessage,
AvatarSearchCriteriaMessage,
AvatarSearchResponse,
WeaponJammedMessage,
LinkDeadAwarenessMsg,
FavoritesMessage, //
ObjectDetectedMessage, //
SplashHitMessage, //
SetChatFilterMessage, //
AvatarSearchCriteriaMessage, //
AvatarSearchResponse, //
WeaponJammedMessage, //
LinkDeadAwarenessMsg, //
// 0x68
DroppodFreefallingMessage,
AvatarFirstTimeEventMessage,
AggravatedDamageMessage,
TriggerSoundMessage,
LootItemMessage,
VehicleSubStateMessage,
SquadMembershipRequest,
SquadMembershipResponse,
DroppodFreefallingMessage, //
AvatarFirstTimeEventMessage, //
AggravatedDamageMessage, //
TriggerSoundMessage, //
LootItemMessage, //
VehicleSubStateMessage, //
SquadMembershipRequest, //
SquadMembershipResponse, //
// OPCODES 0x70-7f
SquadMemberEvent,
PlatoonEvent,
FriendsRequest,
FriendsResponse,
TriggerEnvironmentalDamageMessage,
TrainingZoneMessage,
DeployableObjectsInfoMessage,
SquadMemberEvent, //
PlatoonEvent, //
FriendsRequest, //
FriendsResponse, //
TriggerEnvironmentalDamageMessage, //
TrainingZoneMessage, //
DeployableObjectsInfoMessage, //
SquadState,
// 0x78
OxygenStateMessage,
TradeMessage,
UnknownMessage122,
DamageFeedbackMessage,
DismountBuildingMsg,
UnknownMessage125, // PPT_MOUNTBUILDING
UnknownMessage126, // PPT_INTENDEDDROPZONE
AvatarStatisticsMessage,
OxygenStateMessage, //
TradeMessage, //
UnknownMessage122, //
DamageFeedbackMessage, //
DismountBuildingMsg, //
UnknownMessage125, // PPT_MOUNTBUILDING
UnknownMessage126, // PPT_INTENDEDDROPZONE
AvatarStatisticsMessage, //
// OPCODES 0x80-8f
GenericObjectAction2Message,
DestroyDisplayMessage,
TriggerBotAction,
SquadWaypointRequest,
SquadWaypointEvent,
OffshoreVehicleMessage,
ObjectDeployedMessage,
ObjectDeployedCountMessage,
GenericObjectAction2Message, //
DestroyDisplayMessage, //
TriggerBotAction, //
SquadWaypointRequest, //
SquadWaypointEvent, //
OffshoreVehicleMessage, //
ObjectDeployedMessage, //
ObjectDeployedCountMessage, //
// 0x88
WeaponDelayFireMessage,
BugReportMessage,
PlayerStasisMessage,
UnknownMessage139,
OutfitMembershipRequest,
OutfitMembershipResponse,
OutfitRequest,
OutfitEvent,
WeaponDelayFireMessage, //
BugReportMessage, //
PlayerStasisMessage, //
UnknownMessage139, //
OutfitMembershipRequest, //
OutfitMembershipResponse, //
OutfitRequest, //
OutfitEvent, //
// OPCODES 0x90-9f
OutfitMemberEvent,
OutfitMemberUpdate,
PlanetsideStringAttributeMessage,
DataChallengeMessage,
DataChallengeMessageResp,
WeatherMessage,
SimDataChallenge,
SimDataChallengeResp,
OutfitMemberEvent, //
OutfitMemberUpdate, //
PlanetsideStringAttributeMessage, //
DataChallengeMessage, //
DataChallengeMessageResp, //
WeatherMessage, //
SimDataChallenge, //
SimDataChallengeResp, //
// 0x98
OutfitListEvent,
EmpireIncentivesMessage,
InvalidTerrainMessage,
SyncMessage,
DebugDrawMessage,
SoulMarkMessage,
UplinkPositionEvent,
HotSpotUpdateMessage,
OutfitListEvent, //
EmpireIncentivesMessage, //
InvalidTerrainMessage, //
SyncMessage, //
DebugDrawMessage, //
SoulMarkMessage, //
UplinkPositionEvent, //
HotSpotUpdateMessage, //
// OPCODES 0xa0-af
BuildingInfoUpdateMessage,
FireHintMessage,
UplinkRequest,
UplinkResponse,
WarpgateRequest,
WarpgateResponse,
DamageWithPositionMessage,
GenericActionMessage,
BuildingInfoUpdateMessage, //
FireHintMessage, //
UplinkRequest, //
UplinkResponse, //
WarpgateRequest, //
WarpgateResponse, //
DamageWithPositionMessage, //
GenericActionMessage, //
// 0xa8
ContinentalLockUpdateMessage,
AvatarGrenadeStateMessage,
UnknownMessage170,
UnknownMessage171,
ReleaseAvatarRequestMessage,
AvatarDeadStateMessage,
CSAssistMessage,
CSAssistCommentMessage,
ContinentalLockUpdateMessage, //
AvatarGrenadeStateMessage, //
UnknownMessage170, //
UnknownMessage171, //
ReleaseAvatarRequestMessage, //
AvatarDeadStateMessage, //
CSAssistMessage, //
CSAssistCommentMessage, //
// OPCODES 0xb0-bf
VoiceHostRequest,
VoiceHostKill,
VoiceHostInfo,
BattleplanMessage,
BattleExperienceMessage,
TargetingImplantRequest,
ZonePopulationUpdateMessage,
DisconnectMessage,
VoiceHostRequest, //
VoiceHostKill, //
VoiceHostInfo, //
BattleplanMessage, //
BattleExperienceMessage, //
TargetingImplantRequest, //
ZonePopulationUpdateMessage, //
DisconnectMessage, //
// 0xb8
ExperienceAddedMessage,
OrbitalStrikeWaypointMessage,
KeepAliveMessage,
MapObjectStateBlockMessage,
SnoopMsg,
PlayerStateMessageUpstream,
PlayerStateShiftMessage,
ZipLineMessage,
ExperienceAddedMessage, //
OrbitalStrikeWaypointMessage, //
KeepAliveMessage, //
MapObjectStateBlockMessage, //
SnoopMsg, //
PlayerStateMessageUpstream, //
PlayerStateShiftMessage, //
ZipLineMessage, //
// OPCODES 0xc0-cf
CaptureFlagUpdateMessage,
VanuModuleUpdateMessage,
FacilityBenefitShieldChargeRequestMessage,
ProximityTerminalUseMessage,
QuantityDeltaUpdateMessage,
ChainLashMessage,
ZoneInfoMessage,
LongRangeProjectileInfoMessage,
CaptureFlagUpdateMessage, //
VanuModuleUpdateMessage, //
FacilityBenefitShieldChargeRequestMessage, //
ProximityTerminalUseMessage, //
QuantityDeltaUpdateMessage, //
ChainLashMessage, //
ZoneInfoMessage, //
LongRangeProjectileInfoMessage, //
// 0xc8
WeaponLazeTargetPositionMessage,
ModuleLimitsMessage,
OutfitBenefitMessage,
EmpireChangeTimeMessage,
ClockCalibrationMessage,
DensityLevelUpdateMessage,
ActOfGodMessage,
AvatarAwardMessage,
WeaponLazeTargetPositionMessage, //
ModuleLimitsMessage, //
OutfitBenefitMessage, //
EmpireChangeTimeMessage, //
ClockCalibrationMessage, //
DensityLevelUpdateMessage, //
ActOfGodMessage, //
AvatarAwardMessage, //
// OPCODES 0xd0-df
UnknownMessage208,
DisplayedAwardMessage,
RespawnAMSInfoMessage,
ComponentDamageMessage,
GenericObjectActionAtPositionMessage,
PropertyOverrideMessage,
WarpgateLinkOverrideMessage,
EmpireBenefitsMessage,
UnknownMessage208, //
DisplayedAwardMessage, //
RespawnAMSInfoMessage, //
ComponentDamageMessage, //
GenericObjectActionAtPositionMessage, //
PropertyOverrideMessage, //
WarpgateLinkOverrideMessage, //
EmpireBenefitsMessage, //
// 0xd8
ForceEmpireMessage,
BroadcastWarpgateUpdateMessage,
UnknownMessage218,
SquadMainTerminalMessage,
SquadMainTerminalResponseMessage,
SquadOrderMessage,
SquadOrderResponse,
ZoneLockInfoMessage,
ForceEmpireMessage, //
BroadcastWarpgateUpdateMessage, //
UnknownMessage218, //
SquadMainTerminalMessage, //
SquadMainTerminalResponseMessage, //
SquadOrderMessage, //
SquadOrderResponse, //
ZoneLockInfoMessage, //
// OPCODES 0xe0-ef
SquadBindInfoMessage,
AudioSequenceMessage,
SquadFacilityBindInfoMessage,
ZoneForcedCavernConnectionsMessage,
MissionActionMessage,
MissionKillTriggerMessage,
ReplicationStreamMessage,
SquadDefinitionActionMessage,
SquadBindInfoMessage, //
AudioSequenceMessage, //
SquadFacilityBindInfoMessage, //
ZoneForcedCavernConnectionsMessage, //
MissionActionMessage, //
MissionKillTriggerMessage, //
ReplicationStreamMessage, //
SquadDefinitionActionMessage, //
// 0xe8
SquadDetailDefinitionUpdateMessage,
TacticsMessage,
RabbitUpdateMessage,
SquadInvitationRequestMessage,
CharacterKnowledgeMessage,
GameScoreUpdateMessage,
UnknownMessage238,
OrderTerminalBugMessage,
SquadDetailDefinitionUpdateMessage, //
TacticsMessage, //
RabbitUpdateMessage, //
SquadInvitationRequestMessage, //
CharacterKnowledgeMessage, //
GameScoreUpdateMessage, //
UnknownMessage238, //
OrderTerminalBugMessage, //
// OPCODES 0xf0-f3
QueueTimedHelpMessage,
MailMessage,
GameVarUpdate,
ClientCheatedMessage // last known message type (243, 0xf3)
QueueTimedHelpMessage, //
MailMessage, //
GameVarUpdate, //
ClientCheatedMessage // last known message type (243, 0xf3)
= Value
private def noDecoder(opcode: GamePacketOpcode.Type) =
(_: BitVector) => Attempt.failure(Err(s"Could not find a marshaller for game packet $opcode"))
(bits: BitVector) => Attempt.failure(Err(s"Could not find a marshaller for game packet $opcode (${bits.toHex}"))
/// Mapping of packet IDs to decoders. Notice that we are using the @switch annotation which ensures that the Scala
/// compiler will be able to optimize this as a lookup table (switch statement). Microbenchmarks show a nearly 400x

View file

@ -63,7 +63,7 @@ object PacketType extends Enumeration(1) {
}
/** PlanetSide packet flags (beginning of most packets) */
final case class PlanetSidePacketFlags(packetType: PacketType.Value, secured: Boolean)
final case class PlanetSidePacketFlags(packetType: PacketType.Value, secured: Boolean, advanced: Boolean = true)
/** Codec for [[PlanetSidePacketFlags]] */
object PlanetSidePacketFlags extends Marshallable[PlanetSidePacketFlags] {
@ -71,7 +71,8 @@ object PlanetSidePacketFlags extends Marshallable[PlanetSidePacketFlags] {
("packet_type" | PacketType.codec) :: // first 4-bits
("unused" | constant(bin"0")) ::
("secured" | bool) ::
("advanced" | constant(bin"1")) :: // we only support "advanced packets"
//("advanced" | constant(bin"1")) :: // we only support "advanced packets"
("advanced" | bool) ::
("length_specified" | constant(bin"0")) // we DO NOT support this field
).as[PlanetSidePacketFlags]
}

View file

@ -42,7 +42,7 @@ object PacketCoding {
case Some(_sequence) =>
uint16L.encode(_sequence) match {
case Successful(_seq) => _seq
case f @ Failure(_) => return f
case f @ Failure(_) => return f
}
case None =>
return Failure(Err(s"Missing sequence"))
@ -125,7 +125,7 @@ object PacketCoding {
case Successful(opcode) => Successful(opcode)
case f @ Failure(_) => f
}
case _ =>
Failure(Err("packet not supported"))
}
@ -174,7 +174,7 @@ object PacketCoding {
): Attempt[(PlanetSidePacket, Int)] = {
val (flags, remainder) = Codec.decode[PlanetSidePacketFlags](BitVector(msg)) match {
case Successful(DecodeResult(value, _remainder)) => (value, _remainder)
case Failure(e) => return Failure(Err(s"Failed to parse packet flags: ${e.message}"))
case Failure(e) => return Failure(Err(s"Failed to parse packet flags: ${e.message}"))
}
flags.packetType match {
@ -218,8 +218,8 @@ object PacketCoding {
case (PacketType.ResetSequence, Some(_crypto)) =>
_crypto.decrypt(payload.drop(1)) match {
case Successful(p) if p == hex"01" => Successful((ResetSequence(), sequence))
case Successful(p) => Failure(Err(s"ResetSequence decrypted to unsupported value - $p"))
case _ => Failure(Err(s"ResetSequence did not decrypt properly"))
case Successful(p) => Failure(Err(s"ResetSequence decrypted to unsupported value - $p"))
case _ => Failure(Err(s"ResetSequence did not decrypt properly"))
}
case (ptype, _) =>
Failure(Err(s"Cannot unmarshal $ptype packet at all"))
@ -238,8 +238,7 @@ object PacketCoding {
def decodePacket(msg: ByteVector): Attempt[PlanetSidePacket] = {
if (msg.length < PLANETSIDE_MIN_PACKET_SIZE)
return Failure(Err(s"Packet does not meet the minimum length of $PLANETSIDE_MIN_PACKET_SIZE bytes"))
val firstByte = msg { 0 }
firstByte match {
msg(0) match {
case 0x00 =>
// control packets don't need the first byte
ControlPacketOpcode.codec.decode(msg.drop(1).bits) match {
@ -303,7 +302,7 @@ object PacketCoding {
}
} catch {
case e: Throwable =>
val msg = if(e.getMessage == null) e.getClass.getSimpleName else e.getMessage
val msg = if (e.getMessage == null) e.getClass.getSimpleName else e.getMessage
Failure(Err(s"encrypt error: '$msg' data: ${packetWithPadding.toHex}"))
}
}
@ -321,14 +320,14 @@ object PacketCoding {
// last byte is the padding length
val padding = uint8L.decode(payloadDecrypted.takeRight(1).bits) match {
case Successful(_padding) => _padding.value
case Failure(e) => return Failure(Err(s"Failed to decode the encrypted padding length: ${e.message}"))
case Failure(e) => return Failure(Err(s"Failed to decode the encrypted padding length: ${e.message}"))
}
val payloadNoPadding = payloadDecrypted.dropRight(1 + padding)
val payloadMac = payloadNoPadding.takeRight(Md5Mac.MACLENGTH)
val mac = bytes(Md5Mac.MACLENGTH).decode(payloadMac.bits) match {
case Failure(e) => return Failure(Err("Failed to extract the encrypted MAC: " + e.message))
case Failure(e) => return Failure(Err("Failed to extract the encrypted MAC: " + e.message))
case Successful(_mac) => _mac.value
}

View file

@ -0,0 +1,18 @@
package net.psforever.packet.control
import net.psforever.packet.{ControlPacketOpcode, Marshallable, PlanetSideControlPacket}
import scodec.Codec
import scodec.bits._
import scodec.codecs._
final case class Unknown30(clientNonce: Long) extends PlanetSideControlPacket {
type Packet = Unknown30
def opcode = ControlPacketOpcode.Unknown30
def encode = Unknown30.encode(this)
}
object Unknown30 extends Marshallable[Unknown30] {
implicit val codec: Codec[Unknown30] = (
("client_nonce" | uint32L)
).as[Unknown30]
}

View file

@ -63,8 +63,9 @@ class ChatService(context: ActorContext[ChatService.Command]) extends AbstractBe
case Message(session, message, channel) =>
(channel, message.messageType) match {
case (ChatChannel.Squad(_), CMT_SQUAD) => ;
case (ChatChannel.Default(), messageType) if messageType != CMT_SQUAD => ;
case (ChatChannel.Squad(_), CMT_SQUAD) => ()
case (ChatChannel.Squad(_), CMT_VOICE) if message.contents.startsWith("SH") => ()
case (ChatChannel.Default(), messageType) if messageType != CMT_SQUAD => ()
case _ =>
log.error(s"invalid chat channel $channel for messageType ${message.messageType}")
return this

View file

@ -18,11 +18,14 @@ import net.psforever.types.{PlanetSideGUID, Vector3}
import scala.concurrent.duration.{Duration, _}
class LocalService(zone: Zone) extends Actor {
private val doorCloser = context.actorOf(Props[DoorCloseActor](), s"${zone.id}-local-door-closer")
private val hackClearer = context.actorOf(Props[HackClearActor](), s"${zone.id}-local-hack-clearer")
private val hackCapturer = context.actorOf(Props(classOf[HackCaptureActor], zone.tasks), s"${zone.id}-local-hack-capturer")
private val captureFlagManager = context.actorOf(Props(classOf[CaptureFlagManager], zone.tasks, zone), s"${zone.id}-local-capture-flag-manager")
private val engineer = context.actorOf(Props(classOf[DeployableRemover], zone.tasks), s"${zone.id}-deployable-remover-agent")
private val doorCloser = context.actorOf(Props[DoorCloseActor](), s"${zone.id}-local-door-closer")
private val hackClearer = context.actorOf(Props[HackClearActor](), s"${zone.id}-local-hack-clearer")
private val hackCapturer =
context.actorOf(Props(classOf[HackCaptureActor], zone.tasks), s"${zone.id}-local-hack-capturer")
private val captureFlagManager =
context.actorOf(Props(classOf[CaptureFlagManager], zone.tasks, zone), s"${zone.id}-local-capture-flag-manager")
private val engineer =
context.actorOf(Props(classOf[DeployableRemover], zone.tasks), s"${zone.id}-deployable-remover-agent")
private val teleportDeployment: ActorRef =
context.actorOf(Props[RouterTelepadActivation](), s"${zone.id}-telepad-activate-agent")
private[this] val log = org.log4s.getLogger
@ -83,7 +86,11 @@ class LocalService(zone: Zone) extends Actor {
)
case LocalAction.HackClear(player_guid, target, unk1, unk2) =>
LocalEvents.publish(
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.SendHackMessageHackCleared(target.GUID, unk1, unk2))
LocalServiceResponse(
s"/$forChannel/Local",
player_guid,
LocalResponse.SendHackMessageHackCleared(target.GUID, unk1, unk2)
)
)
case LocalAction.HackTemporarily(player_guid, _, target, unk1, duration, unk2) =>
hackClearer ! HackClearActor.ObjectIsHacked(target, zone, unk1, unk2, duration)
@ -281,6 +288,13 @@ class LocalService(zone: Zone) extends Actor {
//response from HackClearActor
case HackClearActor.SendHackMessageHackCleared(target_guid, _, unk1, unk2) =>
log.info(s"Clearing hack for $target_guid")
LocalEvents.publish(
LocalServiceResponse(
s"/${zone.id}/Local",
Service.defaultPlayerGUID,
LocalResponse.SendHackMessageHackCleared(target_guid, unk1, unk2)
)
)
//message from ProximityTerminalControl
case Terminal.StartProximityEffect(terminal) =>
@ -418,13 +432,9 @@ class LocalService(zone: Zone) extends Actor {
sender() ! Vitality.DamageResolution(target, cause)
// Forward all CaptureFlagManager messages
case msg @
(CaptureFlagManager.SpawnCaptureFlag(_, _, _)
| CaptureFlagManager.PickupFlag(_, _)
| CaptureFlagManager.DropFlag(_)
| CaptureFlagManager.Captured(_)
| CaptureFlagManager.Lost(_, _)
| CaptureFlagManager) =>
case msg @ (CaptureFlagManager.SpawnCaptureFlag(_, _, _) | CaptureFlagManager.PickupFlag(_, _) |
CaptureFlagManager.DropFlag(_) | CaptureFlagManager.Captured(_) | CaptureFlagManager.Lost(_, _) |
CaptureFlagManager) =>
captureFlagManager.forward(msg)
case msg =>