mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-07-13 07:24:53 +00:00
Merge branch 'master' into cooldown-reset
This commit is contained in:
commit
f11bf70af7
13 changed files with 1125 additions and 948 deletions
24
build.sbt
24
build.sbt
|
|
@ -40,16 +40,16 @@ lazy val psforeverSettings = Seq(
|
||||||
classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.Flat,
|
classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.Flat,
|
||||||
resolvers += "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots",
|
resolvers += "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots",
|
||||||
libraryDependencies ++= Seq(
|
libraryDependencies ++= Seq(
|
||||||
"com.typesafe.akka" %% "akka-actor" % "2.6.13",
|
"com.typesafe.akka" %% "akka-actor" % "2.6.14",
|
||||||
"com.typesafe.akka" %% "akka-slf4j" % "2.6.13",
|
"com.typesafe.akka" %% "akka-slf4j" % "2.6.14",
|
||||||
"com.typesafe.akka" %% "akka-protobuf-v3" % "2.6.13",
|
"com.typesafe.akka" %% "akka-protobuf-v3" % "2.6.14",
|
||||||
"com.typesafe.akka" %% "akka-stream" % "2.6.13",
|
"com.typesafe.akka" %% "akka-stream" % "2.6.14",
|
||||||
"com.typesafe.akka" %% "akka-testkit" % "2.6.13" % "test",
|
"com.typesafe.akka" %% "akka-testkit" % "2.6.14" % "test",
|
||||||
"com.typesafe.akka" %% "akka-actor-typed" % "2.6.13",
|
"com.typesafe.akka" %% "akka-actor-typed" % "2.6.14",
|
||||||
"com.typesafe.akka" %% "akka-cluster-typed" % "2.6.13",
|
"com.typesafe.akka" %% "akka-cluster-typed" % "2.6.14",
|
||||||
"com.typesafe.akka" %% "akka-coordination" % "2.6.13",
|
"com.typesafe.akka" %% "akka-coordination" % "2.6.14",
|
||||||
"com.typesafe.akka" %% "akka-cluster-tools" % "2.6.13",
|
"com.typesafe.akka" %% "akka-cluster-tools" % "2.6.14",
|
||||||
"com.typesafe.akka" %% "akka-slf4j" % "2.6.13",
|
"com.typesafe.akka" %% "akka-slf4j" % "2.6.14",
|
||||||
"com.typesafe.akka" %% "akka-http" % "10.2.4",
|
"com.typesafe.akka" %% "akka-http" % "10.2.4",
|
||||||
"com.typesafe.scala-logging" %% "scala-logging" % "3.9.3",
|
"com.typesafe.scala-logging" %% "scala-logging" % "3.9.3",
|
||||||
"org.specs2" %% "specs2-core" % "4.10.6" % "test",
|
"org.specs2" %% "specs2-core" % "4.10.6" % "test",
|
||||||
|
|
@ -66,10 +66,10 @@ lazy val psforeverSettings = Seq(
|
||||||
"io.kamon" %% "kamon-apm-reporter" % "2.1.15",
|
"io.kamon" %% "kamon-apm-reporter" % "2.1.15",
|
||||||
"org.json4s" %% "json4s-native" % "3.6.11",
|
"org.json4s" %% "json4s-native" % "3.6.11",
|
||||||
"io.getquill" %% "quill-jasync-postgres" % "3.7.0",
|
"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",
|
"org.postgresql" % "postgresql" % "42.2.19",
|
||||||
"com.typesafe" % "config" % "1.4.1",
|
"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",
|
"com.beachape" %% "enumeratum" % "1.6.1",
|
||||||
"joda-time" % "joda-time" % "2.10.10",
|
"joda-time" % "joda-time" % "2.10.10",
|
||||||
"commons-io" % "commons-io" % "2.8.0",
|
"commons-io" % "commons-io" % "2.8.0",
|
||||||
|
|
|
||||||
|
|
@ -129,7 +129,7 @@ object MiddlewareActor {
|
||||||
* Do nothing.
|
* Do nothing.
|
||||||
* Wait to be told to do something.
|
* 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()
|
val outQueueBundled: mutable.Queue[PlanetSidePacket] = mutable.Queue()
|
||||||
|
|
||||||
/** Latest outbound sequence number;
|
/** 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
|
var outSequence = 0
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -202,7 +203,8 @@ class MiddlewareActor(
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Latest outbound subslot number;
|
/** 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
|
var outSubslot = 0
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -224,12 +226,13 @@ class MiddlewareActor(
|
||||||
/**
|
/**
|
||||||
* Do not bundle these packets together with other packets
|
* Do not bundle these packets together with other packets
|
||||||
*/
|
*/
|
||||||
val packetsBundledByThemselves: List[PlanetSidePacket=>Boolean] = List(
|
val packetsBundledByThemselves: List[PlanetSidePacket => Boolean] = List(
|
||||||
MiddlewareActor.keepAliveMessageGuard,
|
MiddlewareActor.keepAliveMessageGuard,
|
||||||
MiddlewareActor.characterInfoMessageGuard
|
MiddlewareActor.characterInfoMessageGuard
|
||||||
)
|
)
|
||||||
|
|
||||||
val smpHistoryLength: Int = 100
|
val smpHistoryLength: Int = 100
|
||||||
|
|
||||||
/** History of created `SlottedMetaPacket`s.
|
/** History of created `SlottedMetaPacket`s.
|
||||||
* In case the client does not register receiving a packet by checking against packet subslot index numbers,
|
* In case the client does not register receiving a packet by checking against packet subslot index numbers,
|
||||||
* it will dispatch a `RelatedA` packet,
|
* it will dispatch a `RelatedA` packet,
|
||||||
|
|
@ -239,24 +242,32 @@ class MiddlewareActor(
|
||||||
* The client and server supposedly maintain reciprocating mechanisms.
|
* The client and server supposedly maintain reciprocating mechanisms.
|
||||||
*/
|
*/
|
||||||
val preparedSlottedMetaPackets: Array[SlottedMetaPacket] = new Array[SlottedMetaPacket](smpHistoryLength)
|
val preparedSlottedMetaPackets: Array[SlottedMetaPacket] = new Array[SlottedMetaPacket](smpHistoryLength)
|
||||||
var nextSmpIndex: Int = 0
|
var nextSmpIndex: Int = 0
|
||||||
var acceptedSmpSubslot: Int = 0
|
var acceptedSmpSubslot: Int = 0
|
||||||
|
|
||||||
/** end of life stat */
|
/** end of life stat */
|
||||||
var timesInReorderQueue: Int = 0
|
var timesInReorderQueue: Int = 0
|
||||||
|
|
||||||
/** end of life stat */
|
/** end of life stat */
|
||||||
var timesSubslotMissing: Int = 0
|
var timesSubslotMissing: Int = 0
|
||||||
|
|
||||||
/** Delay between runs of the packet bundler/resolver timer (ms);
|
/** 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
|
val packetProcessorDelay = Config.app.network.middleware.packetBundlingDelay
|
||||||
|
|
||||||
/** Timer that handles the bundling and throttling of outgoing packets and resolves disorganized inbound packets */
|
/** Timer that handles the bundling and throttling of outgoing packets and resolves disorganized inbound packets */
|
||||||
var packetProcessor: Cancellable = Default.Cancellable
|
var packetProcessor: Cancellable = Default.Cancellable
|
||||||
|
|
||||||
/** how long packets that are out of sequential order wait for the missing sequence before being expedited (ms) */
|
/** 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
|
val inReorderTimeout = Config.app.network.middleware.inReorderTimeout
|
||||||
|
|
||||||
/** Timer that handles the bundling and throttling of outgoing packets requesting packets with known subslot numbers */
|
/** Timer that handles the bundling and throttling of outgoing packets requesting packets with known subslot numbers */
|
||||||
var subslotMissingProcessor: Cancellable = Default.Cancellable
|
var subslotMissingProcessor: Cancellable = Default.Cancellable
|
||||||
|
|
||||||
/** how long to wait between repeated requests for packets with known missing subslot numbers (ms) */
|
/** how long to wait between repeated requests for packets with known missing subslot numbers (ms) */
|
||||||
val inSubslotMissingDelay = Config.app.network.middleware.inSubslotMissingDelay
|
val inSubslotMissingDelay = Config.app.network.middleware.inSubslotMissingDelay
|
||||||
|
|
||||||
/** how many time to repeat the request for a packet with a known missing subslot number */
|
/** how many time to repeat the request for a packet with a known missing subslot number */
|
||||||
val inSubslotMissingNumberOfAttempts = Config.app.network.middleware.inSubslotMissingAttempts
|
val inSubslotMissingNumberOfAttempts = Config.app.network.middleware.inSubslotMissingAttempts
|
||||||
|
|
||||||
|
|
@ -274,12 +285,19 @@ class MiddlewareActor(
|
||||||
send(ServerStart(nonce, serverNonce), None, None)
|
send(ServerStart(nonce, serverNonce), None, None)
|
||||||
cryptoSetup()
|
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
|
// TODO ResetSequence
|
||||||
case _ =>
|
case _ =>
|
||||||
log.warn(s"Unexpected packet type $packet in start (before crypto)")
|
log.warn(s"Unexpected packet type $packet in start (before crypto)")
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
}
|
}
|
||||||
case Failure(_) =>
|
case Failure(unmarshalError) =>
|
||||||
// There is a special case where no crypto is being used.
|
// There is a special case where no crypto is being used.
|
||||||
// The only packet coming through looks like PingMsg. This is a hardcoded
|
// The only packet coming through looks like PingMsg. This is a hardcoded
|
||||||
// feature of the client @ 0x005FD618
|
// feature of the client @ 0x005FD618
|
||||||
|
|
@ -300,8 +318,8 @@ class MiddlewareActor(
|
||||||
log.error(s"Unexpected non-crypto packet type $packet in start")
|
log.error(s"Unexpected non-crypto packet type $packet in start")
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
}
|
}
|
||||||
case Failure(e) =>
|
case Failure(decodeError) =>
|
||||||
log.error(s"Could not decode packet in start: $e")
|
log.error(s"Could not decode packet in start: '$unmarshalError' / '$decodeError'")
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -356,17 +374,17 @@ class MiddlewareActor(
|
||||||
packet match {
|
packet match {
|
||||||
case (ClientFinished(clientPubKey, _), Some(_)) =>
|
case (ClientFinished(clientPubKey, _), Some(_)) =>
|
||||||
serverMACBuffer ++= msg.drop(3)
|
serverMACBuffer ++= msg.drop(3)
|
||||||
val agreedKey = dh.agree(clientPubKey.toArray)
|
val agreedKey = dh.agree(clientPubKey.toArray)
|
||||||
val agreedMessage = ByteVector("master secret".getBytes) ++ clientChallenge ++
|
val agreedMessage = ByteVector("master secret".getBytes) ++ clientChallenge ++
|
||||||
hex"00000000" ++ serverChallenge ++ hex"00000000"
|
hex"00000000" ++ serverChallenge ++ hex"00000000"
|
||||||
val masterSecret = new Md5Mac(ByteVector.view(agreedKey)).updateFinal(agreedMessage)
|
val masterSecret = new Md5Mac(ByteVector.view(agreedKey)).updateFinal(agreedMessage)
|
||||||
val mac = new Md5Mac(masterSecret)
|
val mac = new Md5Mac(masterSecret)
|
||||||
//TODO verify client challenge?
|
//TODO verify client challenge?
|
||||||
val serverChallengeResult = mac
|
val serverChallengeResult = mac
|
||||||
.updateFinal(ByteVector("server finished".getBytes) ++ serverMACBuffer ++ hex"01", 0xc)
|
.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"
|
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"
|
hex"00000000" ++ clientChallenge ++ hex"00000000"
|
||||||
val expandedEncKey = mac.updateFinal(encExpansion, 64)
|
val expandedEncKey = mac.updateFinal(encExpansion, 64)
|
||||||
val expandedDecKey = mac.updateFinal(decExpansion, 64)
|
val expandedDecKey = mac.updateFinal(decExpansion, 64)
|
||||||
|
|
@ -381,13 +399,12 @@ class MiddlewareActor(
|
||||||
)
|
)
|
||||||
send(ServerFinished(serverChallengeResult))
|
send(ServerFinished(serverChallengeResult))
|
||||||
//start the queue processor loop
|
//start the queue processor loop
|
||||||
packetProcessor =
|
packetProcessor = context.system.scheduler.scheduleWithFixedDelay(
|
||||||
context.system.scheduler.scheduleWithFixedDelay(
|
packetProcessorDelay,
|
||||||
packetProcessorDelay,
|
packetProcessorDelay
|
||||||
packetProcessorDelay
|
)(() => {
|
||||||
)(()=> {
|
context.self ! ProcessQueue()
|
||||||
context.self ! ProcessQueue()
|
})
|
||||||
})
|
|
||||||
active()
|
active()
|
||||||
|
|
||||||
case other =>
|
case other =>
|
||||||
|
|
@ -416,7 +433,7 @@ class MiddlewareActor(
|
||||||
activeSequenceFunc(packet, sequence)
|
activeSequenceFunc(packet, sequence)
|
||||||
case Successful((packet, None)) =>
|
case Successful((packet, None)) =>
|
||||||
in(packet)
|
in(packet)
|
||||||
case Failure(e) =>
|
case Failure(e) =>
|
||||||
log.error(s"Could not decode $connectionId's packet: $e")
|
log.error(s"Could not decode $connectionId's packet: $e")
|
||||||
}
|
}
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
@ -446,7 +463,7 @@ class MiddlewareActor(
|
||||||
val onSignal: PartialFunction[(ActorContext[Command], Signal), Behavior[Command]] = {
|
val onSignal: PartialFunction[(ActorContext[Command], Signal), Behavior[Command]] = {
|
||||||
case (_, PostStop) =>
|
case (_, PostStop) =>
|
||||||
context.stop(nextActor)
|
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")
|
log.trace(s"out of sequence checks: $timesInReorderQueue, subslot missing checks: $timesSubslotMissing")
|
||||||
}
|
}
|
||||||
packetProcessor.cancel()
|
packetProcessor.cancel()
|
||||||
|
|
@ -575,31 +592,29 @@ class MiddlewareActor(
|
||||||
} else if (outQueue.nonEmpty) {
|
} else if (outQueue.nonEmpty) {
|
||||||
val bundle = {
|
val bundle = {
|
||||||
var length = 0L
|
var length = 0L
|
||||||
val (_, bundle) = outQueue
|
val (_, bundle) = outQueue.dequeueWhile {
|
||||||
.dequeueWhile {
|
case (packet, payload) =>
|
||||||
case (packet, payload) =>
|
// packet length + MultiPacketEx header length
|
||||||
// packet length + MultiPacketEx header length
|
val packetLength = payload.length + (
|
||||||
val packetLength = payload.length + (
|
if (payload.length < 2048) { 8L } //256 * 8; 1L * 8
|
||||||
if (payload.length < 2048) { 8L } //256 * 8; 1L * 8
|
else if (payload.length < 524288) { 16L } //65536 * 8; 2L * 8
|
||||||
else if (payload.length < 524288) { 16L } //65536 * 8; 2L * 8
|
else { 32L } //4L * 8
|
||||||
else { 32L } //4L * 8
|
)
|
||||||
)
|
length += packetLength
|
||||||
length += packetLength
|
|
||||||
|
|
||||||
if (packetsBundledByThemselves.exists { _(packet) }) {
|
if (packetsBundledByThemselves.exists { _(packet) }) {
|
||||||
if (length == packetLength) {
|
if (length == packetLength) {
|
||||||
length += MTU
|
length += MTU * 8
|
||||||
true //dequeue only packet
|
true // dequeue only packet
|
||||||
} else {
|
|
||||||
false //dequeue later
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Some packets may be larger than the MTU limit, in that case we dequeue anyway and split later
|
false // dequeue 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
|
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
.unzip
|
// 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
|
bundle
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -616,7 +631,7 @@ class MiddlewareActor(
|
||||||
case Successful(data) =>
|
case Successful(data) =>
|
||||||
outQueueBundled.enqueue(smp(slot = 0, data.bytes))
|
outQueueBundled.enqueue(smp(slot = 0, data.bytes))
|
||||||
sendFirstBundle()
|
sendFirstBundle()
|
||||||
case Failure(cause) =>
|
case Failure(cause) =>
|
||||||
log.error(s"could not bundle $bundle: ${cause.message}")
|
log.error(s"could not bundle $bundle: ${cause.message}")
|
||||||
//to avoid packets being lost, unwrap bundle and queue the packets individually
|
//to avoid packets being lost, unwrap bundle and queue the packets individually
|
||||||
bundle.foreach { packet =>
|
bundle.foreach { packet =>
|
||||||
|
|
@ -636,7 +651,7 @@ class MiddlewareActor(
|
||||||
* @see `activeNormal`
|
* @see `activeNormal`
|
||||||
* @see `activeWithReordering`
|
* @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.
|
* Properly handle the newly-arrived packet based on its sequence number.
|
||||||
|
|
@ -651,7 +666,7 @@ class MiddlewareActor(
|
||||||
if (sequence == inSequence + 1) {
|
if (sequence == inSequence + 1) {
|
||||||
inSequence = sequence
|
inSequence = sequence
|
||||||
in(packet)
|
in(packet)
|
||||||
} else if(sequence < inSequence) { //expedite this packet
|
} else if (sequence < inSequence) { //expedite this packet
|
||||||
in(packet)
|
in(packet)
|
||||||
} else if (sequence == inSequence) {
|
} else if (sequence == inSequence) {
|
||||||
//do nothing?
|
//do nothing?
|
||||||
|
|
@ -681,7 +696,7 @@ class MiddlewareActor(
|
||||||
inSequence = sequence
|
inSequence = sequence
|
||||||
in(packet)
|
in(packet)
|
||||||
processInReorderQueue()
|
processInReorderQueue()
|
||||||
} else if(sequence < inSequence) { //expedite this packet
|
} else if (sequence < inSequence) { //expedite this packet
|
||||||
inReorderQueue.filterInPlace(_.sequence == sequence)
|
inReorderQueue.filterInPlace(_.sequence == sequence)
|
||||||
in(packet)
|
in(packet)
|
||||||
inReorderQueueFunc = inReorderQueueTest
|
inReorderQueueFunc = inReorderQueueTest
|
||||||
|
|
@ -690,7 +705,7 @@ class MiddlewareActor(
|
||||||
//do nothing?
|
//do nothing?
|
||||||
} else {
|
} else {
|
||||||
var insertAtIndex = 0
|
var insertAtIndex = 0
|
||||||
val length = inReorderQueue.length
|
val length = inReorderQueue.length
|
||||||
while (insertAtIndex < length && sequence >= inReorderQueue(insertAtIndex).sequence) {
|
while (insertAtIndex < length && sequence >= inReorderQueue(insertAtIndex).sequence) {
|
||||||
insertAtIndex += 1
|
insertAtIndex += 1
|
||||||
}
|
}
|
||||||
|
|
@ -704,7 +719,7 @@ class MiddlewareActor(
|
||||||
* @see `inReorderQueueTest`
|
* @see `inReorderQueueTest`
|
||||||
* @see `processInReorderQueueTimeoutOnly`
|
* @see `processInReorderQueueTimeoutOnly`
|
||||||
*/
|
*/
|
||||||
private var inReorderQueueFunc: ()=>Unit = doNothing
|
private var inReorderQueueFunc: () => Unit = doNothing
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Examine inbound packets that need to be reordered by sequence number and
|
* Examine inbound packets that need to be reordered by sequence number and
|
||||||
|
|
@ -715,9 +730,9 @@ class MiddlewareActor(
|
||||||
*/
|
*/
|
||||||
def processInReorderQueue(): Unit = {
|
def processInReorderQueue(): Unit = {
|
||||||
timesInReorderQueue += 1
|
timesInReorderQueue += 1
|
||||||
var currentSequence = inSequence
|
var currentSequence = inSequence
|
||||||
val currentTime = System.currentTimeMillis()
|
val currentTime = System.currentTimeMillis()
|
||||||
val takenPackets = (inReorderQueue.indexWhere { currentTime - _.time > inReorderTimeout.toMillis } match {
|
val takenPackets = (inReorderQueue.indexWhere { currentTime - _.time > inReorderTimeout.toMillis } match {
|
||||||
case -1 =>
|
case -1 =>
|
||||||
inReorderQueue
|
inReorderQueue
|
||||||
.takeWhile { entry =>
|
.takeWhile { entry =>
|
||||||
|
|
@ -731,7 +746,7 @@ class MiddlewareActor(
|
||||||
}
|
}
|
||||||
case index =>
|
case index =>
|
||||||
// Forward all packets ahead of any packet that has been in the queue for 50ms
|
// 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
|
currentSequence = entries.last.sequence
|
||||||
entries
|
entries
|
||||||
}).map(_.packet)
|
}).map(_.packet)
|
||||||
|
|
@ -757,7 +772,7 @@ class MiddlewareActor(
|
||||||
inReorderQueue.dropInPlace(takenPackets.length)
|
inReorderQueue.dropInPlace(takenPackets.length)
|
||||||
takenPackets.foreach { p =>
|
takenPackets.foreach { p =>
|
||||||
inReorderQueueFunc = inReorderQueueTest
|
inReorderQueueFunc = inReorderQueueTest
|
||||||
inSequence = p.sequence
|
inSequence = p.sequence
|
||||||
in(p.packet)
|
in(p.packet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -783,7 +798,7 @@ class MiddlewareActor(
|
||||||
* @see `inSubslotNotMissing`
|
* @see `inSubslotNotMissing`
|
||||||
* @see `inSubslotMissingRequests`
|
* @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.
|
* What to do with a `SlottedMetaPacket` control packet normally.
|
||||||
|
|
@ -851,7 +866,7 @@ class MiddlewareActor(
|
||||||
* resume normal operations when acting upon inbound `SlottedMetaPacket` packets.
|
* resume normal operations when acting upon inbound `SlottedMetaPacket` packets.
|
||||||
* @param slot the optional slot to report the "first" `RelatedB` in a "while"
|
* @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) {
|
if (inSubslotsMissing.isEmpty) {
|
||||||
subslotMissingProcessor.cancel()
|
subslotMissingProcessor.cancel()
|
||||||
activeSubslotsFunc = inSubslotNotMissing
|
activeSubslotsFunc = inSubslotNotMissing
|
||||||
|
|
@ -870,25 +885,25 @@ class MiddlewareActor(
|
||||||
*/
|
*/
|
||||||
def askForMissingSubslots(): Unit = {
|
def askForMissingSubslots(): Unit = {
|
||||||
if (subslotMissingProcessor.isCancelled) {
|
if (subslotMissingProcessor.isCancelled) {
|
||||||
subslotMissingProcessor =
|
subslotMissingProcessor = context.system.scheduler.scheduleWithFixedDelay(
|
||||||
context.system.scheduler.scheduleWithFixedDelay(
|
initialDelay = 0.milliseconds,
|
||||||
initialDelay = 0.milliseconds,
|
inSubslotMissingDelay
|
||||||
inSubslotMissingDelay
|
)(() => {
|
||||||
)(()=> {
|
inSubslotsMissing.synchronized {
|
||||||
inSubslotsMissing.synchronized {
|
timesSubslotMissing += inSubslotsMissing.size
|
||||||
timesSubslotMissing += inSubslotsMissing.size
|
inSubslotsMissing.foreach {
|
||||||
inSubslotsMissing.foreach { case (subslot, attempt) =>
|
case (subslot, attempt) =>
|
||||||
val value = attempt - 1
|
val value = attempt - 1
|
||||||
if(value > 0) {
|
if (value > 0) {
|
||||||
inSubslotsMissing(subslot) = value
|
inSubslotsMissing(subslot) = value
|
||||||
} else {
|
} else {
|
||||||
inSubslotsMissing.remove(subslot)
|
inSubslotsMissing.remove(subslot)
|
||||||
}
|
}
|
||||||
send(RelatedA(0, subslot))
|
send(RelatedA(0, subslot))
|
||||||
}
|
|
||||||
inSubslotsMissingRequestsFinished()
|
|
||||||
}
|
}
|
||||||
})
|
inSubslotsMissingRequestsFinished()
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -207,6 +207,8 @@ object AvatarActor {
|
||||||
|
|
||||||
private case class SetStamina(stamina: Int) extends Command
|
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 AvatarResponse(avatar: Avatar)
|
||||||
|
|
||||||
final case class AvatarLoginResponse(avatar: Avatar)
|
final case class AvatarLoginResponse(avatar: Avatar)
|
||||||
|
|
@ -486,14 +488,14 @@ class AvatarActor(
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
var requiredByCert: Set[Certification] = Set(certification)
|
var requiredByCert: Set[Certification] = Set(certification)
|
||||||
var removeThese: Set[Certification] = Set(certification)
|
var removeThese: Set[Certification] = Set(certification)
|
||||||
val allCerts: Set[Certification] = Certification.values.toSet
|
val allCerts: Set[Certification] = Certification.values.toSet
|
||||||
do {
|
do {
|
||||||
removeThese = allCerts.filter { testingCert =>
|
removeThese = allCerts.filter { testingCert =>
|
||||||
testingCert.requires.intersect(removeThese).nonEmpty
|
testingCert.requires.intersect(removeThese).nonEmpty
|
||||||
}
|
}
|
||||||
requiredByCert = requiredByCert ++ removeThese
|
requiredByCert = requiredByCert ++ removeThese
|
||||||
} while(removeThese.nonEmpty)
|
} while (removeThese.nonEmpty)
|
||||||
|
|
||||||
Future
|
Future
|
||||||
.sequence(
|
.sequence(
|
||||||
|
|
@ -745,13 +747,12 @@ class AvatarActor(
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
||||||
case ActivateImplant(implantType) =>
|
case ActivateImplant(implantType) =>
|
||||||
val res = avatar.implants.zipWithIndex.collectFirst {
|
avatar.implants.zipWithIndex.collectFirst {
|
||||||
case (Some(implant), index) if implant.definition.implantType == implantType => (implant, index)
|
case (Some(implant), index) if implant.definition.implantType == implantType => (implant, index)
|
||||||
}
|
} match {
|
||||||
res match {
|
|
||||||
case Some((implant, slot)) =>
|
case Some((implant, slot)) =>
|
||||||
if (!implant.initialized) {
|
if (!implant.initialized) {
|
||||||
log.error(s"requested activation of uninitialized implant $implant")
|
log.warn(s"requested activation of uninitialized implant $implantType")
|
||||||
} else if (
|
} else if (
|
||||||
!consumeStamina(implant.definition.ActivationStaminaCost) ||
|
!consumeStamina(implant.definition.ActivationStaminaCost) ||
|
||||||
avatar.stamina < implant.definition.StaminaCost
|
avatar.stamina < implant.definition.StaminaCost
|
||||||
|
|
@ -799,6 +800,25 @@ class AvatarActor(
|
||||||
}
|
}
|
||||||
Behaviors.same
|
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) =>
|
case DeactivateImplant(implantType) =>
|
||||||
deactivateImplant(implantType)
|
deactivateImplant(implantType)
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
@ -819,7 +839,7 @@ class AvatarActor(
|
||||||
val totalStamina = math.min(avatar.maxStamina, avatar.stamina + stamina)
|
val totalStamina = math.min(avatar.maxStamina, avatar.stamina + stamina)
|
||||||
val fatigued = if (avatar.fatigued && totalStamina >= 20) {
|
val fatigued = if (avatar.fatigued && totalStamina >= 20) {
|
||||||
avatar.implants.zipWithIndex.foreach {
|
avatar.implants.zipWithIndex.foreach {
|
||||||
case (Some(implant), slot) =>
|
case (Some(_), slot) =>
|
||||||
sessionActor ! SessionActor.SendResponse(
|
sessionActor ! SessionActor.SendResponse(
|
||||||
AvatarImplantMessage(session.get.player.GUID, ImplantAction.OutOfStamina, slot, 0)
|
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
|
// Start client side initialization timer, visible on the character screen
|
||||||
// Progress accumulates according to the client's knowledge of the implant initialization time
|
// 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(
|
session.get.zone.AvatarEvents ! AvatarServiceMessage(
|
||||||
avatar.name,
|
avatar.name,
|
||||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, ActionProgressMessage(slot + 6, 0))
|
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, _) => ;
|
case (None, _) => ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1052,16 +1065,15 @@ class AvatarActor(
|
||||||
AvatarImplantMessage(session.get.player.GUID, ImplantAction.Initialization, slot, 0)
|
AvatarImplantMessage(session.get.player.GUID, ImplantAction.Initialization, slot, 0)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
Some(implant.copy(initialized = false))
|
Some(implant.copy(initialized = false, active = false))
|
||||||
case (None, _) => None
|
case (None, _) => None
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
def deactivateImplant(implantType: ImplantType): Unit = {
|
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)
|
case (Some(implant), index) if implant.definition.implantType == implantType => (implant, index)
|
||||||
}
|
} match {
|
||||||
res match {
|
|
||||||
case Some((implant, slot)) =>
|
case Some((implant, slot)) =>
|
||||||
implantTimers(slot).cancel()
|
implantTimers(slot).cancel()
|
||||||
avatar = avatar.copy(
|
avatar = avatar.copy(
|
||||||
|
|
|
||||||
|
|
@ -363,7 +363,7 @@ class ChatActor(
|
||||||
session.zone.Buildings.values
|
session.zone.Buildings.values
|
||||||
})
|
})
|
||||||
.flatMap { building => building.Amenities.filter { _.isInstanceOf[ResourceSilo] } }
|
.flatMap { building => building.Amenities.filter { _.isInstanceOf[ResourceSilo] } }
|
||||||
if(silos.isEmpty) {
|
if (silos.isEmpty) {
|
||||||
sessionActor ! SessionActor.SendResponse(
|
sessionActor ! SessionActor.SendResponse(
|
||||||
ChatMsg(UNK_229, true, "Server", s"no targets for ntu found with parameters $facility", None)
|
ChatMsg(UNK_229, true, "Server", s"no targets for ntu found with parameters $facility", None)
|
||||||
)
|
)
|
||||||
|
|
@ -371,24 +371,29 @@ class ChatActor(
|
||||||
customNtuValue match {
|
customNtuValue match {
|
||||||
// x = n0% of maximum capacitance
|
// x = n0% of maximum capacitance
|
||||||
case Some(value) if value > -1 && value < 11 =>
|
case Some(value) if value > -1 && value < 11 =>
|
||||||
silos.collect { case silo: ResourceSilo =>
|
silos.collect {
|
||||||
silo.Actor ! ResourceSilo.UpdateChargeLevel(value * silo.MaxNtuCapacitor * 0.1f - silo.NtuCapacitor)
|
case silo: ResourceSilo =>
|
||||||
|
silo.Actor ! ResourceSilo.UpdateChargeLevel(
|
||||||
|
value * silo.MaxNtuCapacitor * 0.1f - silo.NtuCapacitor
|
||||||
|
)
|
||||||
}
|
}
|
||||||
// capacitance set to x (where x > 10) exactly, within limits
|
// capacitance set to x (where x > 10) exactly, within limits
|
||||||
case Some(value) =>
|
case Some(value) =>
|
||||||
silos.collect { case silo: ResourceSilo =>
|
silos.collect {
|
||||||
silo.Actor ! ResourceSilo.UpdateChargeLevel(value - silo.NtuCapacitor)
|
case silo: ResourceSilo =>
|
||||||
|
silo.Actor ! ResourceSilo.UpdateChargeLevel(value - silo.NtuCapacitor)
|
||||||
}
|
}
|
||||||
case None =>
|
case None =>
|
||||||
// x >= n0% of maximum capacitance and x <= maximum capacitance
|
// x >= n0% of maximum capacitance and x <= maximum capacitance
|
||||||
val rand = new scala.util.Random
|
val rand = new scala.util.Random
|
||||||
silos.collect { case silo: ResourceSilo =>
|
silos.collect {
|
||||||
val a = 7
|
case silo: ResourceSilo =>
|
||||||
val b = 10 - a
|
val a = 7
|
||||||
val tenth = silo.MaxNtuCapacitor * 0.1f
|
val b = 10 - a
|
||||||
silo.Actor ! ResourceSilo.UpdateChargeLevel(
|
val tenth = silo.MaxNtuCapacitor * 0.1f
|
||||||
a * tenth + rand.nextFloat() * b * tenth - silo.NtuCapacitor
|
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
|
val (faction, factionPos): (PlanetSideEmpire.Value, Option[Int]) = args.zipWithIndex
|
||||||
.map { case (factionName, pos) => (factionName.toLowerCase, pos) }
|
.map { case (factionName, pos) => (factionName.toLowerCase, pos) }
|
||||||
.flatMap {
|
.flatMap {
|
||||||
case ("tr", pos) => Some(PlanetSideEmpire.TR, pos)
|
case ("tr", pos) => Some(PlanetSideEmpire.TR, pos)
|
||||||
case ("nc", pos) => Some(PlanetSideEmpire.NC, pos)
|
case ("nc", pos) => Some(PlanetSideEmpire.NC, pos)
|
||||||
case ("vs", pos) => Some(PlanetSideEmpire.VS, pos)
|
case ("vs", pos) => Some(PlanetSideEmpire.VS, pos)
|
||||||
case ("none", pos) => Some(PlanetSideEmpire.NEUTRAL, pos)
|
case ("none", pos) => Some(PlanetSideEmpire.NEUTRAL, pos)
|
||||||
case ("bo", pos) => Some(PlanetSideEmpire.NEUTRAL, pos)
|
case ("bo", pos) => Some(PlanetSideEmpire.NEUTRAL, pos)
|
||||||
case ("neutral", pos) => Some(PlanetSideEmpire.NEUTRAL, pos)
|
case ("neutral", pos) => Some(PlanetSideEmpire.NEUTRAL, pos)
|
||||||
case _ => None
|
case _ => None
|
||||||
}
|
}
|
||||||
.headOption match {
|
.headOption match {
|
||||||
case Some((isFaction, pos)) => (isFaction, Some(pos))
|
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 {
|
val (buildingsOption, buildingPos): (Option[Seq[Building]], Option[Int]) = args.zipWithIndex.flatMap {
|
||||||
|
|
@ -475,14 +480,11 @@ class ChatActor(
|
||||||
// [all [<empire>|none]]
|
// [all [<empire>|none]]
|
||||||
(Some(1) | None, Some(0), None, Some(_), None) =>
|
(Some(1) | None, Some(0), None, Some(_), None) =>
|
||||||
val buildings: Seq[Building] = buildingsOption.getOrElse(
|
val buildings: Seq[Building] = buildingsOption.getOrElse(
|
||||||
session.zone.Buildings
|
session.zone.Buildings.values.filter { building =>
|
||||||
.values
|
building.PlayersInSOI.exists { soiPlayer =>
|
||||||
.filter { building =>
|
session.player.CharId == soiPlayer.CharId
|
||||||
building.PlayersInSOI.exists { soiPlayer =>
|
|
||||||
session.player.CharId == soiPlayer.CharId
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.toSeq
|
}.toSeq
|
||||||
)
|
)
|
||||||
buildings foreach { building =>
|
buildings foreach { building =>
|
||||||
// TODO implement timer
|
// TODO implement timer
|
||||||
|
|
@ -563,12 +565,21 @@ class ChatActor(
|
||||||
ChatChannel.Default()
|
ChatChannel.Default()
|
||||||
)
|
)
|
||||||
|
|
||||||
case (CMT_VOICE, _, _) =>
|
case (CMT_VOICE, _, contents) =>
|
||||||
chatService ! ChatService.Message(
|
// SH prefix are tactical voice macros only sent to squad
|
||||||
session,
|
if (contents.startsWith("SH")) {
|
||||||
message.copy(recipient = session.player.Name),
|
channels.foreach {
|
||||||
ChatChannel.Default()
|
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 =>
|
case (CMT_TELL, _, _) if !session.player.silenced =>
|
||||||
chatService ! ChatService.Message(
|
chatService ! ChatService.Message(
|
||||||
|
|
@ -675,11 +686,11 @@ class ChatActor(
|
||||||
case (CMT_WARP, _, contents) if gmCommandAllowed =>
|
case (CMT_WARP, _, contents) if gmCommandAllowed =>
|
||||||
val buffer = contents.toLowerCase.split("\\s+")
|
val buffer = contents.toLowerCase.split("\\s+")
|
||||||
val (coordinates, waypoint) = (buffer.lift(0), buffer.lift(1), buffer.lift(2)) match {
|
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(x), Some(y), Some(z)) => (Some(x, y, z), None)
|
||||||
case (Some("to"), Some(character), None) => (None, None) // TODO not implemented
|
case (Some("to"), Some(character), None) => (None, None) // TODO not implemented
|
||||||
case (Some("near"), Some(objectName), 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 (Some(waypoint), None, None) if waypoint.nonEmpty => (None, Some(waypoint))
|
||||||
case _ => (None, None)
|
case _ => (None, None)
|
||||||
}
|
}
|
||||||
(coordinates, waypoint) match {
|
(coordinates, waypoint) match {
|
||||||
case (Some((x, y, z)), None) if List(x, y, z).forall { str =>
|
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" =>
|
case (None, Some(waypoint)) if waypoint == "-list" =>
|
||||||
val zone = PointOfInterest.get(session.player.Zone.id)
|
val zone = PointOfInterest.get(session.player.Zone.id)
|
||||||
zone match {
|
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 _ => ChatMsg(UNK_229, true, "", s"unknown player zone '${session.player.Zone.id}'", None)
|
||||||
}
|
}
|
||||||
case (None, Some(waypoint)) if waypoint != "-help" =>
|
case (None, Some(waypoint)) if waypoint != "-help" =>
|
||||||
|
|
@ -939,7 +953,8 @@ class ChatActor(
|
||||||
case CMT_VOICE =>
|
case CMT_VOICE =>
|
||||||
if (
|
if (
|
||||||
session.zone == fromSession.zone &&
|
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)
|
sessionActor ! SessionActor.SendResponse(message)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.local.{LocalAction, LocalResponse, LocalServiceMessage, LocalServiceResponse}
|
||||||
import net.psforever.services.properties.PropertyOverrideManager
|
import net.psforever.services.properties.PropertyOverrideManager
|
||||||
import net.psforever.services.support.SupportActor
|
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.hart.HartTimer
|
||||||
import net.psforever.services.vehicle.{VehicleAction, VehicleResponse, VehicleServiceMessage, VehicleServiceResponse}
|
import net.psforever.services.vehicle.{VehicleAction, VehicleResponse, VehicleServiceMessage, VehicleServiceResponse}
|
||||||
import net.psforever.services.{RemoverActor, Service, ServiceManager, InterstellarClusterService => ICS}
|
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 setupAvatarFunc: () => Unit = AvatarCreate
|
||||||
var setCurrentAvatarFunc: Player => Unit = SetCurrentAvatarNormally
|
var setCurrentAvatarFunc: Player => Unit = SetCurrentAvatarNormally
|
||||||
var persist: () => Unit = NoPersistence
|
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)
|
* 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
|
out
|
||||||
case None =>
|
case None =>
|
||||||
//delete stale entity reference from client
|
//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))
|
sendResponse(ObjectDeleteMessage(id.get, 0))
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
case Some(obj) if obj.HasGUID && obj.GUID != id.get =>
|
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.error(
|
||||||
log.debug(s"ValidObject: potentially fatal error in ${continent.id} - requested ${id.get}, got ${obj.Definition.Name} with ${obj.GUID}; GUID mismatch")
|
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
|
None
|
||||||
|
|
||||||
case out @ Some(obj) if obj.HasGUID =>
|
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) =>
|
case Some(entry) if vehicle.Seats(entry.mount).occupant.contains(player) =>
|
||||||
Some(vehicle)
|
Some(vehicle)
|
||||||
case Some(entry) =>
|
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
|
None
|
||||||
case None =>
|
case None =>
|
||||||
//log.warn(s"TransferPassenger: $playerName is missing from the manifest of a summoning ${vehicle.Definition.Name} from ${vehicle.Zone.id}")
|
//log.warn(s"TransferPassenger: $playerName is missing from the manifest of a summoning ${vehicle.Definition.Name} from ${vehicle.Zone.id}")
|
||||||
None
|
None
|
||||||
}).orElse {
|
}).orElse {
|
||||||
manifest.cargo.find { _.name.equals(playerName) } match {
|
manifest.cargo.find { _.name.equals(playerName) } match {
|
||||||
case Some(entry) =>
|
case Some(entry) =>
|
||||||
vehicle.CargoHolds(entry.mount).occupant match {
|
vehicle.CargoHolds(entry.mount).occupant match {
|
||||||
case out @ Some(cargo) if cargo.Seats(0).occupants.exists(_.Name.equals(playerName)) =>
|
case out @ Some(cargo) if cargo.Seats(0).occupants.exists(_.Name.equals(playerName)) =>
|
||||||
out
|
out
|
||||||
case _ =>
|
case _ =>
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
case None =>
|
case None =>
|
||||||
None
|
None
|
||||||
}} match {
|
}
|
||||||
|
} match {
|
||||||
case Some(v: Vehicle) =>
|
case Some(v: Vehicle) =>
|
||||||
galaxyService ! Service.Leave(Some(temp_channel)) //temporary vehicle-specific channel (see above)
|
galaxyService ! Service.Leave(Some(temp_channel)) //temporary vehicle-specific channel (see above)
|
||||||
deadState = DeadState.Release
|
deadState = DeadState.Release
|
||||||
|
|
@ -972,7 +987,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
|
|
||||||
case ztype =>
|
case ztype =>
|
||||||
if (ztype != Zoning.Method.None) {
|
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
|
val previousZoningType = zoningType
|
||||||
CancelZoningProcess()
|
CancelZoningProcess()
|
||||||
|
|
@ -994,7 +1011,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
else
|
else
|
||||||
LoadZonePhysicalSpawnPoint(zone.id, pos, ori, CountSpawnDelay(zone.id, spawnPoint, continent.id))
|
LoadZonePhysicalSpawnPoint(zone.id, pos, ori, CountSpawnDelay(zone.id, spawnPoint, continent.id))
|
||||||
case None =>
|
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
|
//Thread.sleep(1000) // throttle in case of infinite loop
|
||||||
RequestSanctuaryZoneSpawn(player, currentZone = 0)
|
RequestSanctuaryZoneSpawn(player, currentZone = 0)
|
||||||
}
|
}
|
||||||
|
|
@ -1137,7 +1156,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
log.warn(
|
log.warn(
|
||||||
s"FinalizeDeployable: deployable ${definition.Item}@$guid not handled by specific case"
|
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)
|
TryDropFDU(tool, index, obj.Position)
|
||||||
obj.Position = Vector3.Zero
|
obj.Position = Vector3.Zero
|
||||||
continent.Deployables ! Zone.Deployable.Dismiss(obj)
|
continent.Deployables ! Zone.Deployable.Dismiss(obj)
|
||||||
|
|
@ -1382,8 +1403,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
} else {
|
} else {
|
||||||
keepAliveFunc = GetMountableAndSeat(None, player, continent) match {
|
keepAliveFunc = GetMountableAndSeat(None, player, continent) match {
|
||||||
case (Some(v: Vehicle), Some(seatNumber))
|
case (Some(v: Vehicle), Some(seatNumber))
|
||||||
if seatNumber > 0 && v.WeaponControlledFromSeat(seatNumber).isEmpty => KeepAlivePersistence
|
if seatNumber > 0 && v.WeaponControlledFromSeat(seatNumber).isEmpty =>
|
||||||
case _ => NormalKeepAlive
|
KeepAlivePersistence
|
||||||
|
case _ => NormalKeepAlive
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//if not the condition above, player has started playing normally
|
//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) =>
|
case AvatarResponse.EnvironmentalDamage(target, source, amount) =>
|
||||||
CancelZoningProcessWithDescriptiveReason("cancel_dmg")
|
CancelZoningProcessWithDescriptiveReason("cancel_dmg")
|
||||||
//TODO damage marker?
|
//TODO damage marker?
|
||||||
|
|
||||||
case AvatarResponse.Destroy(victim, killer, weapon, pos) =>
|
case AvatarResponse.Destroy(victim, killer, weapon, pos) =>
|
||||||
// guid = victim // killer = killer ;)
|
// guid = victim // killer = killer ;)
|
||||||
|
|
@ -1897,7 +1919,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
DrowningTarget(player.guid, player.progress, player.state),
|
DrowningTarget(player.guid, player.progress, player.state),
|
||||||
vehicle match {
|
vehicle match {
|
||||||
case Some(vinfo) => Some(DrowningTarget(vinfo.guid, vinfo.progress, vinfo.state))
|
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) =>
|
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) =>
|
case LocalResponse.HackObject(target_guid, unk1, unk2) =>
|
||||||
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) =>
|
case LocalResponse.LluSpawned(llu) =>
|
||||||
// Create LLU on client
|
// Create LLU on client
|
||||||
sendResponse(ObjectCreateMessage(
|
sendResponse(
|
||||||
llu.Definition.ObjectId,
|
ObjectCreateMessage(
|
||||||
llu.GUID,
|
llu.Definition.ObjectId,
|
||||||
llu.Definition.Packet.ConstructorData(llu).get
|
llu.GUID,
|
||||||
))
|
llu.Definition.Packet.ConstructorData(llu).get
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
sendResponse(TriggerSoundMessage(TriggeredSound.LLUMaterialize, llu.Position, unk = 20, 0.8000001f))
|
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) =>
|
case LocalResponse.ShuttleEvent(ev) =>
|
||||||
val msg = OrbitalShuttleTimeMsg(
|
val msg = OrbitalShuttleTimeMsg(
|
||||||
ev.u1, ev.u2,
|
ev.u1,
|
||||||
ev.t1, ev.t2, ev.t3,
|
ev.u2,
|
||||||
|
ev.t1,
|
||||||
|
ev.t2,
|
||||||
|
ev.t3,
|
||||||
ev.pairs.map { case ((a, b), c) => PadAndShuttlePair(a, b, c) }
|
ev.pairs.map { case ((a, b), c) => PadAndShuttlePair(a, b, c) }
|
||||||
)
|
)
|
||||||
sendResponse(msg)
|
sendResponse(msg)
|
||||||
|
|
@ -2468,8 +2495,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
MountingAction(tplayer, obj, seat_number)
|
MountingAction(tplayer, obj, seat_number)
|
||||||
keepAliveFunc = KeepAlivePersistence
|
keepAliveFunc = KeepAlivePersistence
|
||||||
|
|
||||||
case Mountable.CanMount(obj: Vehicle, seat_number, _)
|
case Mountable.CanMount(obj: Vehicle, seat_number, _) if obj.Definition == GlobalDefinitions.orbital_shuttle =>
|
||||||
if obj.Definition == GlobalDefinitions.orbital_shuttle =>
|
|
||||||
CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||||
log.info(s"${player.Name} mounts the orbital shuttle")
|
log.info(s"${player.Name} mounts the orbital shuttle")
|
||||||
CancelAllProximityUnits()
|
CancelAllProximityUnits()
|
||||||
|
|
@ -2478,13 +2504,11 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
|
|
||||||
case Mountable.CanMount(obj: Vehicle, seat_number, _) =>
|
case Mountable.CanMount(obj: Vehicle, seat_number, _) =>
|
||||||
CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||||
log.info(s"${player.Name} mounts the ${obj.Definition.Name} in ${
|
log.info(s"${player.Name} mounts the ${obj.Definition.Name} in ${obj.SeatPermissionGroup(seat_number) match {
|
||||||
obj.SeatPermissionGroup(seat_number) match {
|
case Some(AccessPermissionGroup.Driver) => "the driver seat"
|
||||||
case Some(AccessPermissionGroup.Driver) => "the driver seat"
|
case Some(seatType) => s"a $seatType seat, #$seat_number"
|
||||||
case Some(seatType) => s"a $seatType seat, #$seat_number"
|
case None => "a seat"
|
||||||
case None => "a seat"
|
}}")
|
||||||
}
|
|
||||||
}")
|
|
||||||
val obj_guid: PlanetSideGUID = obj.GUID
|
val obj_guid: PlanetSideGUID = obj.GUID
|
||||||
CancelAllProximityUnits()
|
CancelAllProximityUnits()
|
||||||
sendResponse(PlanetsideAttributeMessage(obj_guid, 0, obj.Health))
|
sendResponse(PlanetsideAttributeMessage(obj_guid, 0, obj.Health))
|
||||||
|
|
@ -2541,12 +2565,12 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
DismountAction(tplayer, obj, seat_num)
|
DismountAction(tplayer, obj, seat_num)
|
||||||
|
|
||||||
case Mountable.CanDismount(obj: Vehicle, seat_num, mount_point)
|
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
|
val pguid = player.GUID
|
||||||
if (obj.MountedIn.nonEmpty) {
|
if (obj.MountedIn.nonEmpty) {
|
||||||
//dismount to hart lobby
|
//dismount to hart lobby
|
||||||
log.info(s"${tplayer.Name} dismounts the orbital shuttle into the 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)
|
val (pos, zang) = Vehicles.dismountShuttle(obj, mount_point)
|
||||||
tplayer.Position = pos
|
tplayer.Position = pos
|
||||||
sendResponse(DelayedPathMountMsg(pguid, sguid, 60, true))
|
sendResponse(DelayedPathMountMsg(pguid, sguid, 60, true))
|
||||||
|
|
@ -2554,8 +2578,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
continent.id,
|
continent.id,
|
||||||
LocalAction.SendResponse(ObjectDetachMessage(sguid, pguid, pos, 0, 0, zang))
|
LocalAction.SendResponse(ObjectDetachMessage(sguid, pguid, pos, 0, 0, zang))
|
||||||
)
|
)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
//get ready for orbital drop
|
//get ready for orbital drop
|
||||||
DismountAction(tplayer, obj, seat_num)
|
DismountAction(tplayer, obj, seat_num)
|
||||||
log.info(s"${player.Name} is prepped for dropping")
|
log.info(s"${player.Name} is prepped for dropping")
|
||||||
|
|
@ -2582,8 +2605,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
}
|
}
|
||||||
keepAliveFunc = NormalKeepAlive
|
keepAliveFunc = NormalKeepAlive
|
||||||
|
|
||||||
case Mountable.CanDismount(obj: Vehicle, seat_num, _)
|
case Mountable.CanDismount(obj: Vehicle, seat_num, _) if obj.Definition == GlobalDefinitions.droppod =>
|
||||||
if obj.Definition == GlobalDefinitions.droppod =>
|
|
||||||
log.info(s"${tplayer.Name} has landed on ${continent.id}")
|
log.info(s"${tplayer.Name} has landed on ${continent.id}")
|
||||||
UnaccessContainer(obj)
|
UnaccessContainer(obj)
|
||||||
DismountAction(tplayer, obj, seat_num)
|
DismountAction(tplayer, obj, seat_num)
|
||||||
|
|
@ -2945,7 +2967,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
avatarActor ! AvatarActor.UpdatePurchaseTime(vehicle.Definition)
|
avatarActor ! AvatarActor.UpdatePurchaseTime(vehicle.Definition)
|
||||||
vehicle.MountPoints.find { case (_, mp) => mp.seatIndex == 0 } match {
|
vehicle.MountPoints.find { case (_, mp) => mp.seatIndex == 0 } match {
|
||||||
case Some((mountPoint, _)) => vehicle.Actor ! Mountable.TryMount(player, mountPoint)
|
case Some((mountPoint, _)) => vehicle.Actor ! Mountable.TryMount(player, mountPoint)
|
||||||
case _ => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
|
|
||||||
case VehicleResponse.PlayerSeatedInVehicle(vehicle, pad) =>
|
case VehicleResponse.PlayerSeatedInVehicle(vehicle, pad) =>
|
||||||
|
|
@ -2991,7 +3013,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
vehicle.PassengerInSeat(player) match {
|
vehicle.PassengerInSeat(player) match {
|
||||||
case Some(seatNum) =>
|
case Some(seatNum) =>
|
||||||
//participant: observe changes to equipment
|
//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)
|
UpdateWeaponAtSeatPosition(vehicle, seatNum)
|
||||||
case None =>
|
case None =>
|
||||||
//observer: observe changes to external equipment
|
//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.ClearJammeredStatus()
|
||||||
player.Actor ! JammableUnit.ClearJammeredSound()
|
player.Actor ! JammableUnit.ClearJammeredSound()
|
||||||
}
|
}
|
||||||
if (deadState != DeadState.Alive) {
|
val originalDeadState = deadState
|
||||||
|
deadState = DeadState.Alive
|
||||||
|
if (originalDeadState != DeadState.Alive) {
|
||||||
avatarActor ! AvatarActor.ResetImplants()
|
avatarActor ! AvatarActor.ResetImplants()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3148,8 +3174,6 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
sendResponse(
|
sendResponse(
|
||||||
SetChatFilterMessage(ChatChannel.Platoon, false, ChatChannel.values.toList)
|
SetChatFilterMessage(ChatChannel.Platoon, false, ChatChannel.values.toList)
|
||||||
) //TODO will not always be "on" like this
|
) //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))
|
sendResponse(AvatarDeadStateMessage(DeadState.Alive, 0, 0, tplayer.Position, player.Faction, true))
|
||||||
//looking for squad (members)
|
//looking for squad (members)
|
||||||
if (tplayer.avatar.lookingForSquad || lfsm) {
|
if (tplayer.avatar.lookingForSquad || lfsm) {
|
||||||
|
|
@ -3363,7 +3387,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
def handleGamePkt(pkt: PlanetSideGamePacket) =
|
def handleGamePkt(pkt: PlanetSideGamePacket) =
|
||||||
pkt match {
|
pkt match {
|
||||||
case ConnectToWorldRequestMessage(server, token, majorVersion, minorVersion, revision, buildDate, unk) =>
|
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))
|
sendResponse(ChatMsg(ChatMessageType.CMT_CULLWATERMARK, false, "", "", None))
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
clientKeepAlive.cancel()
|
clientKeepAlive.cancel()
|
||||||
|
|
@ -3572,7 +3598,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
_.GUID != vehicle.GUID
|
_.GUID != vehicle.GUID
|
||||||
}
|
}
|
||||||
case Some(_) =>
|
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
|
player.VehicleSeated = None
|
||||||
(b, List.empty[Vehicle])
|
(b, List.empty[Vehicle])
|
||||||
case None =>
|
case None =>
|
||||||
|
|
@ -3689,7 +3717,8 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
ToggleTeleportSystem(obj, TelepadLike.AppraiseTeleportationSystem(obj, continent))
|
ToggleTeleportSystem(obj, TelepadLike.AppraiseTeleportationSystem(obj, continent))
|
||||||
}
|
}
|
||||||
val name = avatar.name
|
val name = avatar.name
|
||||||
serviceManager.ask(Lookup("hart"))(Timeout(2 seconds))
|
serviceManager
|
||||||
|
.ask(Lookup("hart"))(Timeout(2 seconds))
|
||||||
.onComplete {
|
.onComplete {
|
||||||
case Success(LookupResult("hart", ref)) =>
|
case Success(LookupResult("hart", ref)) =>
|
||||||
ref ! HartTimer.Update(continentId, name)
|
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) =>
|
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) =>
|
case msg @ ProjectileStateMessage(projectile_guid, shot_pos, shot_vel, shot_orient, seq, end, target_guid) =>
|
||||||
val index = projectile_guid.guid - Projectile.baseUID
|
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 ...")
|
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)
|
chatActor ! ChatActor.Message(msg)
|
||||||
|
|
||||||
case _ : VoiceHostRequest =>
|
case _: VoiceHostRequest =>
|
||||||
log.trace(s"VoiceHostRequest: ${player.Name} requested in-game voice chat.")
|
log.trace(s"VoiceHostRequest: ${player.Name} requested in-game voice chat.")
|
||||||
sendResponse(VoiceHostKill())
|
sendResponse(VoiceHostKill())
|
||||||
sendResponse(
|
sendResponse(
|
||||||
ChatMsg(ChatMessageType.CMT_OPEN, false, "", "Try our Discord at https://discord.gg/0nRe5TNbTYoUruA4", None)
|
ChatMsg(ChatMessageType.CMT_OPEN, false, "", "Try our Discord at https://discord.gg/0nRe5TNbTYoUruA4", None)
|
||||||
)
|
)
|
||||||
|
|
||||||
case _ : VoiceHostInfo =>
|
case _: VoiceHostInfo =>
|
||||||
sendResponse(VoiceHostKill())
|
sendResponse(VoiceHostKill())
|
||||||
|
|
||||||
case msg @ ChangeAmmoMessage(item_guid, unk1) =>
|
case msg @ ChangeAmmoMessage(item_guid, unk1) =>
|
||||||
|
|
@ -4182,7 +4213,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
)
|
)
|
||||||
Some(tool)
|
Some(tool)
|
||||||
case _ =>
|
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
|
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")
|
log.warn(s"DropItem: ${player.Name} wanted to drop a $obj, but that isn't possible")
|
||||||
case None =>
|
case None =>
|
||||||
sendResponse(ObjectDeleteMessage(item_guid, 0)) //this is fine; item doesn't exist to the server anyway
|
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) =>
|
case msg @ PickupItemMessage(item_guid, player_guid, unk1, unk2) =>
|
||||||
|
|
@ -4287,7 +4322,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
reloadValue
|
reloadValue
|
||||||
}
|
}
|
||||||
val finalReloadValue = actualReloadValue + currentMagazine
|
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
|
tool.Magazine = finalReloadValue
|
||||||
sendResponse(ReloadMessage(item_guid, finalReloadValue, unk1))
|
sendResponse(ReloadMessage(item_guid, finalReloadValue, unk1))
|
||||||
continent.AvatarEvents ! AvatarServiceMessage(
|
continent.AvatarEvents ! AvatarServiceMessage(
|
||||||
|
|
@ -4296,7 +4333,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} 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(_)) =>
|
case (_, Some(_)) =>
|
||||||
log.warn(s"ReloadMessage: the object that was found for $item_guid was not a Tool")
|
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 ||
|
(session.account.gm ||
|
||||||
(player.avatar.vehicle.contains(object_guid) && vehicle.Owner.contains(player.GUID)) ||
|
(player.avatar.vehicle.contains(object_guid) && vehicle.Owner.contains(player.GUID)) ||
|
||||||
(player.Faction == vehicle.Faction &&
|
(player.Faction == vehicle.Faction &&
|
||||||
(vehicle.Definition.CanBeOwned.nonEmpty &&
|
(vehicle.Definition.CanBeOwned.nonEmpty &&
|
||||||
(vehicle.Owner.isEmpty || continent.GUID(vehicle.Owner.get).isEmpty) || vehicle.Destroyed))) &&
|
(vehicle.Owner.isEmpty || continent.GUID(vehicle.Owner.get).isEmpty) || vehicle.Destroyed))) &&
|
||||||
(vehicle.MountedIn.isEmpty || !vehicle.Seats.values.exists(_.isOccupied))
|
(vehicle.MountedIn.isEmpty || !vehicle.Seats.values.exists(_.isOccupied))
|
||||||
) {
|
) {
|
||||||
vehicle.Actor ! Vehicle.Deconstruct()
|
vehicle.Actor ! Vehicle.Deconstruct()
|
||||||
|
|
@ -4496,7 +4535,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
) =>
|
) =>
|
||||||
ContainableMoveItem(player.Name, source, destination, item, dest)
|
ContainableMoveItem(player.Name, source, destination, item, dest)
|
||||||
case (None, _, _) =>
|
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, _) =>
|
case (_, None, _) =>
|
||||||
log.error(
|
log.error(
|
||||||
s"MoveItem: ${player.Name} wanted to move $item_guid to $destination_guid, but could not find destination object"
|
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) {
|
if (action == ImplantAction.Activation) {
|
||||||
CancelZoningProcessWithDescriptiveReason("cancel_implant")
|
CancelZoningProcessWithDescriptiveReason("cancel_implant")
|
||||||
avatar.implants(slot) match {
|
avatar.implants(slot) match {
|
||||||
case Some(implant) if implant.initialized =>
|
case Some(implant) =>
|
||||||
if (!implant.active) {
|
if (status == 1) {
|
||||||
avatarActor ! AvatarActor.ActivateImplant(implant.definition.implantType)
|
avatarActor ! AvatarActor.ActivateImplant(implant.definition.implantType)
|
||||||
} else {
|
} else {
|
||||||
avatarActor ! AvatarActor.DeactivateImplant(implant.definition.implantType)
|
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")
|
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) {
|
if (llu.Target.GUID == captureTerminal.Owner.GUID) {
|
||||||
continent.LocalEvents ! LocalServiceMessage(continent.id, LocalAction.LluCaptured(llu))
|
continent.LocalEvents ! LocalServiceMessage(continent.id, LocalAction.LluCaptured(llu))
|
||||||
} else {
|
} 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")
|
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 {
|
FindLocalVehicle match {
|
||||||
case Some(vehicle) =>
|
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(
|
sendResponse(
|
||||||
UseItemMessage(
|
UseItemMessage(
|
||||||
avatar_guid,
|
avatar_guid,
|
||||||
|
|
@ -5042,18 +5086,22 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
case None => ;
|
case None => ;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Some(obj: CaptureFlag) =>
|
case Some(obj: CaptureFlag) =>
|
||||||
// LLU can normally only be picked up the faction that owns it
|
// LLU can normally only be picked up the faction that owns it
|
||||||
if (specialItemSlotGuid.isEmpty) {
|
if (specialItemSlotGuid.isEmpty) {
|
||||||
if(obj.Faction == player.Faction) {
|
if (obj.Faction == player.Faction) {
|
||||||
specialItemSlotGuid = Some(obj.GUID)
|
specialItemSlotGuid = Some(obj.GUID)
|
||||||
continent.LocalEvents ! CaptureFlagManager.PickupFlag(obj, player)
|
continent.LocalEvents ! CaptureFlagManager.PickupFlag(obj, player)
|
||||||
} else {
|
} else {
|
||||||
log.warn(s"Player ${player.toString} tried to pick up LLU ${obj.GUID} - ${obj.Faction} that doesn't belong to their faction")
|
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) =>
|
case Some(obj) =>
|
||||||
CancelZoningProcessWithDescriptiveReason("cancel_use")
|
CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||||
|
|
@ -5214,7 +5262,8 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
player.Faction match {
|
player.Faction match {
|
||||||
case PlanetSideEmpire.NC =>
|
case PlanetSideEmpire.NC =>
|
||||||
ToggleMaxSpecialState(enable = false)
|
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 {
|
} else {
|
||||||
log.warn(s"GenericActionMessage: ${player.Name} can't handle action code 21")
|
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")
|
CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||||
log.info(s"${player.Name} wishes to load a saved favorite loadout")
|
log.info(s"${player.Name} wishes to load a saved favorite loadout")
|
||||||
action match {
|
action match {
|
||||||
case FavoritesAction.Save => avatarActor ! AvatarActor.SaveLoadout(player, loadoutType, label, line)
|
case FavoritesAction.Save => avatarActor ! AvatarActor.SaveLoadout(player, loadoutType, label, line)
|
||||||
case FavoritesAction.Delete => avatarActor ! AvatarActor.DeleteLoadout(player, loadoutType, 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.Unknown =>
|
||||||
|
log.warn(s"FavoritesRequest: ${player.Name} requested an unknown favorites action")
|
||||||
}
|
}
|
||||||
|
|
||||||
case msg @ WeaponDelayFireMessage(seq_time, weapon_guid) => ;
|
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)
|
AvatarAction.WeaponDryFire(player.GUID, weapon_guid)
|
||||||
)
|
)
|
||||||
case _ =>
|
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(
|
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
|
//todo: kick cargo passengers out. To be added after PR #216 is merged
|
||||||
obj match {
|
obj match {
|
||||||
case v: Vehicle
|
case v: Vehicle
|
||||||
if bailType == BailType.Bailed &&
|
if bailType == BailType.Bailed &&
|
||||||
v.SeatPermissionGroup(seat_num).contains(AccessPermissionGroup.Driver) &&
|
v.SeatPermissionGroup(seat_num).contains(AccessPermissionGroup.Driver) &&
|
||||||
v.isFlying =>
|
v.isFlying =>
|
||||||
v.Actor ! Vehicle.Deconstruct(None) //immediate deconstruction
|
v.Actor ! Vehicle.Deconstruct(None) //immediate deconstruction
|
||||||
case _ => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
|
|
@ -5667,7 +5719,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
log.debug(s"$msg")
|
log.debug(s"$msg")
|
||||||
|
|
||||||
case msg @ BindPlayerMessage(action, bindDesc, unk1, logging, unk2, unk3, unk4, pos) =>
|
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) =>
|
case msg @ PlanetsideAttributeMessage(object_guid, attribute_type, attribute_value) =>
|
||||||
ValidObject(object_guid) match {
|
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
|
//kick players who should not be seated in the vehicle due to permission changes
|
||||||
if (allow == VehicleLockState.Locked) { //TODO only important permission atm
|
if (allow == VehicleLockState.Locked) { //TODO only important permission atm
|
||||||
vehicle.Seats.foreach { case (seatIndex, seat) =>
|
vehicle.Seats.foreach {
|
||||||
seat.occupant match {
|
case (seatIndex, seat) =>
|
||||||
case Some(tplayer : Player) =>
|
seat.occupant match {
|
||||||
if (
|
case Some(tplayer: Player) =>
|
||||||
vehicle.SeatPermissionGroup(seatIndex).contains(group) && tplayer != player
|
if (vehicle.SeatPermissionGroup(seatIndex).contains(group) && tplayer != player) { //can not kick self
|
||||||
) { //can not kick self
|
seat.unmount(tplayer)
|
||||||
seat.unmount(tplayer)
|
tplayer.VehicleSeated = None
|
||||||
tplayer.VehicleSeated = None
|
continent.VehicleEvents ! VehicleServiceMessage(
|
||||||
continent.VehicleEvents ! VehicleServiceMessage(
|
continent.id,
|
||||||
continent.id,
|
VehicleAction.KickPassenger(tplayer.GUID, 4, false, object_guid)
|
||||||
VehicleAction.KickPassenger(tplayer.GUID, 4, false, object_guid)
|
)
|
||||||
)
|
}
|
||||||
}
|
case _ => ; // No player seated
|
||||||
case _ => ; // No player seated
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
vehicle.CargoHolds.foreach { case (cargoIndex, hold) =>
|
vehicle.CargoHolds.foreach {
|
||||||
hold.occupant match {
|
case (cargoIndex, hold) =>
|
||||||
case Some(cargo) =>
|
hold.occupant match {
|
||||||
if (vehicle.SeatPermissionGroup(cargoIndex).contains(group)) {
|
case Some(cargo) =>
|
||||||
//todo: this probably doesn't work for passengers within the cargo vehicle
|
if (vehicle.SeatPermissionGroup(cargoIndex).contains(group)) {
|
||||||
// Instruct client to start bail dismount procedure
|
//todo: this probably doesn't work for passengers within the cargo vehicle
|
||||||
self ! DismountVehicleCargoMsg(player.GUID, cargo.GUID, true, false, false)
|
// Instruct client to start bail dismount procedure
|
||||||
}
|
self ! DismountVehicleCargoMsg(player.GUID, cargo.GUID, true, false, false)
|
||||||
case None => ; // No vehicle in cargo
|
}
|
||||||
}
|
case None => ; // No vehicle in cargo
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case None => ;
|
case None => ;
|
||||||
}
|
}
|
||||||
} else {
|
} 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 {
|
} 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
|
// Cosmetics options
|
||||||
|
|
@ -5771,7 +5827,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
|
|
||||||
Some(TargetInfo(player.GUID, health, armor))
|
Some(TargetInfo(player.GUID, health, armor))
|
||||||
case _ =>
|
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
|
None
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -6507,16 +6565,16 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
|
|
||||||
//handle inventory contents
|
//handle inventory contents
|
||||||
box.Capacity = if (sumReloadValue <= fullMagazine) {
|
box.Capacity = if (sumReloadValue <= fullMagazine) {
|
||||||
sumReloadValue
|
sumReloadValue
|
||||||
} else {
|
} else {
|
||||||
val splitReloadAmmo: Int = sumReloadValue - fullMagazine
|
val splitReloadAmmo: Int = sumReloadValue - fullMagazine
|
||||||
log.trace(
|
log.trace(
|
||||||
s"PerformToolAmmoChange: ${player.Name} takes ${originalBoxCapacity - splitReloadAmmo} from a box of $originalBoxCapacity $requestedAmmoType ammo"
|
s"PerformToolAmmoChange: ${player.Name} takes ${originalBoxCapacity - splitReloadAmmo} from a box of $originalBoxCapacity $requestedAmmoType ammo"
|
||||||
)
|
)
|
||||||
val boxForInventory = AmmoBox(box.Definition, splitReloadAmmo)
|
val boxForInventory = AmmoBox(box.Definition, splitReloadAmmo)
|
||||||
continent.tasks ! stowNewFunc(boxForInventory)
|
continent.tasks ! stowNewFunc(boxForInventory)
|
||||||
fullMagazine
|
fullMagazine
|
||||||
}
|
}
|
||||||
sendResponse(
|
sendResponse(
|
||||||
InventoryStateMessage(box.GUID, tool.GUID, box.Capacity)
|
InventoryStateMessage(box.GUID, tool.GUID, box.Capacity)
|
||||||
) //should work for both players and vehicles
|
) //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 }
|
else { xs.map(_.obj.asInstanceOf[Tool].Magazine).sum }
|
||||||
val sumReloadValue: Int = box.Magazine + tailReloadValue
|
val sumReloadValue: Int = box.Magazine + tailReloadValue
|
||||||
val actualReloadValue = if (sumReloadValue <= 3) {
|
val actualReloadValue = if (sumReloadValue <= 3) {
|
||||||
RemoveOldEquipmentFromInventory(player)(x.obj)
|
RemoveOldEquipmentFromInventory(player)(x.obj)
|
||||||
sumReloadValue
|
sumReloadValue
|
||||||
} else {
|
} else {
|
||||||
ModifyAmmunition(player)(box.AmmoSlot.Box, 3 - tailReloadValue)
|
ModifyAmmunition(player)(box.AmmoSlot.Box, 3 - tailReloadValue)
|
||||||
3
|
3
|
||||||
}
|
}
|
||||||
log.info(s"${player.Name} found $actualReloadValue more $ammoType grenades to throw")
|
log.info(s"${player.Name} found $actualReloadValue more $ammoType grenades to throw")
|
||||||
ModifyAmmunition(player)(
|
ModifyAmmunition(player)(
|
||||||
tool.AmmoSlot.Box,
|
tool.AmmoSlot.Box,
|
||||||
|
|
@ -6852,27 +6910,33 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
case obj: Hackable if obj.HackedBy.nonEmpty =>
|
case obj: Hackable if obj.HackedBy.nonEmpty =>
|
||||||
//sync hack state
|
//sync hack state
|
||||||
amenity.Definition match {
|
amenity.Definition match {
|
||||||
case GlobalDefinitions.capture_terminal =>
|
case GlobalDefinitions.capture_terminal =>
|
||||||
SendPlanetsideAttributeMessage(
|
SendPlanetsideAttributeMessage(
|
||||||
amenity.GUID,
|
amenity.GUID,
|
||||||
PlanetsideAttributeEnum.ControlConsoleHackUpdate,
|
PlanetsideAttributeEnum.ControlConsoleHackUpdate,
|
||||||
HackCaptureActor.GetHackUpdateAttributeValue(amenity.asInstanceOf[CaptureTerminal], isResecured = false))
|
HackCaptureActor.GetHackUpdateAttributeValue(amenity.asInstanceOf[CaptureTerminal], isResecured = false)
|
||||||
case _ =>
|
)
|
||||||
HackObject(amenity.GUID, 1114636288L, 8L) //generic hackable object
|
case _ =>
|
||||||
}
|
HackObject(amenity.GUID, 1114636288L, 8L) //generic hackable object
|
||||||
|
}
|
||||||
|
|
||||||
// sync capture flags
|
// sync capture flags
|
||||||
case llu: CaptureFlag =>
|
case llu: CaptureFlag =>
|
||||||
// Create LLU
|
// Create LLU
|
||||||
sendResponse(ObjectCreateMessage(
|
sendResponse(
|
||||||
llu.Definition.ObjectId,
|
ObjectCreateMessage(
|
||||||
llu.GUID,
|
llu.Definition.ObjectId,
|
||||||
llu.Definition.Packet.ConstructorData(llu).get
|
llu.GUID,
|
||||||
))
|
llu.Definition.Packet.ConstructorData(llu).get
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
// Attach it to a player if it has a carrier
|
// Attach it to a player if it has a carrier
|
||||||
if (llu.Carrier.nonEmpty) {
|
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 _ => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
|
|
@ -6919,7 +6983,11 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
* @param attribute_number The attribute number
|
* @param attribute_number The attribute number
|
||||||
* @param attribute_value The attribute value
|
* @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))
|
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")
|
log.trace(s"AvatarCreate: ${player.Name} - $guid -> $data")
|
||||||
}
|
}
|
||||||
continent.Population ! Zone.Population.Spawn(avatar, player, avatarActor)
|
continent.Population ! Zone.Population.Spawn(avatar, player, avatarActor)
|
||||||
//cautious redundancy
|
|
||||||
deadState = DeadState.Alive
|
|
||||||
avatarActor ! AvatarActor.RefreshPurchaseTimes()
|
avatarActor ! AvatarActor.RefreshPurchaseTimes()
|
||||||
//begin looking for conditions to set the avatar
|
//begin looking for conditions to set the avatar
|
||||||
context.system.scheduler.scheduleOnce(delay = 250 millisecond, self, SetCurrentAvatar(player, 200))
|
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))
|
sendResponse(ObjectCreateDetailedMessage(ObjectClass.avatar, guid, data))
|
||||||
log.debug(s"AvatarRejoin: ${player.Name} - $guid -> $data")
|
log.debug(s"AvatarRejoin: ${player.Name} - $guid -> $data")
|
||||||
}
|
}
|
||||||
//cautious redundancy
|
|
||||||
deadState = DeadState.Alive
|
|
||||||
avatarActor ! AvatarActor.RefreshPurchaseTimes()
|
avatarActor ! AvatarActor.RefreshPurchaseTimes()
|
||||||
setupAvatarFunc = AvatarCreate
|
setupAvatarFunc = AvatarCreate
|
||||||
//begin looking for conditions to set the avatar
|
//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."))
|
sendResponse(DisconnectMessage("RequestSanctuaryZoneSpawn: player is already in sanctuary."))
|
||||||
} else {
|
} else {
|
||||||
continent.GUID(player.VehicleSeated) match {
|
continent.GUID(player.VehicleSeated) match {
|
||||||
case Some(obj : Vehicle) if !obj.Destroyed =>
|
case Some(obj: Vehicle) if !obj.Destroyed =>
|
||||||
cluster ! ICS.GetRandomSpawnPoint(
|
cluster ! ICS.GetRandomSpawnPoint(
|
||||||
Zones.sanctuaryZoneNumber(player.Faction),
|
Zones.sanctuaryZoneNumber(player.Faction),
|
||||||
player.Faction,
|
player.Faction,
|
||||||
|
|
@ -7525,7 +7589,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
case _: Vehicle =>
|
case _: Vehicle =>
|
||||||
terminal.Actor ! CommonMessages.Use(player, Some((target, continent.VehicleEvents)))
|
terminal.Actor ! CommonMessages.Use(player, Some((target, continent.VehicleEvents)))
|
||||||
case _ =>
|
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 {
|
terminal.Definition match {
|
||||||
case GlobalDefinitions.adv_med_terminal | GlobalDefinitions.medical_terminal =>
|
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 = {
|
def StopUsingProximityUnit(terminal: Terminal with ProximityUnit): Unit = {
|
||||||
val term_guid = terminal.GUID
|
val term_guid = terminal.GUID
|
||||||
val targets = FindProximityUnitTargetsInScope(terminal)
|
val targets = FindProximityUnitTargetsInScope(terminal)
|
||||||
if (targets.nonEmpty) {
|
if (targets.nonEmpty) {
|
||||||
if (usingMedicalTerminal.contains(term_guid)) {
|
if (usingMedicalTerminal.contains(term_guid)) {
|
||||||
usingMedicalTerminal = None
|
usingMedicalTerminal = None
|
||||||
|
|
@ -7799,10 +7865,10 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
* @return the projectile
|
* @return the projectile
|
||||||
*/
|
*/
|
||||||
def ResolveProjectileInteraction(
|
def ResolveProjectileInteraction(
|
||||||
projectile_guid: PlanetSideGUID,
|
projectile_guid: PlanetSideGUID,
|
||||||
resolution: DamageResolution.Value,
|
resolution: DamageResolution.Value,
|
||||||
target: PlanetSideGameObject with FactionAffinity with Vitality,
|
target: PlanetSideGameObject with FactionAffinity with Vitality,
|
||||||
pos: Vector3
|
pos: Vector3
|
||||||
): Option[DamageInteraction] = {
|
): Option[DamageInteraction] = {
|
||||||
FindProjectileEntry(projectile_guid) match {
|
FindProjectileEntry(projectile_guid) match {
|
||||||
case Some(projectile) =>
|
case Some(projectile) =>
|
||||||
|
|
@ -7821,11 +7887,11 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
* @return a copy of the projectile
|
* @return a copy of the projectile
|
||||||
*/
|
*/
|
||||||
def ResolveProjectileInteraction(
|
def ResolveProjectileInteraction(
|
||||||
projectile: Projectile,
|
projectile: Projectile,
|
||||||
index: Int,
|
index: Int,
|
||||||
resolution: DamageResolution.Value,
|
resolution: DamageResolution.Value,
|
||||||
target: PlanetSideGameObject with FactionAffinity with Vitality,
|
target: PlanetSideGameObject with FactionAffinity with Vitality,
|
||||||
pos: Vector3
|
pos: Vector3
|
||||||
): Option[DamageInteraction] = {
|
): Option[DamageInteraction] = {
|
||||||
if (!projectiles(index).contains(projectile)) {
|
if (!projectiles(index).contains(projectile)) {
|
||||||
log.error(s"expected projectile could not be found at $index; can not resolve")
|
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
|
* @return a copy of the projectile
|
||||||
*/
|
*/
|
||||||
def ResolveProjectileInteraction(
|
def ResolveProjectileInteraction(
|
||||||
projectile: Projectile,
|
projectile: Projectile,
|
||||||
resolution: DamageResolution.Value,
|
resolution: DamageResolution.Value,
|
||||||
target: PlanetSideGameObject with FactionAffinity with Vitality,
|
target: PlanetSideGameObject with FactionAffinity with Vitality,
|
||||||
pos: Vector3
|
pos: Vector3
|
||||||
): Option[DamageInteraction] = {
|
): Option[DamageInteraction] = {
|
||||||
if (projectile.isMiss) {
|
if (projectile.isMiss) {
|
||||||
log.warn("expected projectile was already counted as a missed shot; can not resolve any further")
|
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 {
|
} else {
|
||||||
player.Find(tool) match {
|
player.Find(tool) match {
|
||||||
case Some(newIndex) =>
|
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
|
player.Slot(newIndex).Equipment = None
|
||||||
true
|
true
|
||||||
case None =>
|
case None =>
|
||||||
|
|
@ -8430,16 +8498,17 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
val events = continent.AvatarEvents
|
val events = continent.AvatarEvents
|
||||||
val zoneId = continent.id
|
val zoneId = continent.id
|
||||||
(player.Inventory.Items ++ player.HolsterItems())
|
(player.Inventory.Items ++ player.HolsterItems())
|
||||||
.collect { case InventoryItem(obj: BoomerTrigger, index) =>
|
.collect {
|
||||||
player.Slot(index).Equipment = None
|
case InventoryItem(obj: BoomerTrigger, index) =>
|
||||||
sendResponse(ObjectDeleteMessage(obj.GUID, 0))
|
player.Slot(index).Equipment = None
|
||||||
if (player.HasGUID && player.VisibleSlots.contains(index)) {
|
sendResponse(ObjectDeleteMessage(obj.GUID, 0))
|
||||||
events ! AvatarServiceMessage(
|
if (player.HasGUID && player.VisibleSlots.contains(index)) {
|
||||||
zoneId,
|
events ! AvatarServiceMessage(
|
||||||
AvatarAction.ObjectDelete(player.GUID, obj.GUID)
|
zoneId,
|
||||||
)
|
AvatarAction.ObjectDelete(player.GUID, obj.GUID)
|
||||||
}
|
)
|
||||||
obj
|
}
|
||||||
|
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}"
|
val msg: String = s"${player.Name} is driving a ${vehicle.Definition.Name}"
|
||||||
log.info(msg)
|
log.info(msg)
|
||||||
log.debug(s"LoadZoneInVehicleAsDriver: $msg")
|
log.debug(s"LoadZoneInVehicleAsDriver: $msg")
|
||||||
val manifest = vehicle.PrepareGatingManifest()
|
val manifest = vehicle.PrepareGatingManifest()
|
||||||
val pguid = player.GUID
|
val pguid = player.GUID
|
||||||
val toChannel = manifest.file
|
val toChannel = manifest.file
|
||||||
val topLevel = interstellarFerryTopLevelGUID.getOrElse(vehicle.GUID)
|
val topLevel = interstellarFerryTopLevelGUID.getOrElse(vehicle.GUID)
|
||||||
|
|
@ -8634,7 +8703,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
manifest.cargo.foreach {
|
manifest.cargo.foreach {
|
||||||
case ManifestPassengerEntry("MISSING_DRIVER", index) =>
|
case ManifestPassengerEntry("MISSING_DRIVER", index) =>
|
||||||
val cargo = vehicle.CargoHolds(index).occupant.get
|
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)
|
CargoBehavior.HandleVehicleCargoDismount(cargo.GUID, cargo, vehicle.GUID, vehicle, false, false, true)
|
||||||
case entry =>
|
case entry =>
|
||||||
val cargo = vehicle.CargoHolds(entry.mount).occupant.get
|
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 =>
|
case hold if hold.isOccupied =>
|
||||||
val cargo = hold.occupant.get
|
val cargo = hold.occupant.get
|
||||||
cargo.Continent = toZoneId
|
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(
|
// galaxyService ! GalaxyServiceMessage(
|
||||||
// toChannel,
|
// toChannel,
|
||||||
// GalaxyAction.TransferPassenger(player_guid, toChannel, vehicle, topLevel, manifest)
|
// GalaxyAction.TransferPassenger(player_guid, toChannel, vehicle, topLevel, manifest)
|
||||||
// )
|
// )
|
||||||
}
|
}
|
||||||
case None =>
|
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"
|
s"WeaponFireMessage: ${player.Name}'s ${projectile_info.Name} is a remote projectile"
|
||||||
)
|
)
|
||||||
continent.tasks ! (if (projectile.HasGUID) {
|
continent.tasks ! (if (projectile.HasGUID) {
|
||||||
continent.AvatarEvents ! AvatarServiceMessage(
|
continent.AvatarEvents ! AvatarServiceMessage(
|
||||||
continent.id,
|
continent.id,
|
||||||
AvatarAction.ProjectileExplodes(
|
AvatarAction.ProjectileExplodes(
|
||||||
player.GUID,
|
player.GUID,
|
||||||
projectile.GUID,
|
projectile.GUID,
|
||||||
projectile
|
projectile
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
ReregisterProjectile(projectile)
|
ReregisterProjectile(projectile)
|
||||||
} else {
|
} else {
|
||||||
RegisterProjectile(projectile)
|
RegisterProjectile(projectile)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
projectilesToCleanUp(projectileIndex) = false
|
projectilesToCleanUp(projectileIndex) = false
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -12,23 +12,44 @@ object ControlPacketOpcode extends Enumeration {
|
||||||
type Type = Value
|
type Type = Value
|
||||||
val
|
val
|
||||||
// OPCODES 0x00-0f
|
// OPCODES 0x00-0f
|
||||||
HandleGamePacket, // a whoopsi case: not actually a control packet, but a game packet
|
HandleGamePacket, // a whoopsi case: not actually a control packet, but a game packet
|
||||||
ClientStart, // first packet ever sent during client connection
|
ClientStart, // first packet ever sent during client connection
|
||||||
ServerStart, // second packet sent in response to ClientStart
|
ServerStart, // second packet sent in response to ClientStart
|
||||||
MultiPacket, // used to send multiple packets with one UDP message (subpackets limited to <= 255)
|
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
|
Unknown4, //
|
||||||
|
TeardownConnection, //
|
||||||
|
Unknown6, //
|
||||||
|
ControlSync, // sent to the server from the client
|
||||||
// 0x08
|
// 0x08
|
||||||
ControlSyncResp, // the response generated by the server
|
ControlSyncResp, // the response generated by the server
|
||||||
SlottedMetaPacket0, SlottedMetaPacket1, SlottedMetaPacket2, SlottedMetaPacket3, SlottedMetaPacket4,
|
SlottedMetaPacket0, //
|
||||||
SlottedMetaPacket5, SlottedMetaPacket6,
|
SlottedMetaPacket1, //
|
||||||
|
SlottedMetaPacket2, //
|
||||||
|
SlottedMetaPacket3, //
|
||||||
|
SlottedMetaPacket4, //
|
||||||
|
SlottedMetaPacket5, //
|
||||||
|
SlottedMetaPacket6, //
|
||||||
// OPCODES 0x10-1f
|
// OPCODES 0x10-1f
|
||||||
SlottedMetaPacket7, RelatedA0, RelatedA1, RelatedA2, RelatedA3, RelatedB0, RelatedB1, RelatedB2,
|
SlottedMetaPacket7, //
|
||||||
|
RelatedA0, //
|
||||||
|
RelatedA1, //
|
||||||
|
RelatedA2, //
|
||||||
|
RelatedA3, //
|
||||||
|
RelatedB0, //
|
||||||
|
RelatedB1, //
|
||||||
|
RelatedB2, //
|
||||||
// 0x18
|
// 0x18
|
||||||
RelatedB3, MultiPacketEx, // same as MultiPacket, but with the ability to send extended length packets
|
RelatedB3, //
|
||||||
Unknown26, Unknown27, Unknown28, ConnectionClose, Unknown30 = Value
|
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) =
|
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(
|
def getPacketDecoder(
|
||||||
opcode: ControlPacketOpcode.Type
|
opcode: ControlPacketOpcode.Type
|
||||||
|
|
@ -69,7 +90,7 @@ object ControlPacketOpcode extends Enumeration {
|
||||||
case 0x1b => noDecoder(Unknown27)
|
case 0x1b => noDecoder(Unknown27)
|
||||||
case 0x1c => noDecoder(Unknown28)
|
case 0x1c => noDecoder(Unknown28)
|
||||||
case 0x1d => control.ConnectionClose.decode
|
case 0x1d => control.ConnectionClose.decode
|
||||||
case 0x1e => noDecoder(Unknown30)
|
case 0x1e => control.Unknown30.decode
|
||||||
case _ => noDecoder(opcode)
|
case _ => noDecoder(opcode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,284 +18,284 @@ object GamePacketOpcode extends Enumeration {
|
||||||
type Type = Value
|
type Type = Value
|
||||||
val
|
val
|
||||||
// OPCODES 0x00-0f
|
// OPCODES 0x00-0f
|
||||||
Unknown0, // PPT_NULL in beta client
|
Unknown0, // PPT_NULL in beta client
|
||||||
LoginMessage,
|
LoginMessage, //
|
||||||
LoginRespMessage,
|
LoginRespMessage, //
|
||||||
ConnectToWorldRequestMessage, // found by searching for 83 F8 03 89 in IDA
|
ConnectToWorldRequestMessage, // found by searching for 83 F8 03 89 in IDA
|
||||||
ConnectToWorldMessage,
|
ConnectToWorldMessage, //
|
||||||
VNLWorldStatusMessage,
|
VNLWorldStatusMessage, //
|
||||||
UnknownMessage6, // PPT_TRANSFERTOWORLDREQUEST
|
UnknownMessage6, // PPT_TRANSFERTOWORLDREQUEST
|
||||||
UnknownMessage7, // PPT_TRANSFERTOWORLDRESPONSE
|
UnknownMessage7, // PPT_TRANSFERTOWORLDRESPONSE
|
||||||
// 0x08
|
// 0x08
|
||||||
PlayerStateMessage,
|
PlayerStateMessage, //
|
||||||
HitMessage,
|
HitMessage, //
|
||||||
HitHint,
|
HitHint, //
|
||||||
DamageMessage,
|
DamageMessage, //
|
||||||
DestroyMessage,
|
DestroyMessage, //
|
||||||
ReloadMessage,
|
ReloadMessage, //
|
||||||
MountVehicleMsg,
|
MountVehicleMsg, //
|
||||||
DismountVehicleMsg,
|
DismountVehicleMsg, //
|
||||||
// OPCODES 0x10-1f
|
// OPCODES 0x10-1f
|
||||||
UseItemMessage,
|
UseItemMessage, //
|
||||||
MoveItemMessage,
|
MoveItemMessage, //
|
||||||
ChatMsg,
|
ChatMsg, //
|
||||||
CharacterNoRecordMessage,
|
CharacterNoRecordMessage, //
|
||||||
CharacterInfoMessage,
|
CharacterInfoMessage, //
|
||||||
UnknownMessage21, // PPT_DISCONNECT
|
UnknownMessage21, // PPT_DISCONNECT
|
||||||
BindPlayerMessage,
|
BindPlayerMessage, //
|
||||||
ObjectCreateMessage_Duplicate, // PPT_OBJECTCREATE
|
ObjectCreateMessage_Duplicate, // PPT_OBJECTCREATE
|
||||||
// 0x18
|
// 0x18
|
||||||
ObjectCreateMessage, // PPT_OBJECTCREATEDETAILED
|
ObjectCreateMessage, // PPT_OBJECTCREATEDETAILED
|
||||||
ObjectDeleteMessage,
|
ObjectDeleteMessage, //
|
||||||
PingMsg,
|
PingMsg, //
|
||||||
VehicleStateMessage,
|
VehicleStateMessage, //
|
||||||
FrameVehicleStateMessage,
|
FrameVehicleStateMessage, //
|
||||||
GenericObjectStateMsg,
|
GenericObjectStateMsg, //
|
||||||
ChildObjectStateMessage,
|
ChildObjectStateMessage, //
|
||||||
ActionResultMessage,
|
ActionResultMessage, //
|
||||||
// OPCODES 0x20-2f
|
// OPCODES 0x20-2f
|
||||||
UnknownMessage32, // PPT_ACTIONBEGIN
|
UnknownMessage32, // PPT_ACTIONBEGIN
|
||||||
ActionProgressMessage,
|
ActionProgressMessage, //
|
||||||
ActionCancelMessage,
|
ActionCancelMessage, //
|
||||||
ActionCancelAcknowledgeMessage,
|
ActionCancelAcknowledgeMessage, //
|
||||||
SetEmpireMessage,
|
SetEmpireMessage, //
|
||||||
EmoteMsg,
|
EmoteMsg, //
|
||||||
UnuseItemMessage,
|
UnuseItemMessage, //
|
||||||
ObjectDetachMessage,
|
ObjectDetachMessage, //
|
||||||
// 0x28
|
// 0x28
|
||||||
CreateShortcutMessage,
|
CreateShortcutMessage, //
|
||||||
ChangeShortcutBankMessage,
|
ChangeShortcutBankMessage, //
|
||||||
ObjectAttachMessage,
|
ObjectAttachMessage, //
|
||||||
UnknownMessage43, // PPT_OBJECTEMPTY
|
UnknownMessage43, // PPT_OBJECTEMPTY
|
||||||
PlanetsideAttributeMessage,
|
PlanetsideAttributeMessage, //
|
||||||
RequestDestroyMessage,
|
RequestDestroyMessage, //
|
||||||
UnknownMessage46, // PPT_EQUIPITEM
|
UnknownMessage46, // PPT_EQUIPITEM
|
||||||
CharacterCreateRequestMessage,
|
CharacterCreateRequestMessage, //
|
||||||
// OPCODES 0x30-3f
|
// OPCODES 0x30-3f
|
||||||
CharacterRequestMessage,
|
CharacterRequestMessage, //
|
||||||
LoadMapMessage,
|
LoadMapMessage, //
|
||||||
SetCurrentAvatarMessage,
|
SetCurrentAvatarMessage, //
|
||||||
ObjectHeldMessage,
|
ObjectHeldMessage, //
|
||||||
WeaponFireMessage,
|
WeaponFireMessage, //
|
||||||
AvatarJumpMessage,
|
AvatarJumpMessage, //
|
||||||
PickupItemMessage,
|
PickupItemMessage, //
|
||||||
DropItemMessage,
|
DropItemMessage, //
|
||||||
// 0x38
|
// 0x38
|
||||||
InventoryStateMessage,
|
InventoryStateMessage, //
|
||||||
ChangeFireStateMessage_Start,
|
ChangeFireStateMessage_Start, //
|
||||||
ChangeFireStateMessage_Stop,
|
ChangeFireStateMessage_Stop, //
|
||||||
UnknownMessage59,
|
UnknownMessage59, //
|
||||||
GenericCollisionMsg,
|
GenericCollisionMsg, //
|
||||||
QuantityUpdateMessage,
|
QuantityUpdateMessage, //
|
||||||
ArmorChangedMessage,
|
ArmorChangedMessage, //
|
||||||
ProjectileStateMessage,
|
ProjectileStateMessage, //
|
||||||
// OPCODES 0x40-4f
|
// OPCODES 0x40-4f
|
||||||
MountVehicleCargoMsg,
|
MountVehicleCargoMsg, //
|
||||||
DismountVehicleCargoMsg,
|
DismountVehicleCargoMsg, //
|
||||||
CargoMountPointStatusMessage,
|
CargoMountPointStatusMessage, //
|
||||||
BeginZoningMessage,
|
BeginZoningMessage, //
|
||||||
ItemTransactionMessage,
|
ItemTransactionMessage, //
|
||||||
ItemTransactionResultMessage,
|
ItemTransactionResultMessage, //
|
||||||
ChangeFireModeMessage,
|
ChangeFireModeMessage, //
|
||||||
ChangeAmmoMessage,
|
ChangeAmmoMessage, //
|
||||||
// 0x48
|
// 0x48
|
||||||
TimeOfDayMessage,
|
TimeOfDayMessage, //
|
||||||
UnknownMessage73, // PPT_PROJECTILE_EVENT_BLOCK
|
UnknownMessage73, // PPT_PROJECTILE_EVENT_BLOCK
|
||||||
SpawnRequestMessage,
|
SpawnRequestMessage, //
|
||||||
DeployRequestMessage,
|
DeployRequestMessage, //
|
||||||
UnknownMessage76, // PPT_BUILDINGSTATECHANGED
|
UnknownMessage76, // PPT_BUILDINGSTATECHANGED
|
||||||
RepairMessage,
|
RepairMessage, //
|
||||||
ServerVehicleOverrideMsg,
|
ServerVehicleOverrideMsg, //
|
||||||
LashMessage,
|
LashMessage,
|
||||||
// OPCODES 0x50-5f
|
// OPCODES 0x50-5f
|
||||||
TargetingInfoMessage,
|
TargetingInfoMessage, //
|
||||||
TriggerEffectMessage,
|
TriggerEffectMessage, //
|
||||||
WeaponDryFireMessage,
|
WeaponDryFireMessage, //
|
||||||
DroppodLaunchRequestMessage,
|
DroppodLaunchRequestMessage, //
|
||||||
HackMessage,
|
HackMessage, //
|
||||||
DroppodLaunchResponseMessage,
|
DroppodLaunchResponseMessage, //
|
||||||
GenericObjectActionMessage,
|
GenericObjectActionMessage, //
|
||||||
AvatarVehicleTimerMessage,
|
AvatarVehicleTimerMessage, //
|
||||||
// 0x58
|
// 0x58
|
||||||
AvatarImplantMessage,
|
AvatarImplantMessage, //
|
||||||
UnknownMessage89, // PPT_SEARCHMESSAGE
|
UnknownMessage89, // PPT_SEARCHMESSAGE
|
||||||
DelayedPathMountMsg,
|
DelayedPathMountMsg, //
|
||||||
OrbitalShuttleTimeMsg,
|
OrbitalShuttleTimeMsg, //
|
||||||
AIDamage,
|
AIDamage, //
|
||||||
DeployObjectMessage,
|
DeployObjectMessage, //
|
||||||
FavoritesRequest,
|
FavoritesRequest, //
|
||||||
FavoritesResponse,
|
FavoritesResponse, //
|
||||||
// OPCODES 0x60-6f
|
// OPCODES 0x60-6f
|
||||||
FavoritesMessage,
|
FavoritesMessage, //
|
||||||
ObjectDetectedMessage,
|
ObjectDetectedMessage, //
|
||||||
SplashHitMessage,
|
SplashHitMessage, //
|
||||||
SetChatFilterMessage,
|
SetChatFilterMessage, //
|
||||||
AvatarSearchCriteriaMessage,
|
AvatarSearchCriteriaMessage, //
|
||||||
AvatarSearchResponse,
|
AvatarSearchResponse, //
|
||||||
WeaponJammedMessage,
|
WeaponJammedMessage, //
|
||||||
LinkDeadAwarenessMsg,
|
LinkDeadAwarenessMsg, //
|
||||||
// 0x68
|
// 0x68
|
||||||
DroppodFreefallingMessage,
|
DroppodFreefallingMessage, //
|
||||||
AvatarFirstTimeEventMessage,
|
AvatarFirstTimeEventMessage, //
|
||||||
AggravatedDamageMessage,
|
AggravatedDamageMessage, //
|
||||||
TriggerSoundMessage,
|
TriggerSoundMessage, //
|
||||||
LootItemMessage,
|
LootItemMessage, //
|
||||||
VehicleSubStateMessage,
|
VehicleSubStateMessage, //
|
||||||
SquadMembershipRequest,
|
SquadMembershipRequest, //
|
||||||
SquadMembershipResponse,
|
SquadMembershipResponse, //
|
||||||
// OPCODES 0x70-7f
|
// OPCODES 0x70-7f
|
||||||
SquadMemberEvent,
|
SquadMemberEvent, //
|
||||||
PlatoonEvent,
|
PlatoonEvent, //
|
||||||
FriendsRequest,
|
FriendsRequest, //
|
||||||
FriendsResponse,
|
FriendsResponse, //
|
||||||
TriggerEnvironmentalDamageMessage,
|
TriggerEnvironmentalDamageMessage, //
|
||||||
TrainingZoneMessage,
|
TrainingZoneMessage, //
|
||||||
DeployableObjectsInfoMessage,
|
DeployableObjectsInfoMessage, //
|
||||||
SquadState,
|
SquadState,
|
||||||
// 0x78
|
// 0x78
|
||||||
OxygenStateMessage,
|
OxygenStateMessage, //
|
||||||
TradeMessage,
|
TradeMessage, //
|
||||||
UnknownMessage122,
|
UnknownMessage122, //
|
||||||
DamageFeedbackMessage,
|
DamageFeedbackMessage, //
|
||||||
DismountBuildingMsg,
|
DismountBuildingMsg, //
|
||||||
UnknownMessage125, // PPT_MOUNTBUILDING
|
UnknownMessage125, // PPT_MOUNTBUILDING
|
||||||
UnknownMessage126, // PPT_INTENDEDDROPZONE
|
UnknownMessage126, // PPT_INTENDEDDROPZONE
|
||||||
AvatarStatisticsMessage,
|
AvatarStatisticsMessage, //
|
||||||
// OPCODES 0x80-8f
|
// OPCODES 0x80-8f
|
||||||
GenericObjectAction2Message,
|
GenericObjectAction2Message, //
|
||||||
DestroyDisplayMessage,
|
DestroyDisplayMessage, //
|
||||||
TriggerBotAction,
|
TriggerBotAction, //
|
||||||
SquadWaypointRequest,
|
SquadWaypointRequest, //
|
||||||
SquadWaypointEvent,
|
SquadWaypointEvent, //
|
||||||
OffshoreVehicleMessage,
|
OffshoreVehicleMessage, //
|
||||||
ObjectDeployedMessage,
|
ObjectDeployedMessage, //
|
||||||
ObjectDeployedCountMessage,
|
ObjectDeployedCountMessage, //
|
||||||
// 0x88
|
// 0x88
|
||||||
WeaponDelayFireMessage,
|
WeaponDelayFireMessage, //
|
||||||
BugReportMessage,
|
BugReportMessage, //
|
||||||
PlayerStasisMessage,
|
PlayerStasisMessage, //
|
||||||
UnknownMessage139,
|
UnknownMessage139, //
|
||||||
OutfitMembershipRequest,
|
OutfitMembershipRequest, //
|
||||||
OutfitMembershipResponse,
|
OutfitMembershipResponse, //
|
||||||
OutfitRequest,
|
OutfitRequest, //
|
||||||
OutfitEvent,
|
OutfitEvent, //
|
||||||
// OPCODES 0x90-9f
|
// OPCODES 0x90-9f
|
||||||
OutfitMemberEvent,
|
OutfitMemberEvent, //
|
||||||
OutfitMemberUpdate,
|
OutfitMemberUpdate, //
|
||||||
PlanetsideStringAttributeMessage,
|
PlanetsideStringAttributeMessage, //
|
||||||
DataChallengeMessage,
|
DataChallengeMessage, //
|
||||||
DataChallengeMessageResp,
|
DataChallengeMessageResp, //
|
||||||
WeatherMessage,
|
WeatherMessage, //
|
||||||
SimDataChallenge,
|
SimDataChallenge, //
|
||||||
SimDataChallengeResp,
|
SimDataChallengeResp, //
|
||||||
// 0x98
|
// 0x98
|
||||||
OutfitListEvent,
|
OutfitListEvent, //
|
||||||
EmpireIncentivesMessage,
|
EmpireIncentivesMessage, //
|
||||||
InvalidTerrainMessage,
|
InvalidTerrainMessage, //
|
||||||
SyncMessage,
|
SyncMessage, //
|
||||||
DebugDrawMessage,
|
DebugDrawMessage, //
|
||||||
SoulMarkMessage,
|
SoulMarkMessage, //
|
||||||
UplinkPositionEvent,
|
UplinkPositionEvent, //
|
||||||
HotSpotUpdateMessage,
|
HotSpotUpdateMessage, //
|
||||||
// OPCODES 0xa0-af
|
// OPCODES 0xa0-af
|
||||||
BuildingInfoUpdateMessage,
|
BuildingInfoUpdateMessage, //
|
||||||
FireHintMessage,
|
FireHintMessage, //
|
||||||
UplinkRequest,
|
UplinkRequest, //
|
||||||
UplinkResponse,
|
UplinkResponse, //
|
||||||
WarpgateRequest,
|
WarpgateRequest, //
|
||||||
WarpgateResponse,
|
WarpgateResponse, //
|
||||||
DamageWithPositionMessage,
|
DamageWithPositionMessage, //
|
||||||
GenericActionMessage,
|
GenericActionMessage, //
|
||||||
// 0xa8
|
// 0xa8
|
||||||
ContinentalLockUpdateMessage,
|
ContinentalLockUpdateMessage, //
|
||||||
AvatarGrenadeStateMessage,
|
AvatarGrenadeStateMessage, //
|
||||||
UnknownMessage170,
|
UnknownMessage170, //
|
||||||
UnknownMessage171,
|
UnknownMessage171, //
|
||||||
ReleaseAvatarRequestMessage,
|
ReleaseAvatarRequestMessage, //
|
||||||
AvatarDeadStateMessage,
|
AvatarDeadStateMessage, //
|
||||||
CSAssistMessage,
|
CSAssistMessage, //
|
||||||
CSAssistCommentMessage,
|
CSAssistCommentMessage, //
|
||||||
// OPCODES 0xb0-bf
|
// OPCODES 0xb0-bf
|
||||||
VoiceHostRequest,
|
VoiceHostRequest, //
|
||||||
VoiceHostKill,
|
VoiceHostKill, //
|
||||||
VoiceHostInfo,
|
VoiceHostInfo, //
|
||||||
BattleplanMessage,
|
BattleplanMessage, //
|
||||||
BattleExperienceMessage,
|
BattleExperienceMessage, //
|
||||||
TargetingImplantRequest,
|
TargetingImplantRequest, //
|
||||||
ZonePopulationUpdateMessage,
|
ZonePopulationUpdateMessage, //
|
||||||
DisconnectMessage,
|
DisconnectMessage, //
|
||||||
// 0xb8
|
// 0xb8
|
||||||
ExperienceAddedMessage,
|
ExperienceAddedMessage, //
|
||||||
OrbitalStrikeWaypointMessage,
|
OrbitalStrikeWaypointMessage, //
|
||||||
KeepAliveMessage,
|
KeepAliveMessage, //
|
||||||
MapObjectStateBlockMessage,
|
MapObjectStateBlockMessage, //
|
||||||
SnoopMsg,
|
SnoopMsg, //
|
||||||
PlayerStateMessageUpstream,
|
PlayerStateMessageUpstream, //
|
||||||
PlayerStateShiftMessage,
|
PlayerStateShiftMessage, //
|
||||||
ZipLineMessage,
|
ZipLineMessage, //
|
||||||
// OPCODES 0xc0-cf
|
// OPCODES 0xc0-cf
|
||||||
CaptureFlagUpdateMessage,
|
CaptureFlagUpdateMessage, //
|
||||||
VanuModuleUpdateMessage,
|
VanuModuleUpdateMessage, //
|
||||||
FacilityBenefitShieldChargeRequestMessage,
|
FacilityBenefitShieldChargeRequestMessage, //
|
||||||
ProximityTerminalUseMessage,
|
ProximityTerminalUseMessage, //
|
||||||
QuantityDeltaUpdateMessage,
|
QuantityDeltaUpdateMessage, //
|
||||||
ChainLashMessage,
|
ChainLashMessage, //
|
||||||
ZoneInfoMessage,
|
ZoneInfoMessage, //
|
||||||
LongRangeProjectileInfoMessage,
|
LongRangeProjectileInfoMessage, //
|
||||||
// 0xc8
|
// 0xc8
|
||||||
WeaponLazeTargetPositionMessage,
|
WeaponLazeTargetPositionMessage, //
|
||||||
ModuleLimitsMessage,
|
ModuleLimitsMessage, //
|
||||||
OutfitBenefitMessage,
|
OutfitBenefitMessage, //
|
||||||
EmpireChangeTimeMessage,
|
EmpireChangeTimeMessage, //
|
||||||
ClockCalibrationMessage,
|
ClockCalibrationMessage, //
|
||||||
DensityLevelUpdateMessage,
|
DensityLevelUpdateMessage, //
|
||||||
ActOfGodMessage,
|
ActOfGodMessage, //
|
||||||
AvatarAwardMessage,
|
AvatarAwardMessage, //
|
||||||
// OPCODES 0xd0-df
|
// OPCODES 0xd0-df
|
||||||
UnknownMessage208,
|
UnknownMessage208, //
|
||||||
DisplayedAwardMessage,
|
DisplayedAwardMessage, //
|
||||||
RespawnAMSInfoMessage,
|
RespawnAMSInfoMessage, //
|
||||||
ComponentDamageMessage,
|
ComponentDamageMessage, //
|
||||||
GenericObjectActionAtPositionMessage,
|
GenericObjectActionAtPositionMessage, //
|
||||||
PropertyOverrideMessage,
|
PropertyOverrideMessage, //
|
||||||
WarpgateLinkOverrideMessage,
|
WarpgateLinkOverrideMessage, //
|
||||||
EmpireBenefitsMessage,
|
EmpireBenefitsMessage, //
|
||||||
// 0xd8
|
// 0xd8
|
||||||
ForceEmpireMessage,
|
ForceEmpireMessage, //
|
||||||
BroadcastWarpgateUpdateMessage,
|
BroadcastWarpgateUpdateMessage, //
|
||||||
UnknownMessage218,
|
UnknownMessage218, //
|
||||||
SquadMainTerminalMessage,
|
SquadMainTerminalMessage, //
|
||||||
SquadMainTerminalResponseMessage,
|
SquadMainTerminalResponseMessage, //
|
||||||
SquadOrderMessage,
|
SquadOrderMessage, //
|
||||||
SquadOrderResponse,
|
SquadOrderResponse, //
|
||||||
ZoneLockInfoMessage,
|
ZoneLockInfoMessage, //
|
||||||
// OPCODES 0xe0-ef
|
// OPCODES 0xe0-ef
|
||||||
SquadBindInfoMessage,
|
SquadBindInfoMessage, //
|
||||||
AudioSequenceMessage,
|
AudioSequenceMessage, //
|
||||||
SquadFacilityBindInfoMessage,
|
SquadFacilityBindInfoMessage, //
|
||||||
ZoneForcedCavernConnectionsMessage,
|
ZoneForcedCavernConnectionsMessage, //
|
||||||
MissionActionMessage,
|
MissionActionMessage, //
|
||||||
MissionKillTriggerMessage,
|
MissionKillTriggerMessage, //
|
||||||
ReplicationStreamMessage,
|
ReplicationStreamMessage, //
|
||||||
SquadDefinitionActionMessage,
|
SquadDefinitionActionMessage, //
|
||||||
// 0xe8
|
// 0xe8
|
||||||
SquadDetailDefinitionUpdateMessage,
|
SquadDetailDefinitionUpdateMessage, //
|
||||||
TacticsMessage,
|
TacticsMessage, //
|
||||||
RabbitUpdateMessage,
|
RabbitUpdateMessage, //
|
||||||
SquadInvitationRequestMessage,
|
SquadInvitationRequestMessage, //
|
||||||
CharacterKnowledgeMessage,
|
CharacterKnowledgeMessage, //
|
||||||
GameScoreUpdateMessage,
|
GameScoreUpdateMessage, //
|
||||||
UnknownMessage238,
|
UnknownMessage238, //
|
||||||
OrderTerminalBugMessage,
|
OrderTerminalBugMessage, //
|
||||||
// OPCODES 0xf0-f3
|
// OPCODES 0xf0-f3
|
||||||
QueueTimedHelpMessage,
|
QueueTimedHelpMessage, //
|
||||||
MailMessage,
|
MailMessage, //
|
||||||
GameVarUpdate,
|
GameVarUpdate, //
|
||||||
ClientCheatedMessage // last known message type (243, 0xf3)
|
ClientCheatedMessage // last known message type (243, 0xf3)
|
||||||
= Value
|
= Value
|
||||||
|
|
||||||
private def noDecoder(opcode: GamePacketOpcode.Type) =
|
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
|
/// 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
|
/// compiler will be able to optimize this as a lookup table (switch statement). Microbenchmarks show a nearly 400x
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ object PacketType extends Enumeration(1) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** PlanetSide packet flags (beginning of most packets) */
|
/** 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]] */
|
/** Codec for [[PlanetSidePacketFlags]] */
|
||||||
object PlanetSidePacketFlags extends Marshallable[PlanetSidePacketFlags] {
|
object PlanetSidePacketFlags extends Marshallable[PlanetSidePacketFlags] {
|
||||||
|
|
@ -71,7 +71,8 @@ object PlanetSidePacketFlags extends Marshallable[PlanetSidePacketFlags] {
|
||||||
("packet_type" | PacketType.codec) :: // first 4-bits
|
("packet_type" | PacketType.codec) :: // first 4-bits
|
||||||
("unused" | constant(bin"0")) ::
|
("unused" | constant(bin"0")) ::
|
||||||
("secured" | bool) ::
|
("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
|
("length_specified" | constant(bin"0")) // we DO NOT support this field
|
||||||
).as[PlanetSidePacketFlags]
|
).as[PlanetSidePacketFlags]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ object PacketCoding {
|
||||||
case Some(_sequence) =>
|
case Some(_sequence) =>
|
||||||
uint16L.encode(_sequence) match {
|
uint16L.encode(_sequence) match {
|
||||||
case Successful(_seq) => _seq
|
case Successful(_seq) => _seq
|
||||||
case f @ Failure(_) => return f
|
case f @ Failure(_) => return f
|
||||||
}
|
}
|
||||||
case None =>
|
case None =>
|
||||||
return Failure(Err(s"Missing sequence"))
|
return Failure(Err(s"Missing sequence"))
|
||||||
|
|
@ -125,7 +125,7 @@ object PacketCoding {
|
||||||
case Successful(opcode) => Successful(opcode)
|
case Successful(opcode) => Successful(opcode)
|
||||||
case f @ Failure(_) => f
|
case f @ Failure(_) => f
|
||||||
}
|
}
|
||||||
|
|
||||||
case _ =>
|
case _ =>
|
||||||
Failure(Err("packet not supported"))
|
Failure(Err("packet not supported"))
|
||||||
}
|
}
|
||||||
|
|
@ -174,7 +174,7 @@ object PacketCoding {
|
||||||
): Attempt[(PlanetSidePacket, Int)] = {
|
): Attempt[(PlanetSidePacket, Int)] = {
|
||||||
val (flags, remainder) = Codec.decode[PlanetSidePacketFlags](BitVector(msg)) match {
|
val (flags, remainder) = Codec.decode[PlanetSidePacketFlags](BitVector(msg)) match {
|
||||||
case Successful(DecodeResult(value, _remainder)) => (value, _remainder)
|
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 {
|
flags.packetType match {
|
||||||
|
|
@ -218,8 +218,8 @@ object PacketCoding {
|
||||||
case (PacketType.ResetSequence, Some(_crypto)) =>
|
case (PacketType.ResetSequence, Some(_crypto)) =>
|
||||||
_crypto.decrypt(payload.drop(1)) match {
|
_crypto.decrypt(payload.drop(1)) match {
|
||||||
case Successful(p) if p == hex"01" => Successful((ResetSequence(), sequence))
|
case Successful(p) if p == hex"01" => Successful((ResetSequence(), sequence))
|
||||||
case Successful(p) => Failure(Err(s"ResetSequence decrypted to unsupported value - $p"))
|
case Successful(p) => Failure(Err(s"ResetSequence decrypted to unsupported value - $p"))
|
||||||
case _ => Failure(Err(s"ResetSequence did not decrypt properly"))
|
case _ => Failure(Err(s"ResetSequence did not decrypt properly"))
|
||||||
}
|
}
|
||||||
case (ptype, _) =>
|
case (ptype, _) =>
|
||||||
Failure(Err(s"Cannot unmarshal $ptype packet at all"))
|
Failure(Err(s"Cannot unmarshal $ptype packet at all"))
|
||||||
|
|
@ -238,8 +238,7 @@ object PacketCoding {
|
||||||
def decodePacket(msg: ByteVector): Attempt[PlanetSidePacket] = {
|
def decodePacket(msg: ByteVector): Attempt[PlanetSidePacket] = {
|
||||||
if (msg.length < PLANETSIDE_MIN_PACKET_SIZE)
|
if (msg.length < PLANETSIDE_MIN_PACKET_SIZE)
|
||||||
return Failure(Err(s"Packet does not meet the minimum length of $PLANETSIDE_MIN_PACKET_SIZE bytes"))
|
return Failure(Err(s"Packet does not meet the minimum length of $PLANETSIDE_MIN_PACKET_SIZE bytes"))
|
||||||
val firstByte = msg { 0 }
|
msg(0) match {
|
||||||
firstByte match {
|
|
||||||
case 0x00 =>
|
case 0x00 =>
|
||||||
// control packets don't need the first byte
|
// control packets don't need the first byte
|
||||||
ControlPacketOpcode.codec.decode(msg.drop(1).bits) match {
|
ControlPacketOpcode.codec.decode(msg.drop(1).bits) match {
|
||||||
|
|
@ -303,7 +302,7 @@ object PacketCoding {
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
case e: Throwable =>
|
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}"))
|
Failure(Err(s"encrypt error: '$msg' data: ${packetWithPadding.toHex}"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -321,14 +320,14 @@ object PacketCoding {
|
||||||
// last byte is the padding length
|
// last byte is the padding length
|
||||||
val padding = uint8L.decode(payloadDecrypted.takeRight(1).bits) match {
|
val padding = uint8L.decode(payloadDecrypted.takeRight(1).bits) match {
|
||||||
case Successful(_padding) => _padding.value
|
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 payloadNoPadding = payloadDecrypted.dropRight(1 + padding)
|
||||||
val payloadMac = payloadNoPadding.takeRight(Md5Mac.MACLENGTH)
|
val payloadMac = payloadNoPadding.takeRight(Md5Mac.MACLENGTH)
|
||||||
|
|
||||||
val mac = bytes(Md5Mac.MACLENGTH).decode(payloadMac.bits) match {
|
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
|
case Successful(_mac) => _mac.value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
18
src/main/scala/net/psforever/packet/control/Unknown30.scala
Normal file
18
src/main/scala/net/psforever/packet/control/Unknown30.scala
Normal 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]
|
||||||
|
}
|
||||||
|
|
@ -63,8 +63,9 @@ class ChatService(context: ActorContext[ChatService.Command]) extends AbstractBe
|
||||||
|
|
||||||
case Message(session, message, channel) =>
|
case Message(session, message, channel) =>
|
||||||
(channel, message.messageType) match {
|
(channel, message.messageType) match {
|
||||||
case (ChatChannel.Squad(_), CMT_SQUAD) => ;
|
case (ChatChannel.Squad(_), CMT_SQUAD) => ()
|
||||||
case (ChatChannel.Default(), messageType) if messageType != CMT_SQUAD => ;
|
case (ChatChannel.Squad(_), CMT_VOICE) if message.contents.startsWith("SH") => ()
|
||||||
|
case (ChatChannel.Default(), messageType) if messageType != CMT_SQUAD => ()
|
||||||
case _ =>
|
case _ =>
|
||||||
log.error(s"invalid chat channel $channel for messageType ${message.messageType}")
|
log.error(s"invalid chat channel $channel for messageType ${message.messageType}")
|
||||||
return this
|
return this
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,14 @@ import net.psforever.types.{PlanetSideGUID, Vector3}
|
||||||
import scala.concurrent.duration.{Duration, _}
|
import scala.concurrent.duration.{Duration, _}
|
||||||
|
|
||||||
class LocalService(zone: Zone) extends Actor {
|
class LocalService(zone: Zone) extends Actor {
|
||||||
private val doorCloser = context.actorOf(Props[DoorCloseActor](), s"${zone.id}-local-door-closer")
|
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 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 hackCapturer =
|
||||||
private val captureFlagManager = context.actorOf(Props(classOf[CaptureFlagManager], zone.tasks, zone), s"${zone.id}-local-capture-flag-manager")
|
context.actorOf(Props(classOf[HackCaptureActor], zone.tasks), s"${zone.id}-local-hack-capturer")
|
||||||
private val engineer = context.actorOf(Props(classOf[DeployableRemover], zone.tasks), s"${zone.id}-deployable-remover-agent")
|
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 =
|
private val teleportDeployment: ActorRef =
|
||||||
context.actorOf(Props[RouterTelepadActivation](), s"${zone.id}-telepad-activate-agent")
|
context.actorOf(Props[RouterTelepadActivation](), s"${zone.id}-telepad-activate-agent")
|
||||||
private[this] val log = org.log4s.getLogger
|
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) =>
|
case LocalAction.HackClear(player_guid, target, unk1, unk2) =>
|
||||||
LocalEvents.publish(
|
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) =>
|
case LocalAction.HackTemporarily(player_guid, _, target, unk1, duration, unk2) =>
|
||||||
hackClearer ! HackClearActor.ObjectIsHacked(target, zone, unk1, unk2, duration)
|
hackClearer ! HackClearActor.ObjectIsHacked(target, zone, unk1, unk2, duration)
|
||||||
|
|
@ -281,6 +288,13 @@ class LocalService(zone: Zone) extends Actor {
|
||||||
//response from HackClearActor
|
//response from HackClearActor
|
||||||
case HackClearActor.SendHackMessageHackCleared(target_guid, _, unk1, unk2) =>
|
case HackClearActor.SendHackMessageHackCleared(target_guid, _, unk1, unk2) =>
|
||||||
log.info(s"Clearing hack for $target_guid")
|
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
|
//message from ProximityTerminalControl
|
||||||
case Terminal.StartProximityEffect(terminal) =>
|
case Terminal.StartProximityEffect(terminal) =>
|
||||||
|
|
@ -418,13 +432,9 @@ class LocalService(zone: Zone) extends Actor {
|
||||||
sender() ! Vitality.DamageResolution(target, cause)
|
sender() ! Vitality.DamageResolution(target, cause)
|
||||||
|
|
||||||
// Forward all CaptureFlagManager messages
|
// Forward all CaptureFlagManager messages
|
||||||
case msg @
|
case msg @ (CaptureFlagManager.SpawnCaptureFlag(_, _, _) | CaptureFlagManager.PickupFlag(_, _) |
|
||||||
(CaptureFlagManager.SpawnCaptureFlag(_, _, _)
|
CaptureFlagManager.DropFlag(_) | CaptureFlagManager.Captured(_) | CaptureFlagManager.Lost(_, _) |
|
||||||
| CaptureFlagManager.PickupFlag(_, _)
|
CaptureFlagManager) =>
|
||||||
| CaptureFlagManager.DropFlag(_)
|
|
||||||
| CaptureFlagManager.Captured(_)
|
|
||||||
| CaptureFlagManager.Lost(_, _)
|
|
||||||
| CaptureFlagManager) =>
|
|
||||||
captureFlagManager.forward(msg)
|
captureFlagManager.forward(msg)
|
||||||
|
|
||||||
case msg =>
|
case msg =>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue