Merge branch 'master' into cooldown-reset

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

View file

@ -40,16 +40,16 @@ lazy val psforeverSettings = Seq(
classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.Flat, 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",

View file

@ -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()
}
})
} }
} }

View file

@ -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(

View file

@ -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)
} }

View file

@ -60,7 +60,12 @@ import net.psforever.services.local.support.{CaptureFlagManager, HackCaptureActo
import net.psforever.services.local.{LocalAction, LocalResponse, LocalServiceMessage, LocalServiceResponse} import net.psforever.services.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

View file

@ -20,7 +20,12 @@ import net.psforever.objects.serverobject.painbox.PainboxDefinition
import net.psforever.objects.serverobject.terminals._ import net.psforever.objects.serverobject.terminals._
import net.psforever.objects.serverobject.tube.SpawnTubeDefinition import net.psforever.objects.serverobject.tube.SpawnTubeDefinition
import net.psforever.objects.serverobject.resourcesilo.ResourceSiloDefinition import net.psforever.objects.serverobject.resourcesilo.ResourceSiloDefinition
import net.psforever.objects.serverobject.structures.{AmenityDefinition, AutoRepairStats, BuildingDefinition, WarpGateDefinition} import net.psforever.objects.serverobject.structures.{
AmenityDefinition,
AutoRepairStats,
BuildingDefinition,
WarpGateDefinition
}
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminalDefinition import net.psforever.objects.serverobject.terminals.capture.CaptureTerminalDefinition
import net.psforever.objects.serverobject.terminals.implant.{ImplantTerminalDefinition, ImplantTerminalMechDefinition} import net.psforever.objects.serverobject.terminals.implant.{ImplantTerminalDefinition, ImplantTerminalMechDefinition}
import net.psforever.objects.serverobject.turret.{FacilityTurretDefinition, TurretUpgrade} import net.psforever.objects.serverobject.turret.{FacilityTurretDefinition, TurretUpgrade}
@ -1105,7 +1110,7 @@ object GlobalDefinitions {
val generator = new GeneratorDefinition(351) val generator = new GeneratorDefinition(351)
val obbasemesh = new AmenityDefinition(598) { } val obbasemesh = new AmenityDefinition(598) {}
initMiscellaneous() initMiscellaneous()
/* /*
@ -3742,7 +3747,7 @@ object GlobalDefinitions {
ProjectileDefinition.CalculateDerivedFields(quasar_projectile) ProjectileDefinition.CalculateDerivedFields(quasar_projectile)
radiator_grenade_projectile.Name = "radiator_grenade_projectile" // Todo : Radiator damages ? radiator_grenade_projectile.Name = "radiator_grenade_projectile" // Todo : Radiator damages ?
radiator_grenade_projectile.GrenadeProjectile = true //not really, but technically yes radiator_grenade_projectile.GrenadeProjectile = true //not really, but technically yes
radiator_grenade_projectile.ProjectileDamageType = DamageType.Direct radiator_grenade_projectile.ProjectileDamageType = DamageType.Direct
radiator_grenade_projectile.InitialVelocity = 30 radiator_grenade_projectile.InitialVelocity = 30
radiator_grenade_projectile.Lifespan = 3f radiator_grenade_projectile.Lifespan = 3f
@ -4751,7 +4756,7 @@ object GlobalDefinitions {
pellet_gun.FireModes.head.AmmoTypeIndices += 0 pellet_gun.FireModes.head.AmmoTypeIndices += 0
pellet_gun.FireModes.head.AmmoSlotIndex = 0 pellet_gun.FireModes.head.AmmoSlotIndex = 0
pellet_gun.FireModes.head.Magazine = 1 //what is this? pellet_gun.FireModes.head.Magazine = 1 //what is this?
pellet_gun.FireModes.head.Chamber = 8 //1 shell * 8 pellets = 8 pellet_gun.FireModes.head.Chamber = 8 //1 shell * 8 pellets = 8
pellet_gun.Tile = InventoryTile.Tile63 pellet_gun.Tile = InventoryTile.Tile63
six_shooter.Name = "six_shooter" six_shooter.Name = "six_shooter"
@ -5619,9 +5624,9 @@ object GlobalDefinitions {
* Initialize `VehicleDefinition` globals. * Initialize `VehicleDefinition` globals.
*/ */
private def init_vehicles(): Unit = { private def init_vehicles(): Unit = {
val atvForm = GeometryForm.representByCylinder(radius = 1.1797f, height = 1.1875f) _ val atvForm = GeometryForm.representByCylinder(radius = 1.1797f, height = 1.1875f) _
val delivererForm = GeometryForm.representByCylinder(radius = 2.46095f, height = 2.40626f) _ //TODO hexahedron val delivererForm = GeometryForm.representByCylinder(radius = 2.46095f, height = 2.40626f) _ //TODO hexahedron
val apcForm = GeometryForm.representByCylinder(radius = 4.6211f, height = 3.90626f) _ //TODO hexahedron val apcForm = GeometryForm.representByCylinder(radius = 4.6211f, height = 3.90626f) _ //TODO hexahedron
val liberatorForm = GeometryForm.representByCylinder(radius = 3.74615f, height = 2.51563f) _ val liberatorForm = GeometryForm.representByCylinder(radius = 3.74615f, height = 2.51563f) _
val bailableSeat = new SeatDefinition() { val bailableSeat = new SeatDefinition() {
@ -5637,11 +5642,11 @@ object GlobalDefinitions {
fury.Repairable = true fury.Repairable = true
fury.RepairIfDestroyed = false fury.RepairIfDestroyed = false
fury.MaxShields = 130 fury.MaxShields = 130
fury.Seats += 0 -> bailableSeat fury.Seats += 0 -> bailableSeat
fury.controlledWeapons += 0 -> 1 fury.controlledWeapons += 0 -> 1
fury.Weapons += 1 -> fury_weapon_systema fury.Weapons += 1 -> fury_weapon_systema
fury.MountPoints += 1 -> MountInfo(0) fury.MountPoints += 1 -> MountInfo(0)
fury.MountPoints += 2 -> MountInfo(0) fury.MountPoints += 2 -> MountInfo(0)
fury.TrunkSize = InventoryTile.Tile1111 fury.TrunkSize = InventoryTile.Tile1111
fury.TrunkOffset = 30 fury.TrunkOffset = 30
fury.TrunkLocation = Vector3(-1.71f, 0f, 0f) fury.TrunkLocation = Vector3(-1.71f, 0f, 0f)
@ -5668,11 +5673,11 @@ object GlobalDefinitions {
quadassault.Repairable = true quadassault.Repairable = true
quadassault.RepairIfDestroyed = false quadassault.RepairIfDestroyed = false
quadassault.MaxShields = 130 quadassault.MaxShields = 130
quadassault.Seats += 0 -> bailableSeat quadassault.Seats += 0 -> bailableSeat
quadassault.controlledWeapons += 0 -> 1 quadassault.controlledWeapons += 0 -> 1
quadassault.Weapons += 1 -> quadassault_weapon_system quadassault.Weapons += 1 -> quadassault_weapon_system
quadassault.MountPoints += 1 -> MountInfo(0) quadassault.MountPoints += 1 -> MountInfo(0)
quadassault.MountPoints += 2 -> MountInfo(0) quadassault.MountPoints += 2 -> MountInfo(0)
quadassault.TrunkSize = InventoryTile.Tile1111 quadassault.TrunkSize = InventoryTile.Tile1111
quadassault.TrunkOffset = 30 quadassault.TrunkOffset = 30
quadassault.TrunkLocation = Vector3(-1.71f, 0f, 0f) quadassault.TrunkLocation = Vector3(-1.71f, 0f, 0f)
@ -5730,12 +5735,12 @@ object GlobalDefinitions {
two_man_assault_buggy.Repairable = true two_man_assault_buggy.Repairable = true
two_man_assault_buggy.RepairIfDestroyed = false two_man_assault_buggy.RepairIfDestroyed = false
two_man_assault_buggy.MaxShields = 250 two_man_assault_buggy.MaxShields = 250
two_man_assault_buggy.Seats += 0 -> bailableSeat two_man_assault_buggy.Seats += 0 -> bailableSeat
two_man_assault_buggy.Seats += 1 -> bailableSeat two_man_assault_buggy.Seats += 1 -> bailableSeat
two_man_assault_buggy.controlledWeapons += 1 -> 2 two_man_assault_buggy.controlledWeapons += 1 -> 2
two_man_assault_buggy.Weapons += 2 -> chaingun_p two_man_assault_buggy.Weapons += 2 -> chaingun_p
two_man_assault_buggy.MountPoints += 1 -> MountInfo(0) two_man_assault_buggy.MountPoints += 1 -> MountInfo(0)
two_man_assault_buggy.MountPoints += 2 -> MountInfo(1) two_man_assault_buggy.MountPoints += 2 -> MountInfo(1)
two_man_assault_buggy.TrunkSize = InventoryTile.Tile1511 two_man_assault_buggy.TrunkSize = InventoryTile.Tile1511
two_man_assault_buggy.TrunkOffset = 30 two_man_assault_buggy.TrunkOffset = 30
two_man_assault_buggy.TrunkLocation = Vector3(-2.5f, 0f, 0f) two_man_assault_buggy.TrunkLocation = Vector3(-2.5f, 0f, 0f)
@ -5762,13 +5767,13 @@ object GlobalDefinitions {
skyguard.Repairable = true skyguard.Repairable = true
skyguard.RepairIfDestroyed = false skyguard.RepairIfDestroyed = false
skyguard.MaxShields = 200 skyguard.MaxShields = 200
skyguard.Seats += 0 -> bailableSeat skyguard.Seats += 0 -> bailableSeat
skyguard.Seats += 1 -> bailableSeat skyguard.Seats += 1 -> bailableSeat
skyguard.controlledWeapons += 1 -> 2 skyguard.controlledWeapons += 1 -> 2
skyguard.Weapons += 2 -> skyguard_weapon_system skyguard.Weapons += 2 -> skyguard_weapon_system
skyguard.MountPoints += 1 -> MountInfo(0) skyguard.MountPoints += 1 -> MountInfo(0)
skyguard.MountPoints += 2 -> MountInfo(0) skyguard.MountPoints += 2 -> MountInfo(0)
skyguard.MountPoints += 3 -> MountInfo(1) skyguard.MountPoints += 3 -> MountInfo(1)
skyguard.TrunkSize = InventoryTile.Tile1511 skyguard.TrunkSize = InventoryTile.Tile1511
skyguard.TrunkOffset = 30 skyguard.TrunkOffset = 30
skyguard.TrunkLocation = Vector3(2.5f, 0f, 0f) skyguard.TrunkLocation = Vector3(2.5f, 0f, 0f)
@ -5795,16 +5800,16 @@ object GlobalDefinitions {
threemanheavybuggy.Repairable = true threemanheavybuggy.Repairable = true
threemanheavybuggy.RepairIfDestroyed = false threemanheavybuggy.RepairIfDestroyed = false
threemanheavybuggy.MaxShields = 340 threemanheavybuggy.MaxShields = 340
threemanheavybuggy.Seats += 0 -> bailableSeat threemanheavybuggy.Seats += 0 -> bailableSeat
threemanheavybuggy.Seats += 1 -> bailableSeat threemanheavybuggy.Seats += 1 -> bailableSeat
threemanheavybuggy.Seats += 2 -> bailableSeat threemanheavybuggy.Seats += 2 -> bailableSeat
threemanheavybuggy.controlledWeapons += 1 -> 3 threemanheavybuggy.controlledWeapons += 1 -> 3
threemanheavybuggy.controlledWeapons += 2 -> 4 threemanheavybuggy.controlledWeapons += 2 -> 4
threemanheavybuggy.Weapons += 3 -> chaingun_p threemanheavybuggy.Weapons += 3 -> chaingun_p
threemanheavybuggy.Weapons += 4 -> grenade_launcher_marauder threemanheavybuggy.Weapons += 4 -> grenade_launcher_marauder
threemanheavybuggy.MountPoints += 1 -> MountInfo(0) threemanheavybuggy.MountPoints += 1 -> MountInfo(0)
threemanheavybuggy.MountPoints += 2 -> MountInfo(1) threemanheavybuggy.MountPoints += 2 -> MountInfo(1)
threemanheavybuggy.MountPoints += 3 -> MountInfo(2) threemanheavybuggy.MountPoints += 3 -> MountInfo(2)
threemanheavybuggy.TrunkSize = InventoryTile.Tile1511 threemanheavybuggy.TrunkSize = InventoryTile.Tile1511
threemanheavybuggy.TrunkOffset = 30 threemanheavybuggy.TrunkOffset = 30
threemanheavybuggy.TrunkLocation = Vector3(3.01f, 0f, 0f) threemanheavybuggy.TrunkLocation = Vector3(3.01f, 0f, 0f)
@ -5832,12 +5837,12 @@ object GlobalDefinitions {
twomanheavybuggy.Repairable = true twomanheavybuggy.Repairable = true
twomanheavybuggy.RepairIfDestroyed = false twomanheavybuggy.RepairIfDestroyed = false
twomanheavybuggy.MaxShields = 360 twomanheavybuggy.MaxShields = 360
twomanheavybuggy.Seats += 0 -> bailableSeat twomanheavybuggy.Seats += 0 -> bailableSeat
twomanheavybuggy.Seats += 1 -> bailableSeat twomanheavybuggy.Seats += 1 -> bailableSeat
twomanheavybuggy.controlledWeapons += 1 -> 2 twomanheavybuggy.controlledWeapons += 1 -> 2
twomanheavybuggy.Weapons += 2 -> advanced_missile_launcher_t twomanheavybuggy.Weapons += 2 -> advanced_missile_launcher_t
twomanheavybuggy.MountPoints += 1 -> MountInfo(0) twomanheavybuggy.MountPoints += 1 -> MountInfo(0)
twomanheavybuggy.MountPoints += 2 -> MountInfo(1) twomanheavybuggy.MountPoints += 2 -> MountInfo(1)
twomanheavybuggy.TrunkSize = InventoryTile.Tile1511 twomanheavybuggy.TrunkSize = InventoryTile.Tile1511
twomanheavybuggy.TrunkOffset = 30 twomanheavybuggy.TrunkOffset = 30
twomanheavybuggy.TrunkLocation = Vector3(-0.23f, -2.05f, 0f) twomanheavybuggy.TrunkLocation = Vector3(-0.23f, -2.05f, 0f)
@ -5865,12 +5870,12 @@ object GlobalDefinitions {
twomanhoverbuggy.Repairable = true twomanhoverbuggy.Repairable = true
twomanhoverbuggy.RepairIfDestroyed = false twomanhoverbuggy.RepairIfDestroyed = false
twomanhoverbuggy.MaxShields = 320 twomanhoverbuggy.MaxShields = 320
twomanhoverbuggy.Seats += 0 -> bailableSeat twomanhoverbuggy.Seats += 0 -> bailableSeat
twomanhoverbuggy.Seats += 1 -> bailableSeat twomanhoverbuggy.Seats += 1 -> bailableSeat
twomanhoverbuggy.controlledWeapons += 1 -> 2 twomanhoverbuggy.controlledWeapons += 1 -> 2
twomanhoverbuggy.Weapons += 2 -> flux_cannon_thresher twomanhoverbuggy.Weapons += 2 -> flux_cannon_thresher
twomanhoverbuggy.MountPoints += 1 -> MountInfo(0) twomanhoverbuggy.MountPoints += 1 -> MountInfo(0)
twomanhoverbuggy.MountPoints += 2 -> MountInfo(1) twomanhoverbuggy.MountPoints += 2 -> MountInfo(1)
twomanhoverbuggy.TrunkSize = InventoryTile.Tile1511 twomanhoverbuggy.TrunkSize = InventoryTile.Tile1511
twomanhoverbuggy.TrunkOffset = 30 twomanhoverbuggy.TrunkOffset = 30
twomanhoverbuggy.TrunkLocation = Vector3(-3.39f, 0f, 0f) twomanhoverbuggy.TrunkLocation = Vector3(-3.39f, 0f, 0f)
@ -5888,7 +5893,10 @@ object GlobalDefinitions {
Modifiers = ExplodingRadialDegrade Modifiers = ExplodingRadialDegrade
} }
twomanhoverbuggy.DrownAtMaxDepth = true twomanhoverbuggy.DrownAtMaxDepth = true
twomanhoverbuggy.UnderwaterLifespan(suffocation = 45000L, recovery = 5000L) //but the thresher hovers over water, so ...? twomanhoverbuggy.UnderwaterLifespan(
suffocation = 45000L,
recovery = 5000L
) //but the thresher hovers over water, so ...?
twomanhoverbuggy.Geometry = GeometryForm.representByCylinder(radius = 2.1875f, height = 2.01563f) twomanhoverbuggy.Geometry = GeometryForm.representByCylinder(radius = 2.1875f, height = 2.01563f)
mediumtransport.Name = "mediumtransport" // Deliverer mediumtransport.Name = "mediumtransport" // Deliverer
@ -5900,19 +5908,19 @@ object GlobalDefinitions {
mediumtransport.Seats += 0 -> new SeatDefinition() { mediumtransport.Seats += 0 -> new SeatDefinition() {
restriction = NoReinforcedOrMax restriction = NoReinforcedOrMax
} }
mediumtransport.Seats += 1 -> new SeatDefinition() mediumtransport.Seats += 1 -> new SeatDefinition()
mediumtransport.Seats += 2 -> new SeatDefinition() mediumtransport.Seats += 2 -> new SeatDefinition()
mediumtransport.Seats += 3 -> new SeatDefinition() mediumtransport.Seats += 3 -> new SeatDefinition()
mediumtransport.Seats += 4 -> new SeatDefinition() mediumtransport.Seats += 4 -> new SeatDefinition()
mediumtransport.controlledWeapons += 1 -> 5 mediumtransport.controlledWeapons += 1 -> 5
mediumtransport.controlledWeapons += 2 -> 6 mediumtransport.controlledWeapons += 2 -> 6
mediumtransport.Weapons += 5 -> mediumtransport_weapon_systemA mediumtransport.Weapons += 5 -> mediumtransport_weapon_systemA
mediumtransport.Weapons += 6 -> mediumtransport_weapon_systemB mediumtransport.Weapons += 6 -> mediumtransport_weapon_systemB
mediumtransport.MountPoints += 1 -> MountInfo(0) mediumtransport.MountPoints += 1 -> MountInfo(0)
mediumtransport.MountPoints += 2 -> MountInfo(1) mediumtransport.MountPoints += 2 -> MountInfo(1)
mediumtransport.MountPoints += 3 -> MountInfo(2) mediumtransport.MountPoints += 3 -> MountInfo(2)
mediumtransport.MountPoints += 4 -> MountInfo(3) mediumtransport.MountPoints += 4 -> MountInfo(3)
mediumtransport.MountPoints += 5 -> MountInfo(4) mediumtransport.MountPoints += 5 -> MountInfo(4)
mediumtransport.TrunkSize = InventoryTile.Tile1515 mediumtransport.TrunkSize = InventoryTile.Tile1515
mediumtransport.TrunkOffset = 30 mediumtransport.TrunkOffset = 30
mediumtransport.TrunkLocation = Vector3(-3.46f, 0f, 0f) mediumtransport.TrunkLocation = Vector3(-3.46f, 0f, 0f)
@ -5943,23 +5951,23 @@ object GlobalDefinitions {
battlewagon.Seats += 0 -> new SeatDefinition() { battlewagon.Seats += 0 -> new SeatDefinition() {
restriction = NoReinforcedOrMax restriction = NoReinforcedOrMax
} }
battlewagon.Seats += 1 -> new SeatDefinition() battlewagon.Seats += 1 -> new SeatDefinition()
battlewagon.Seats += 2 -> new SeatDefinition() battlewagon.Seats += 2 -> new SeatDefinition()
battlewagon.Seats += 3 -> new SeatDefinition() battlewagon.Seats += 3 -> new SeatDefinition()
battlewagon.Seats += 4 -> new SeatDefinition() battlewagon.Seats += 4 -> new SeatDefinition()
battlewagon.controlledWeapons += 1 -> 5 battlewagon.controlledWeapons += 1 -> 5
battlewagon.controlledWeapons += 2 -> 6 battlewagon.controlledWeapons += 2 -> 6
battlewagon.controlledWeapons += 3 -> 7 battlewagon.controlledWeapons += 3 -> 7
battlewagon.controlledWeapons += 4 -> 8 battlewagon.controlledWeapons += 4 -> 8
battlewagon.Weapons += 5 -> battlewagon_weapon_systema battlewagon.Weapons += 5 -> battlewagon_weapon_systema
battlewagon.Weapons += 6 -> battlewagon_weapon_systemb battlewagon.Weapons += 6 -> battlewagon_weapon_systemb
battlewagon.Weapons += 7 -> battlewagon_weapon_systemc battlewagon.Weapons += 7 -> battlewagon_weapon_systemc
battlewagon.Weapons += 8 -> battlewagon_weapon_systemd battlewagon.Weapons += 8 -> battlewagon_weapon_systemd
battlewagon.MountPoints += 1 -> MountInfo(0) battlewagon.MountPoints += 1 -> MountInfo(0)
battlewagon.MountPoints += 2 -> MountInfo(1) battlewagon.MountPoints += 2 -> MountInfo(1)
battlewagon.MountPoints += 3 -> MountInfo(2) battlewagon.MountPoints += 3 -> MountInfo(2)
battlewagon.MountPoints += 4 -> MountInfo(3) battlewagon.MountPoints += 4 -> MountInfo(3)
battlewagon.MountPoints += 5 -> MountInfo(4) battlewagon.MountPoints += 5 -> MountInfo(4)
battlewagon.TrunkSize = InventoryTile.Tile1515 battlewagon.TrunkSize = InventoryTile.Tile1515
battlewagon.TrunkOffset = 30 battlewagon.TrunkOffset = 30
battlewagon.TrunkLocation = Vector3(-3.46f, 0f, 0f) battlewagon.TrunkLocation = Vector3(-3.46f, 0f, 0f)
@ -5989,19 +5997,19 @@ object GlobalDefinitions {
thunderer.Seats += 0 -> new SeatDefinition() { thunderer.Seats += 0 -> new SeatDefinition() {
restriction = NoReinforcedOrMax restriction = NoReinforcedOrMax
} }
thunderer.Seats += 1 -> new SeatDefinition() thunderer.Seats += 1 -> new SeatDefinition()
thunderer.Seats += 2 -> new SeatDefinition() thunderer.Seats += 2 -> new SeatDefinition()
thunderer.Seats += 3 -> new SeatDefinition() thunderer.Seats += 3 -> new SeatDefinition()
thunderer.Seats += 4 -> new SeatDefinition() thunderer.Seats += 4 -> new SeatDefinition()
thunderer.Weapons += 5 -> thunderer_weapon_systema thunderer.Weapons += 5 -> thunderer_weapon_systema
thunderer.Weapons += 6 -> thunderer_weapon_systemb thunderer.Weapons += 6 -> thunderer_weapon_systemb
thunderer.controlledWeapons += 1 -> 5 thunderer.controlledWeapons += 1 -> 5
thunderer.controlledWeapons += 2 -> 6 thunderer.controlledWeapons += 2 -> 6
thunderer.MountPoints += 1 -> MountInfo(0) thunderer.MountPoints += 1 -> MountInfo(0)
thunderer.MountPoints += 2 -> MountInfo(1) thunderer.MountPoints += 2 -> MountInfo(1)
thunderer.MountPoints += 3 -> MountInfo(2) thunderer.MountPoints += 3 -> MountInfo(2)
thunderer.MountPoints += 4 -> MountInfo(3) thunderer.MountPoints += 4 -> MountInfo(3)
thunderer.MountPoints += 5 -> MountInfo(4) thunderer.MountPoints += 5 -> MountInfo(4)
thunderer.TrunkSize = InventoryTile.Tile1515 thunderer.TrunkSize = InventoryTile.Tile1515
thunderer.TrunkOffset = 30 thunderer.TrunkOffset = 30
thunderer.TrunkLocation = Vector3(-3.46f, 0f, 0f) thunderer.TrunkLocation = Vector3(-3.46f, 0f, 0f)
@ -6032,19 +6040,19 @@ object GlobalDefinitions {
aurora.Seats += 0 -> new SeatDefinition() { aurora.Seats += 0 -> new SeatDefinition() {
restriction = NoReinforcedOrMax restriction = NoReinforcedOrMax
} }
aurora.Seats += 1 -> new SeatDefinition() aurora.Seats += 1 -> new SeatDefinition()
aurora.Seats += 2 -> new SeatDefinition() aurora.Seats += 2 -> new SeatDefinition()
aurora.Seats += 3 -> new SeatDefinition() aurora.Seats += 3 -> new SeatDefinition()
aurora.Seats += 4 -> new SeatDefinition() aurora.Seats += 4 -> new SeatDefinition()
aurora.controlledWeapons += 1 -> 5 aurora.controlledWeapons += 1 -> 5
aurora.controlledWeapons += 2 -> 6 aurora.controlledWeapons += 2 -> 6
aurora.Weapons += 5 -> aurora_weapon_systema aurora.Weapons += 5 -> aurora_weapon_systema
aurora.Weapons += 6 -> aurora_weapon_systemb aurora.Weapons += 6 -> aurora_weapon_systemb
aurora.MountPoints += 1 -> MountInfo(0) aurora.MountPoints += 1 -> MountInfo(0)
aurora.MountPoints += 2 -> MountInfo(1) aurora.MountPoints += 2 -> MountInfo(1)
aurora.MountPoints += 3 -> MountInfo(2) aurora.MountPoints += 3 -> MountInfo(2)
aurora.MountPoints += 4 -> MountInfo(3) aurora.MountPoints += 4 -> MountInfo(3)
aurora.MountPoints += 5 -> MountInfo(4) aurora.MountPoints += 5 -> MountInfo(4)
aurora.TrunkSize = InventoryTile.Tile1515 aurora.TrunkSize = InventoryTile.Tile1515
aurora.TrunkOffset = 30 aurora.TrunkOffset = 30
aurora.TrunkLocation = Vector3(-3.46f, 0f, 0f) aurora.TrunkLocation = Vector3(-3.46f, 0f, 0f)
@ -6072,41 +6080,41 @@ object GlobalDefinitions {
apc_tr.Repairable = true apc_tr.Repairable = true
apc_tr.RepairIfDestroyed = false apc_tr.RepairIfDestroyed = false
apc_tr.MaxShields = 1200 apc_tr.MaxShields = 1200
apc_tr.Seats += 0 -> new SeatDefinition() apc_tr.Seats += 0 -> new SeatDefinition()
apc_tr.Seats += 1 -> new SeatDefinition() apc_tr.Seats += 1 -> new SeatDefinition()
apc_tr.Seats += 2 -> new SeatDefinition() apc_tr.Seats += 2 -> new SeatDefinition()
apc_tr.Seats += 3 -> new SeatDefinition() apc_tr.Seats += 3 -> new SeatDefinition()
apc_tr.Seats += 4 -> new SeatDefinition() apc_tr.Seats += 4 -> new SeatDefinition()
apc_tr.Seats += 5 -> new SeatDefinition() apc_tr.Seats += 5 -> new SeatDefinition()
apc_tr.Seats += 6 -> new SeatDefinition() apc_tr.Seats += 6 -> new SeatDefinition()
apc_tr.Seats += 7 -> new SeatDefinition() apc_tr.Seats += 7 -> new SeatDefinition()
apc_tr.Seats += 8 -> new SeatDefinition() apc_tr.Seats += 8 -> new SeatDefinition()
apc_tr.Seats += 9 -> maxOnlySeat apc_tr.Seats += 9 -> maxOnlySeat
apc_tr.Seats += 10 -> maxOnlySeat apc_tr.Seats += 10 -> maxOnlySeat
apc_tr.controlledWeapons += 1 -> 11 apc_tr.controlledWeapons += 1 -> 11
apc_tr.controlledWeapons += 2 -> 12 apc_tr.controlledWeapons += 2 -> 12
apc_tr.controlledWeapons += 5 -> 15 apc_tr.controlledWeapons += 5 -> 15
apc_tr.controlledWeapons += 6 -> 16 apc_tr.controlledWeapons += 6 -> 16
apc_tr.controlledWeapons += 7 -> 13 apc_tr.controlledWeapons += 7 -> 13
apc_tr.controlledWeapons += 8 -> 14 apc_tr.controlledWeapons += 8 -> 14
apc_tr.Weapons += 11 -> apc_weapon_systemc_tr apc_tr.Weapons += 11 -> apc_weapon_systemc_tr
apc_tr.Weapons += 12 -> apc_weapon_systemb apc_tr.Weapons += 12 -> apc_weapon_systemb
apc_tr.Weapons += 13 -> apc_weapon_systema apc_tr.Weapons += 13 -> apc_weapon_systema
apc_tr.Weapons += 14 -> apc_weapon_systemd_tr apc_tr.Weapons += 14 -> apc_weapon_systemd_tr
apc_tr.Weapons += 15 -> apc_ballgun_r apc_tr.Weapons += 15 -> apc_ballgun_r
apc_tr.Weapons += 16 -> apc_ballgun_l apc_tr.Weapons += 16 -> apc_ballgun_l
apc_tr.MountPoints += 1 -> MountInfo(0) apc_tr.MountPoints += 1 -> MountInfo(0)
apc_tr.MountPoints += 2 -> MountInfo(0) apc_tr.MountPoints += 2 -> MountInfo(0)
apc_tr.MountPoints += 3 -> MountInfo(1) apc_tr.MountPoints += 3 -> MountInfo(1)
apc_tr.MountPoints += 4 -> MountInfo(2) apc_tr.MountPoints += 4 -> MountInfo(2)
apc_tr.MountPoints += 5 -> MountInfo(3) apc_tr.MountPoints += 5 -> MountInfo(3)
apc_tr.MountPoints += 6 -> MountInfo(4) apc_tr.MountPoints += 6 -> MountInfo(4)
apc_tr.MountPoints += 7 -> MountInfo(5) apc_tr.MountPoints += 7 -> MountInfo(5)
apc_tr.MountPoints += 8 -> MountInfo(6) apc_tr.MountPoints += 8 -> MountInfo(6)
apc_tr.MountPoints += 9 -> MountInfo(7) apc_tr.MountPoints += 9 -> MountInfo(7)
apc_tr.MountPoints += 10 -> MountInfo(8) apc_tr.MountPoints += 10 -> MountInfo(8)
apc_tr.MountPoints += 11 -> MountInfo(9) apc_tr.MountPoints += 11 -> MountInfo(9)
apc_tr.MountPoints += 12 -> MountInfo(10) apc_tr.MountPoints += 12 -> MountInfo(10)
apc_tr.TrunkSize = InventoryTile.Tile2016 apc_tr.TrunkSize = InventoryTile.Tile2016
apc_tr.TrunkOffset = 30 apc_tr.TrunkOffset = 30
apc_tr.TrunkLocation = Vector3(-5.82f, 0f, 0f) apc_tr.TrunkLocation = Vector3(-5.82f, 0f, 0f)
@ -6134,41 +6142,41 @@ object GlobalDefinitions {
apc_nc.Repairable = true apc_nc.Repairable = true
apc_nc.RepairIfDestroyed = false apc_nc.RepairIfDestroyed = false
apc_nc.MaxShields = 1200 apc_nc.MaxShields = 1200
apc_nc.Seats += 0 -> new SeatDefinition() apc_nc.Seats += 0 -> new SeatDefinition()
apc_nc.Seats += 1 -> new SeatDefinition() apc_nc.Seats += 1 -> new SeatDefinition()
apc_nc.Seats += 2 -> new SeatDefinition() apc_nc.Seats += 2 -> new SeatDefinition()
apc_nc.Seats += 3 -> new SeatDefinition() apc_nc.Seats += 3 -> new SeatDefinition()
apc_nc.Seats += 4 -> new SeatDefinition() apc_nc.Seats += 4 -> new SeatDefinition()
apc_nc.Seats += 5 -> new SeatDefinition() apc_nc.Seats += 5 -> new SeatDefinition()
apc_nc.Seats += 6 -> new SeatDefinition() apc_nc.Seats += 6 -> new SeatDefinition()
apc_nc.Seats += 7 -> new SeatDefinition() apc_nc.Seats += 7 -> new SeatDefinition()
apc_nc.Seats += 8 -> new SeatDefinition() apc_nc.Seats += 8 -> new SeatDefinition()
apc_nc.Seats += 9 -> maxOnlySeat apc_nc.Seats += 9 -> maxOnlySeat
apc_nc.Seats += 10 -> maxOnlySeat apc_nc.Seats += 10 -> maxOnlySeat
apc_nc.controlledWeapons += 1 -> 11 apc_nc.controlledWeapons += 1 -> 11
apc_nc.controlledWeapons += 2 -> 12 apc_nc.controlledWeapons += 2 -> 12
apc_nc.controlledWeapons += 5 -> 15 apc_nc.controlledWeapons += 5 -> 15
apc_nc.controlledWeapons += 6 -> 16 apc_nc.controlledWeapons += 6 -> 16
apc_nc.controlledWeapons += 7 -> 13 apc_nc.controlledWeapons += 7 -> 13
apc_nc.controlledWeapons += 8 -> 14 apc_nc.controlledWeapons += 8 -> 14
apc_nc.Weapons += 11 -> apc_weapon_systemc_nc apc_nc.Weapons += 11 -> apc_weapon_systemc_nc
apc_nc.Weapons += 12 -> apc_weapon_systemb apc_nc.Weapons += 12 -> apc_weapon_systemb
apc_nc.Weapons += 13 -> apc_weapon_systema apc_nc.Weapons += 13 -> apc_weapon_systema
apc_nc.Weapons += 14 -> apc_weapon_systemd_nc apc_nc.Weapons += 14 -> apc_weapon_systemd_nc
apc_nc.Weapons += 15 -> apc_ballgun_r apc_nc.Weapons += 15 -> apc_ballgun_r
apc_nc.Weapons += 16 -> apc_ballgun_l apc_nc.Weapons += 16 -> apc_ballgun_l
apc_nc.MountPoints += 1 -> MountInfo(0) apc_nc.MountPoints += 1 -> MountInfo(0)
apc_nc.MountPoints += 2 -> MountInfo(0) apc_nc.MountPoints += 2 -> MountInfo(0)
apc_nc.MountPoints += 3 -> MountInfo(1) apc_nc.MountPoints += 3 -> MountInfo(1)
apc_nc.MountPoints += 4 -> MountInfo(2) apc_nc.MountPoints += 4 -> MountInfo(2)
apc_nc.MountPoints += 5 -> MountInfo(3) apc_nc.MountPoints += 5 -> MountInfo(3)
apc_nc.MountPoints += 6 -> MountInfo(4) apc_nc.MountPoints += 6 -> MountInfo(4)
apc_nc.MountPoints += 7 -> MountInfo(5) apc_nc.MountPoints += 7 -> MountInfo(5)
apc_nc.MountPoints += 8 -> MountInfo(6) apc_nc.MountPoints += 8 -> MountInfo(6)
apc_nc.MountPoints += 9 -> MountInfo(7) apc_nc.MountPoints += 9 -> MountInfo(7)
apc_nc.MountPoints += 10 -> MountInfo(8) apc_nc.MountPoints += 10 -> MountInfo(8)
apc_nc.MountPoints += 11 -> MountInfo(9) apc_nc.MountPoints += 11 -> MountInfo(9)
apc_nc.MountPoints += 12 -> MountInfo(10) apc_nc.MountPoints += 12 -> MountInfo(10)
apc_nc.TrunkSize = InventoryTile.Tile2016 apc_nc.TrunkSize = InventoryTile.Tile2016
apc_nc.TrunkOffset = 30 apc_nc.TrunkOffset = 30
apc_nc.TrunkLocation = Vector3(-5.82f, 0f, 0f) apc_nc.TrunkLocation = Vector3(-5.82f, 0f, 0f)
@ -6196,41 +6204,41 @@ object GlobalDefinitions {
apc_vs.Repairable = true apc_vs.Repairable = true
apc_vs.RepairIfDestroyed = false apc_vs.RepairIfDestroyed = false
apc_vs.MaxShields = 1200 apc_vs.MaxShields = 1200
apc_vs.Seats += 0 -> new SeatDefinition() apc_vs.Seats += 0 -> new SeatDefinition()
apc_vs.Seats += 1 -> new SeatDefinition() apc_vs.Seats += 1 -> new SeatDefinition()
apc_vs.Seats += 2 -> new SeatDefinition() apc_vs.Seats += 2 -> new SeatDefinition()
apc_vs.Seats += 3 -> new SeatDefinition() apc_vs.Seats += 3 -> new SeatDefinition()
apc_vs.Seats += 4 -> new SeatDefinition() apc_vs.Seats += 4 -> new SeatDefinition()
apc_vs.Seats += 5 -> new SeatDefinition() apc_vs.Seats += 5 -> new SeatDefinition()
apc_vs.Seats += 6 -> new SeatDefinition() apc_vs.Seats += 6 -> new SeatDefinition()
apc_vs.Seats += 7 -> new SeatDefinition() apc_vs.Seats += 7 -> new SeatDefinition()
apc_vs.Seats += 8 -> new SeatDefinition() apc_vs.Seats += 8 -> new SeatDefinition()
apc_vs.Seats += 9 -> maxOnlySeat apc_vs.Seats += 9 -> maxOnlySeat
apc_vs.Seats += 10 -> maxOnlySeat apc_vs.Seats += 10 -> maxOnlySeat
apc_vs.controlledWeapons += 1 -> 11 apc_vs.controlledWeapons += 1 -> 11
apc_vs.controlledWeapons += 2 -> 12 apc_vs.controlledWeapons += 2 -> 12
apc_vs.controlledWeapons += 5 -> 15 apc_vs.controlledWeapons += 5 -> 15
apc_vs.controlledWeapons += 6 -> 16 apc_vs.controlledWeapons += 6 -> 16
apc_vs.controlledWeapons += 7 -> 13 apc_vs.controlledWeapons += 7 -> 13
apc_vs.controlledWeapons += 8 -> 14 apc_vs.controlledWeapons += 8 -> 14
apc_vs.Weapons += 11 -> apc_weapon_systemc_vs apc_vs.Weapons += 11 -> apc_weapon_systemc_vs
apc_vs.Weapons += 12 -> apc_weapon_systemb apc_vs.Weapons += 12 -> apc_weapon_systemb
apc_vs.Weapons += 13 -> apc_weapon_systema apc_vs.Weapons += 13 -> apc_weapon_systema
apc_vs.Weapons += 14 -> apc_weapon_systemd_vs apc_vs.Weapons += 14 -> apc_weapon_systemd_vs
apc_vs.Weapons += 15 -> apc_ballgun_r apc_vs.Weapons += 15 -> apc_ballgun_r
apc_vs.Weapons += 16 -> apc_ballgun_l apc_vs.Weapons += 16 -> apc_ballgun_l
apc_vs.MountPoints += 1 -> MountInfo(0) apc_vs.MountPoints += 1 -> MountInfo(0)
apc_vs.MountPoints += 2 -> MountInfo(0) apc_vs.MountPoints += 2 -> MountInfo(0)
apc_vs.MountPoints += 3 -> MountInfo(1) apc_vs.MountPoints += 3 -> MountInfo(1)
apc_vs.MountPoints += 4 -> MountInfo(2) apc_vs.MountPoints += 4 -> MountInfo(2)
apc_vs.MountPoints += 5 -> MountInfo(3) apc_vs.MountPoints += 5 -> MountInfo(3)
apc_vs.MountPoints += 6 -> MountInfo(4) apc_vs.MountPoints += 6 -> MountInfo(4)
apc_vs.MountPoints += 7 -> MountInfo(5) apc_vs.MountPoints += 7 -> MountInfo(5)
apc_vs.MountPoints += 8 -> MountInfo(6) apc_vs.MountPoints += 8 -> MountInfo(6)
apc_vs.MountPoints += 9 -> MountInfo(7) apc_vs.MountPoints += 9 -> MountInfo(7)
apc_vs.MountPoints += 10 -> MountInfo(8) apc_vs.MountPoints += 10 -> MountInfo(8)
apc_vs.MountPoints += 11 -> MountInfo(9) apc_vs.MountPoints += 11 -> MountInfo(9)
apc_vs.MountPoints += 12 -> MountInfo(10) apc_vs.MountPoints += 12 -> MountInfo(10)
apc_vs.TrunkSize = InventoryTile.Tile2016 apc_vs.TrunkSize = InventoryTile.Tile2016
apc_vs.TrunkOffset = 30 apc_vs.TrunkOffset = 30
apc_vs.TrunkLocation = Vector3(-5.82f, 0f, 0f) apc_vs.TrunkLocation = Vector3(-5.82f, 0f, 0f)
@ -6262,9 +6270,9 @@ object GlobalDefinitions {
restriction = NoReinforcedOrMax restriction = NoReinforcedOrMax
} }
lightning.controlledWeapons += 0 -> 1 lightning.controlledWeapons += 0 -> 1
lightning.Weapons += 1 -> lightning_weapon_system lightning.Weapons += 1 -> lightning_weapon_system
lightning.MountPoints += 1 -> MountInfo(0) lightning.MountPoints += 1 -> MountInfo(0)
lightning.MountPoints += 2 -> MountInfo(0) lightning.MountPoints += 2 -> MountInfo(0)
lightning.TrunkSize = InventoryTile.Tile1511 lightning.TrunkSize = InventoryTile.Tile1511
lightning.TrunkOffset = 30 lightning.TrunkOffset = 30
lightning.TrunkLocation = Vector3(-3f, 0f, 0f) lightning.TrunkLocation = Vector3(-3f, 0f, 0f)
@ -6295,15 +6303,15 @@ object GlobalDefinitions {
prowler.Seats += 0 -> new SeatDefinition() { prowler.Seats += 0 -> new SeatDefinition() {
restriction = NoReinforcedOrMax restriction = NoReinforcedOrMax
} }
prowler.Seats += 1 -> new SeatDefinition() prowler.Seats += 1 -> new SeatDefinition()
prowler.Seats += 2 -> new SeatDefinition() prowler.Seats += 2 -> new SeatDefinition()
prowler.controlledWeapons += 1 -> 3 prowler.controlledWeapons += 1 -> 3
prowler.controlledWeapons += 2 -> 4 prowler.controlledWeapons += 2 -> 4
prowler.Weapons += 3 -> prowler_weapon_systemA prowler.Weapons += 3 -> prowler_weapon_systemA
prowler.Weapons += 4 -> prowler_weapon_systemB prowler.Weapons += 4 -> prowler_weapon_systemB
prowler.MountPoints += 1 -> MountInfo(0) prowler.MountPoints += 1 -> MountInfo(0)
prowler.MountPoints += 2 -> MountInfo(1) prowler.MountPoints += 2 -> MountInfo(1)
prowler.MountPoints += 3 -> MountInfo(2) prowler.MountPoints += 3 -> MountInfo(2)
prowler.TrunkSize = InventoryTile.Tile1511 prowler.TrunkSize = InventoryTile.Tile1511
prowler.TrunkOffset = 30 prowler.TrunkOffset = 30
prowler.TrunkLocation = Vector3(-4.71f, 0f, 0f) prowler.TrunkLocation = Vector3(-4.71f, 0f, 0f)
@ -6334,11 +6342,11 @@ object GlobalDefinitions {
vanguard.Seats += 0 -> new SeatDefinition() { vanguard.Seats += 0 -> new SeatDefinition() {
restriction = NoReinforcedOrMax restriction = NoReinforcedOrMax
} }
vanguard.Seats += 1 -> new SeatDefinition() vanguard.Seats += 1 -> new SeatDefinition()
vanguard.controlledWeapons += 1 -> 2 vanguard.controlledWeapons += 1 -> 2
vanguard.Weapons += 2 -> vanguard_weapon_system vanguard.Weapons += 2 -> vanguard_weapon_system
vanguard.MountPoints += 1 -> MountInfo(0) vanguard.MountPoints += 1 -> MountInfo(0)
vanguard.MountPoints += 2 -> MountInfo(1) vanguard.MountPoints += 2 -> MountInfo(1)
vanguard.TrunkSize = InventoryTile.Tile1511 vanguard.TrunkSize = InventoryTile.Tile1511
vanguard.TrunkOffset = 30 vanguard.TrunkOffset = 30
vanguard.TrunkLocation = Vector3(-4.84f, 0f, 0f) vanguard.TrunkLocation = Vector3(-4.84f, 0f, 0f)
@ -6369,13 +6377,13 @@ object GlobalDefinitions {
magrider.Seats += 0 -> new SeatDefinition() { magrider.Seats += 0 -> new SeatDefinition() {
restriction = NoReinforcedOrMax restriction = NoReinforcedOrMax
} }
magrider.Seats += 1 -> new SeatDefinition() magrider.Seats += 1 -> new SeatDefinition()
magrider.controlledWeapons += 0 -> 2 magrider.controlledWeapons += 0 -> 2
magrider.controlledWeapons += 1 -> 3 magrider.controlledWeapons += 1 -> 3
magrider.Weapons += 2 -> particle_beam_magrider magrider.Weapons += 2 -> particle_beam_magrider
magrider.Weapons += 3 -> heavy_rail_beam_magrider magrider.Weapons += 3 -> heavy_rail_beam_magrider
magrider.MountPoints += 1 -> MountInfo(0) magrider.MountPoints += 1 -> MountInfo(0)
magrider.MountPoints += 2 -> MountInfo(1) magrider.MountPoints += 2 -> MountInfo(1)
magrider.TrunkSize = InventoryTile.Tile1511 magrider.TrunkSize = InventoryTile.Tile1511
magrider.TrunkOffset = 30 magrider.TrunkOffset = 30
magrider.TrunkLocation = Vector3(5.06f, 0f, 0f) magrider.TrunkLocation = Vector3(5.06f, 0f, 0f)
@ -6504,7 +6512,7 @@ object GlobalDefinitions {
} }
router.DrownAtMaxDepth = true router.DrownAtMaxDepth = true
router.MaxDepth = 2 router.MaxDepth = 2
router.UnderwaterLifespan(suffocation = 45000L, recovery = 5000L) //but the router hovers over water, so ...? router.UnderwaterLifespan(suffocation = 45000L, recovery = 5000L) //but the router hovers over water, so ...?
router.Geometry = GeometryForm.representByCylinder(radius = 3.64845f, height = 3.51563f) //TODO hexahedron router.Geometry = GeometryForm.representByCylinder(radius = 3.64845f, height = 3.51563f) //TODO hexahedron
switchblade.Name = "switchblade" switchblade.Name = "switchblade"
@ -6513,11 +6521,11 @@ object GlobalDefinitions {
switchblade.Repairable = true switchblade.Repairable = true
switchblade.RepairIfDestroyed = false switchblade.RepairIfDestroyed = false
switchblade.MaxShields = 350 switchblade.MaxShields = 350
switchblade.Seats += 0 -> new SeatDefinition() switchblade.Seats += 0 -> new SeatDefinition()
switchblade.controlledWeapons += 0 -> 1 switchblade.controlledWeapons += 0 -> 1
switchblade.Weapons += 1 -> scythe switchblade.Weapons += 1 -> scythe
switchblade.MountPoints += 1 -> MountInfo(0) switchblade.MountPoints += 1 -> MountInfo(0)
switchblade.MountPoints += 2 -> MountInfo(0) switchblade.MountPoints += 2 -> MountInfo(0)
switchblade.TrunkSize = InventoryTile.Tile1511 switchblade.TrunkSize = InventoryTile.Tile1511
switchblade.TrunkOffset = 30 switchblade.TrunkOffset = 30
switchblade.TrunkLocation = Vector3(-2.5f, 0f, 0f) switchblade.TrunkLocation = Vector3(-2.5f, 0f, 0f)
@ -6541,7 +6549,10 @@ object GlobalDefinitions {
} }
switchblade.DrownAtMaxDepth = true switchblade.DrownAtMaxDepth = true
switchblade.MaxDepth = 2 switchblade.MaxDepth = 2
switchblade.UnderwaterLifespan(suffocation = 45000L, recovery = 5000L) //but the switchblade hovers over water, so ...? switchblade.UnderwaterLifespan(
suffocation = 45000L,
recovery = 5000L
) //but the switchblade hovers over water, so ...?
switchblade.Geometry = GeometryForm.representByCylinder(radius = 2.4335f, height = 2.73438f) switchblade.Geometry = GeometryForm.representByCylinder(radius = 2.4335f, height = 2.73438f)
flail.Name = "flail" flail.Name = "flail"
@ -6550,10 +6561,10 @@ object GlobalDefinitions {
flail.Repairable = true flail.Repairable = true
flail.RepairIfDestroyed = false flail.RepairIfDestroyed = false
flail.MaxShields = 480 flail.MaxShields = 480
flail.Seats += 0 -> new SeatDefinition() flail.Seats += 0 -> new SeatDefinition()
flail.controlledWeapons += 0 -> 1 flail.controlledWeapons += 0 -> 1
flail.Weapons += 1 -> flail_weapon flail.Weapons += 1 -> flail_weapon
flail.MountPoints += 1 -> MountInfo(0) flail.MountPoints += 1 -> MountInfo(0)
flail.TrunkSize = InventoryTile.Tile1511 flail.TrunkSize = InventoryTile.Tile1511
flail.TrunkOffset = 30 flail.TrunkOffset = 30
flail.TrunkLocation = Vector3(-3.75f, 0f, 0f) flail.TrunkLocation = Vector3(-3.75f, 0f, 0f)
@ -6586,11 +6597,11 @@ object GlobalDefinitions {
mosquito.RepairIfDestroyed = false mosquito.RepairIfDestroyed = false
mosquito.MaxShields = 133 mosquito.MaxShields = 133
mosquito.CanFly = true mosquito.CanFly = true
mosquito.Seats += 0 -> bailableSeat mosquito.Seats += 0 -> bailableSeat
mosquito.controlledWeapons += 0 -> 1 mosquito.controlledWeapons += 0 -> 1
mosquito.Weapons += 1 -> rotarychaingun_mosquito mosquito.Weapons += 1 -> rotarychaingun_mosquito
mosquito.MountPoints += 1 -> MountInfo(0) mosquito.MountPoints += 1 -> MountInfo(0)
mosquito.MountPoints += 2 -> MountInfo(0) mosquito.MountPoints += 2 -> MountInfo(0)
mosquito.TrunkSize = InventoryTile.Tile1111 mosquito.TrunkSize = InventoryTile.Tile1111
mosquito.TrunkOffset = 30 mosquito.TrunkOffset = 30
mosquito.TrunkLocation = Vector3(-4.6f, 0f, 0f) mosquito.TrunkLocation = Vector3(-4.6f, 0f, 0f)
@ -6619,11 +6630,11 @@ object GlobalDefinitions {
lightgunship.RepairIfDestroyed = false lightgunship.RepairIfDestroyed = false
lightgunship.MaxShields = 200 lightgunship.MaxShields = 200
lightgunship.CanFly = true lightgunship.CanFly = true
lightgunship.Seats += 0 -> bailableSeat lightgunship.Seats += 0 -> bailableSeat
lightgunship.controlledWeapons += 0 -> 1 lightgunship.controlledWeapons += 0 -> 1
lightgunship.Weapons += 1 -> lightgunship_weapon_system lightgunship.Weapons += 1 -> lightgunship_weapon_system
lightgunship.MountPoints += 1 -> MountInfo(0) lightgunship.MountPoints += 1 -> MountInfo(0)
lightgunship.MountPoints += 2 -> MountInfo(0) lightgunship.MountPoints += 2 -> MountInfo(0)
lightgunship.TrunkSize = InventoryTile.Tile1511 lightgunship.TrunkSize = InventoryTile.Tile1511
lightgunship.TrunkOffset = 30 lightgunship.TrunkOffset = 30
lightgunship.TrunkLocation = Vector3(-5.61f, 0f, 0f) lightgunship.TrunkLocation = Vector3(-5.61f, 0f, 0f)
@ -6653,11 +6664,11 @@ object GlobalDefinitions {
wasp.RepairIfDestroyed = false wasp.RepairIfDestroyed = false
wasp.MaxShields = 103 wasp.MaxShields = 103
wasp.CanFly = true wasp.CanFly = true
wasp.Seats += 0 -> bailableSeat wasp.Seats += 0 -> bailableSeat
wasp.controlledWeapons += 0 -> 1 wasp.controlledWeapons += 0 -> 1
wasp.Weapons += 1 -> wasp_weapon_system wasp.Weapons += 1 -> wasp_weapon_system
wasp.MountPoints += 1 -> MountInfo(0) wasp.MountPoints += 1 -> MountInfo(0)
wasp.MountPoints += 2 -> MountInfo(0) wasp.MountPoints += 2 -> MountInfo(0)
wasp.TrunkSize = InventoryTile.Tile1111 wasp.TrunkSize = InventoryTile.Tile1111
wasp.TrunkOffset = 30 wasp.TrunkOffset = 30
wasp.TrunkLocation = Vector3(-4.6f, 0f, 0f) wasp.TrunkLocation = Vector3(-4.6f, 0f, 0f)
@ -6686,19 +6697,19 @@ object GlobalDefinitions {
liberator.RepairIfDestroyed = false liberator.RepairIfDestroyed = false
liberator.MaxShields = 500 liberator.MaxShields = 500
liberator.CanFly = true liberator.CanFly = true
liberator.Seats += 0 -> new SeatDefinition() liberator.Seats += 0 -> new SeatDefinition()
liberator.Seats += 1 -> bailableSeat liberator.Seats += 1 -> bailableSeat
liberator.Seats += 2 -> bailableSeat liberator.Seats += 2 -> bailableSeat
liberator.controlledWeapons += 0 -> 3 liberator.controlledWeapons += 0 -> 3
liberator.controlledWeapons += 1 -> 4 liberator.controlledWeapons += 1 -> 4
liberator.controlledWeapons += 2 -> 5 liberator.controlledWeapons += 2 -> 5
liberator.Weapons += 3 -> liberator_weapon_system liberator.Weapons += 3 -> liberator_weapon_system
liberator.Weapons += 4 -> liberator_bomb_bay liberator.Weapons += 4 -> liberator_bomb_bay
liberator.Weapons += 5 -> liberator_25mm_cannon liberator.Weapons += 5 -> liberator_25mm_cannon
liberator.MountPoints += 1 -> MountInfo(0) liberator.MountPoints += 1 -> MountInfo(0)
liberator.MountPoints += 2 -> MountInfo(1) liberator.MountPoints += 2 -> MountInfo(1)
liberator.MountPoints += 3 -> MountInfo(1) liberator.MountPoints += 3 -> MountInfo(1)
liberator.MountPoints += 4 -> MountInfo(2) liberator.MountPoints += 4 -> MountInfo(2)
liberator.TrunkSize = InventoryTile.Tile1515 liberator.TrunkSize = InventoryTile.Tile1515
liberator.TrunkOffset = 30 liberator.TrunkOffset = 30
liberator.TrunkLocation = Vector3(-0.76f, -1.88f, 0f) liberator.TrunkLocation = Vector3(-0.76f, -1.88f, 0f)
@ -6728,19 +6739,19 @@ object GlobalDefinitions {
vulture.RepairIfDestroyed = false vulture.RepairIfDestroyed = false
vulture.MaxShields = 500 vulture.MaxShields = 500
vulture.CanFly = true vulture.CanFly = true
vulture.Seats += 0 -> new SeatDefinition() vulture.Seats += 0 -> new SeatDefinition()
vulture.Seats += 1 -> bailableSeat vulture.Seats += 1 -> bailableSeat
vulture.Seats += 2 -> bailableSeat vulture.Seats += 2 -> bailableSeat
vulture.controlledWeapons += 0 -> 3 vulture.controlledWeapons += 0 -> 3
vulture.controlledWeapons += 1 -> 4 vulture.controlledWeapons += 1 -> 4
vulture.controlledWeapons += 2 -> 5 vulture.controlledWeapons += 2 -> 5
vulture.Weapons += 3 -> vulture_nose_weapon_system vulture.Weapons += 3 -> vulture_nose_weapon_system
vulture.Weapons += 4 -> vulture_bomb_bay vulture.Weapons += 4 -> vulture_bomb_bay
vulture.Weapons += 5 -> vulture_tail_cannon vulture.Weapons += 5 -> vulture_tail_cannon
vulture.MountPoints += 1 -> MountInfo(0) vulture.MountPoints += 1 -> MountInfo(0)
vulture.MountPoints += 2 -> MountInfo(1) vulture.MountPoints += 2 -> MountInfo(1)
vulture.MountPoints += 3 -> MountInfo(1) vulture.MountPoints += 3 -> MountInfo(1)
vulture.MountPoints += 4 -> MountInfo(2) vulture.MountPoints += 4 -> MountInfo(2)
vulture.TrunkSize = InventoryTile.Tile1611 vulture.TrunkSize = InventoryTile.Tile1611
vulture.TrunkOffset = 30 vulture.TrunkOffset = 30
vulture.TrunkLocation = Vector3(-0.76f, -1.88f, 0f) vulture.TrunkLocation = Vector3(-0.76f, -1.88f, 0f)
@ -6790,25 +6801,25 @@ object GlobalDefinitions {
bailable = true bailable = true
restriction = MaxOnly restriction = MaxOnly
} }
dropship.Seats += 11 -> bailableSeat dropship.Seats += 11 -> bailableSeat
dropship.controlledWeapons += 1 -> 12 dropship.controlledWeapons += 1 -> 12
dropship.controlledWeapons += 2 -> 13 dropship.controlledWeapons += 2 -> 13
dropship.controlledWeapons += 11 -> 14 dropship.controlledWeapons += 11 -> 14
dropship.Weapons += 12 -> cannon_dropship_20mm dropship.Weapons += 12 -> cannon_dropship_20mm
dropship.Weapons += 13 -> cannon_dropship_20mm dropship.Weapons += 13 -> cannon_dropship_20mm
dropship.Weapons += 14 -> dropship_rear_turret dropship.Weapons += 14 -> dropship_rear_turret
dropship.Cargo += 15 -> new CargoDefinition() { dropship.Cargo += 15 -> new CargoDefinition() {
restriction = SmallCargo restriction = SmallCargo
} }
dropship.MountPoints += 1 -> MountInfo(0) dropship.MountPoints += 1 -> MountInfo(0)
dropship.MountPoints += 2 -> MountInfo(11) dropship.MountPoints += 2 -> MountInfo(11)
dropship.MountPoints += 3 -> MountInfo(1) dropship.MountPoints += 3 -> MountInfo(1)
dropship.MountPoints += 4 -> MountInfo(2) dropship.MountPoints += 4 -> MountInfo(2)
dropship.MountPoints += 5 -> MountInfo(3) dropship.MountPoints += 5 -> MountInfo(3)
dropship.MountPoints += 6 -> MountInfo(4) dropship.MountPoints += 6 -> MountInfo(4)
dropship.MountPoints += 7 -> MountInfo(5) dropship.MountPoints += 7 -> MountInfo(5)
dropship.MountPoints += 8 -> MountInfo(6) dropship.MountPoints += 8 -> MountInfo(6)
dropship.MountPoints += 9 -> MountInfo(7) dropship.MountPoints += 9 -> MountInfo(7)
dropship.MountPoints += 10 -> MountInfo(8) dropship.MountPoints += 10 -> MountInfo(8)
dropship.MountPoints += 11 -> MountInfo(9) dropship.MountPoints += 11 -> MountInfo(9)
dropship.MountPoints += 12 -> MountInfo(10) dropship.MountPoints += 12 -> MountInfo(10)
@ -6843,28 +6854,28 @@ object GlobalDefinitions {
galaxy_gunship.RepairIfDestroyed = false galaxy_gunship.RepairIfDestroyed = false
galaxy_gunship.MaxShields = 1200 galaxy_gunship.MaxShields = 1200
galaxy_gunship.CanFly = true galaxy_gunship.CanFly = true
galaxy_gunship.Seats += 0 -> new SeatDefinition() galaxy_gunship.Seats += 0 -> new SeatDefinition()
galaxy_gunship.Seats += 1 -> bailableSeat galaxy_gunship.Seats += 1 -> bailableSeat
galaxy_gunship.Seats += 2 -> bailableSeat galaxy_gunship.Seats += 2 -> bailableSeat
galaxy_gunship.Seats += 3 -> bailableSeat galaxy_gunship.Seats += 3 -> bailableSeat
galaxy_gunship.Seats += 4 -> bailableSeat galaxy_gunship.Seats += 4 -> bailableSeat
galaxy_gunship.Seats += 5 -> bailableSeat galaxy_gunship.Seats += 5 -> bailableSeat
galaxy_gunship.controlledWeapons += 1 -> 6 galaxy_gunship.controlledWeapons += 1 -> 6
galaxy_gunship.controlledWeapons += 2 -> 7 galaxy_gunship.controlledWeapons += 2 -> 7
galaxy_gunship.controlledWeapons += 3 -> 8 galaxy_gunship.controlledWeapons += 3 -> 8
galaxy_gunship.controlledWeapons += 4 -> 9 galaxy_gunship.controlledWeapons += 4 -> 9
galaxy_gunship.controlledWeapons += 5 -> 10 galaxy_gunship.controlledWeapons += 5 -> 10
galaxy_gunship.Weapons += 6 -> galaxy_gunship_cannon galaxy_gunship.Weapons += 6 -> galaxy_gunship_cannon
galaxy_gunship.Weapons += 7 -> galaxy_gunship_cannon galaxy_gunship.Weapons += 7 -> galaxy_gunship_cannon
galaxy_gunship.Weapons += 8 -> galaxy_gunship_tailgun galaxy_gunship.Weapons += 8 -> galaxy_gunship_tailgun
galaxy_gunship.Weapons += 9 -> galaxy_gunship_gun galaxy_gunship.Weapons += 9 -> galaxy_gunship_gun
galaxy_gunship.Weapons += 10 -> galaxy_gunship_gun galaxy_gunship.Weapons += 10 -> galaxy_gunship_gun
galaxy_gunship.MountPoints += 1 -> MountInfo(0) galaxy_gunship.MountPoints += 1 -> MountInfo(0)
galaxy_gunship.MountPoints += 2 -> MountInfo(3) galaxy_gunship.MountPoints += 2 -> MountInfo(3)
galaxy_gunship.MountPoints += 3 -> MountInfo(1) galaxy_gunship.MountPoints += 3 -> MountInfo(1)
galaxy_gunship.MountPoints += 4 -> MountInfo(2) galaxy_gunship.MountPoints += 4 -> MountInfo(2)
galaxy_gunship.MountPoints += 5 -> MountInfo(4) galaxy_gunship.MountPoints += 5 -> MountInfo(4)
galaxy_gunship.MountPoints += 6 -> MountInfo(5) galaxy_gunship.MountPoints += 6 -> MountInfo(5)
galaxy_gunship.TrunkSize = InventoryTile.Tile1816 galaxy_gunship.TrunkSize = InventoryTile.Tile1816
galaxy_gunship.TrunkOffset = 30 galaxy_gunship.TrunkOffset = 30
galaxy_gunship.TrunkLocation = Vector3(-9.85f, 0f, 0f) galaxy_gunship.TrunkLocation = Vector3(-9.85f, 0f, 0f)
@ -6898,8 +6909,8 @@ object GlobalDefinitions {
lodestar.MaxShields = 1000 lodestar.MaxShields = 1000
lodestar.CanFly = true lodestar.CanFly = true
lodestar.Seats += 0 -> new SeatDefinition() lodestar.Seats += 0 -> new SeatDefinition()
lodestar.MountPoints += 1 -> MountInfo(0) lodestar.MountPoints += 1 -> MountInfo(0)
lodestar.MountPoints += 2 -> MountInfo(1) lodestar.MountPoints += 2 -> MountInfo(1)
lodestar.Cargo += 1 -> new CargoDefinition() lodestar.Cargo += 1 -> new CargoDefinition()
lodestar.Utilities += 2 -> UtilityType.lodestar_repair_terminal lodestar.Utilities += 2 -> UtilityType.lodestar_repair_terminal
lodestar.UtilityOffset += 2 -> Vector3(0, 20, 0) lodestar.UtilityOffset += 2 -> Vector3(0, 20, 0)
@ -6939,11 +6950,11 @@ object GlobalDefinitions {
phantasm.MaxShields = 500 phantasm.MaxShields = 500
phantasm.CanCloak = true phantasm.CanCloak = true
phantasm.CanFly = true phantasm.CanFly = true
phantasm.Seats += 0 -> new SeatDefinition() phantasm.Seats += 0 -> new SeatDefinition()
phantasm.Seats += 1 -> bailableSeat phantasm.Seats += 1 -> bailableSeat
phantasm.Seats += 2 -> bailableSeat phantasm.Seats += 2 -> bailableSeat
phantasm.Seats += 3 -> bailableSeat phantasm.Seats += 3 -> bailableSeat
phantasm.Seats += 4 -> bailableSeat phantasm.Seats += 4 -> bailableSeat
phantasm.MountPoints += 1 -> MountInfo(0) phantasm.MountPoints += 1 -> MountInfo(0)
phantasm.MountPoints += 2 -> MountInfo(1) phantasm.MountPoints += 2 -> MountInfo(1)
phantasm.MountPoints += 3 -> MountInfo(2) phantasm.MountPoints += 3 -> MountInfo(2)
@ -7002,15 +7013,15 @@ object GlobalDefinitions {
physically, they correlate to positions in the HART building rather than with the shuttle model by itself; physically, they correlate to positions in the HART building rather than with the shuttle model by itself;
set the shuttle pad based on the zonemap extraction values then position the shuttle relative to that pad; set the shuttle pad based on the zonemap extraction values then position the shuttle relative to that pad;
rotation based on the shuttle should place these offsets in the HART lobby whose gantry hall corresponds to that mount index rotation based on the shuttle should place these offsets in the HART lobby whose gantry hall corresponds to that mount index
*/ */
orbital_shuttle.MountPoints += 1 -> MountInfo(0, Vector3(-62, 4, -28.2f)) orbital_shuttle.MountPoints += 1 -> MountInfo(0, Vector3(-62, 4, -28.2f))
orbital_shuttle.MountPoints += 2 -> MountInfo(0, Vector3(-62, 28, -28.2f)) orbital_shuttle.MountPoints += 2 -> MountInfo(0, Vector3(-62, 28, -28.2f))
orbital_shuttle.MountPoints += 3 -> MountInfo(0, Vector3(-62, 4, -18.2f)) orbital_shuttle.MountPoints += 3 -> MountInfo(0, Vector3(-62, 4, -18.2f))
orbital_shuttle.MountPoints += 4 -> MountInfo(0, Vector3(-62, 28, -18.2f)) orbital_shuttle.MountPoints += 4 -> MountInfo(0, Vector3(-62, 28, -18.2f))
orbital_shuttle.MountPoints += 5 -> MountInfo(0, Vector3( 62, 4, -28.2f)) orbital_shuttle.MountPoints += 5 -> MountInfo(0, Vector3(62, 4, -28.2f))
orbital_shuttle.MountPoints += 6 -> MountInfo(0, Vector3( 62, 28, -28.2f)) orbital_shuttle.MountPoints += 6 -> MountInfo(0, Vector3(62, 28, -28.2f))
orbital_shuttle.MountPoints += 7 -> MountInfo(0, Vector3( 62, 4, -18.2f)) orbital_shuttle.MountPoints += 7 -> MountInfo(0, Vector3(62, 4, -18.2f))
orbital_shuttle.MountPoints += 8 -> MountInfo(0, Vector3( 62, 28, -18.2f)) orbital_shuttle.MountPoints += 8 -> MountInfo(0, Vector3(62, 28, -18.2f))
orbital_shuttle.TrunkSize = InventoryTile.None orbital_shuttle.TrunkSize = InventoryTile.None
orbital_shuttle.Packet = new OrbitalShuttleConverter orbital_shuttle.Packet = new OrbitalShuttleConverter
orbital_shuttle.DeconstructionTime = None orbital_shuttle.DeconstructionTime = None
@ -7023,9 +7034,9 @@ object GlobalDefinitions {
* Initialize `Deployable` globals. * Initialize `Deployable` globals.
*/ */
private def init_deployables(): Unit = { private def init_deployables(): Unit = {
val mine = GeometryForm.representByCylinder(radius = 0.1914f, height = 0.0957f) _ val mine = GeometryForm.representByCylinder(radius = 0.1914f, height = 0.0957f) _
val smallTurret = GeometryForm.representByCylinder(radius = 0.48435f, height = 1.23438f) _ val smallTurret = GeometryForm.representByCylinder(radius = 0.48435f, height = 1.23438f) _
val sensor = GeometryForm.representByCylinder(radius = 0.1914f, height = 1.21875f) _ val sensor = GeometryForm.representByCylinder(radius = 0.1914f, height = 1.21875f) _
val largeTurret = GeometryForm.representByCylinder(radius = 0.8437f, height = 2.29687f) _ val largeTurret = GeometryForm.representByCylinder(radius = 0.8437f, height = 2.29687f) _
boomer.Name = "boomer" boomer.Name = "boomer"
@ -7197,11 +7208,11 @@ object GlobalDefinitions {
portable_manned_turret.Damageable = true portable_manned_turret.Damageable = true
portable_manned_turret.Repairable = true portable_manned_turret.Repairable = true
portable_manned_turret.RepairIfDestroyed = false portable_manned_turret.RepairIfDestroyed = false
portable_manned_turret.controlledWeapons += 0 -> 1 portable_manned_turret.controlledWeapons += 0 -> 1
portable_manned_turret.WeaponPaths += 1 -> new mutable.HashMap() portable_manned_turret.WeaponPaths += 1 -> new mutable.HashMap()
portable_manned_turret.WeaponPaths(1) += TurretUpgrade.None -> energy_gun portable_manned_turret.WeaponPaths(1) += TurretUpgrade.None -> energy_gun
portable_manned_turret.MountPoints += 1 -> MountInfo(0) portable_manned_turret.MountPoints += 1 -> MountInfo(0)
portable_manned_turret.MountPoints += 2 -> MountInfo(0) portable_manned_turret.MountPoints += 2 -> MountInfo(0)
portable_manned_turret.ReserveAmmunition = true portable_manned_turret.ReserveAmmunition = true
portable_manned_turret.FactionLocked = true portable_manned_turret.FactionLocked = true
portable_manned_turret.Packet = fieldTurretConverter portable_manned_turret.Packet = fieldTurretConverter
@ -7227,9 +7238,9 @@ object GlobalDefinitions {
portable_manned_turret_nc.RepairIfDestroyed = false portable_manned_turret_nc.RepairIfDestroyed = false
portable_manned_turret_nc.WeaponPaths += 1 -> new mutable.HashMap() portable_manned_turret_nc.WeaponPaths += 1 -> new mutable.HashMap()
portable_manned_turret_nc.WeaponPaths(1) += TurretUpgrade.None -> energy_gun_nc portable_manned_turret_nc.WeaponPaths(1) += TurretUpgrade.None -> energy_gun_nc
portable_manned_turret_nc.controlledWeapons += 0 -> 1 portable_manned_turret_nc.controlledWeapons += 0 -> 1
portable_manned_turret_nc.MountPoints += 1 -> MountInfo(0) portable_manned_turret_nc.MountPoints += 1 -> MountInfo(0)
portable_manned_turret_nc.MountPoints += 2 -> MountInfo(0) portable_manned_turret_nc.MountPoints += 2 -> MountInfo(0)
portable_manned_turret_nc.ReserveAmmunition = true portable_manned_turret_nc.ReserveAmmunition = true
portable_manned_turret_nc.FactionLocked = true portable_manned_turret_nc.FactionLocked = true
portable_manned_turret_nc.Packet = fieldTurretConverter portable_manned_turret_nc.Packet = fieldTurretConverter
@ -7255,9 +7266,9 @@ object GlobalDefinitions {
portable_manned_turret_tr.RepairIfDestroyed = false portable_manned_turret_tr.RepairIfDestroyed = false
portable_manned_turret_tr.WeaponPaths += 1 -> new mutable.HashMap() portable_manned_turret_tr.WeaponPaths += 1 -> new mutable.HashMap()
portable_manned_turret_tr.WeaponPaths(1) += TurretUpgrade.None -> energy_gun_tr portable_manned_turret_tr.WeaponPaths(1) += TurretUpgrade.None -> energy_gun_tr
portable_manned_turret_tr.controlledWeapons += 0 -> 1 portable_manned_turret_tr.controlledWeapons += 0 -> 1
portable_manned_turret_tr.MountPoints += 1 -> MountInfo(0) portable_manned_turret_tr.MountPoints += 1 -> MountInfo(0)
portable_manned_turret_tr.MountPoints += 2 -> MountInfo(0) portable_manned_turret_tr.MountPoints += 2 -> MountInfo(0)
portable_manned_turret_tr.ReserveAmmunition = true portable_manned_turret_tr.ReserveAmmunition = true
portable_manned_turret_tr.FactionLocked = true portable_manned_turret_tr.FactionLocked = true
portable_manned_turret_tr.Packet = fieldTurretConverter portable_manned_turret_tr.Packet = fieldTurretConverter
@ -7283,9 +7294,9 @@ object GlobalDefinitions {
portable_manned_turret_vs.RepairIfDestroyed = false portable_manned_turret_vs.RepairIfDestroyed = false
portable_manned_turret_vs.WeaponPaths += 1 -> new mutable.HashMap() portable_manned_turret_vs.WeaponPaths += 1 -> new mutable.HashMap()
portable_manned_turret_vs.WeaponPaths(1) += TurretUpgrade.None -> energy_gun_vs portable_manned_turret_vs.WeaponPaths(1) += TurretUpgrade.None -> energy_gun_vs
portable_manned_turret_vs.controlledWeapons += 0 -> 1 portable_manned_turret_vs.controlledWeapons += 0 -> 1
portable_manned_turret_vs.MountPoints += 1 -> MountInfo(0) portable_manned_turret_vs.MountPoints += 1 -> MountInfo(0)
portable_manned_turret_vs.MountPoints += 2 -> MountInfo(0) portable_manned_turret_vs.MountPoints += 2 -> MountInfo(0)
portable_manned_turret_vs.ReserveAmmunition = true portable_manned_turret_vs.ReserveAmmunition = true
portable_manned_turret_vs.FactionLocked = true portable_manned_turret_vs.FactionLocked = true
portable_manned_turret_vs.Packet = fieldTurretConverter portable_manned_turret_vs.Packet = fieldTurretConverter
@ -7477,7 +7488,8 @@ object GlobalDefinitions {
implant_terminal_interface.MaxHealth = 500 implant_terminal_interface.MaxHealth = 500
implant_terminal_interface.Damageable = false //TODO true implant_terminal_interface.Damageable = false //TODO true
implant_terminal_interface.Repairable = true implant_terminal_interface.Repairable = true
implant_terminal_interface.autoRepair = AutoRepairStats(1, 5000, 200, 1) //TODO amount and drain are default? undefined? implant_terminal_interface.autoRepair =
AutoRepairStats(1, 5000, 200, 1) //TODO amount and drain are default? undefined?
implant_terminal_interface.RepairIfDestroyed = true implant_terminal_interface.RepairIfDestroyed = true
//TODO will need geometry when Damageable = true //TODO will need geometry when Damageable = true
@ -7791,8 +7803,8 @@ object GlobalDefinitions {
manned_turret.WeaponPaths(1) += TurretUpgrade.None -> phalanx_sgl_hevgatcan manned_turret.WeaponPaths(1) += TurretUpgrade.None -> phalanx_sgl_hevgatcan
manned_turret.WeaponPaths(1) += TurretUpgrade.AVCombo -> phalanx_avcombo manned_turret.WeaponPaths(1) += TurretUpgrade.AVCombo -> phalanx_avcombo
manned_turret.WeaponPaths(1) += TurretUpgrade.FlakCombo -> phalanx_flakcombo manned_turret.WeaponPaths(1) += TurretUpgrade.FlakCombo -> phalanx_flakcombo
manned_turret.controlledWeapons += 0 -> 1 manned_turret.controlledWeapons += 0 -> 1
manned_turret.MountPoints += 1 -> MountInfo(0) manned_turret.MountPoints += 1 -> MountInfo(0)
manned_turret.FactionLocked = true manned_turret.FactionLocked = true
manned_turret.ReserveAmmunition = false manned_turret.ReserveAmmunition = false
manned_turret.explodes = true manned_turret.explodes = true
@ -7815,9 +7827,9 @@ object GlobalDefinitions {
vanu_sentry_turret.RepairIfDestroyed = true vanu_sentry_turret.RepairIfDestroyed = true
vanu_sentry_turret.WeaponPaths += 1 -> new mutable.HashMap() vanu_sentry_turret.WeaponPaths += 1 -> new mutable.HashMap()
vanu_sentry_turret.WeaponPaths(1) += TurretUpgrade.None -> vanu_sentry_turret_weapon vanu_sentry_turret.WeaponPaths(1) += TurretUpgrade.None -> vanu_sentry_turret_weapon
vanu_sentry_turret.controlledWeapons += 0 -> 1 vanu_sentry_turret.controlledWeapons += 0 -> 1
vanu_sentry_turret.MountPoints += 1 -> MountInfo(0) vanu_sentry_turret.MountPoints += 1 -> MountInfo(0)
vanu_sentry_turret.MountPoints += 2 -> MountInfo(0) vanu_sentry_turret.MountPoints += 2 -> MountInfo(0)
vanu_sentry_turret.FactionLocked = false vanu_sentry_turret.FactionLocked = false
vanu_sentry_turret.ReserveAmmunition = false vanu_sentry_turret.ReserveAmmunition = false
vanu_sentry_turret.Geometry = GeometryForm.representByCylinder(radius = 1.76311f, height = 3.984375f) vanu_sentry_turret.Geometry = GeometryForm.representByCylinder(radius = 1.76311f, height = 3.984375f)

View file

@ -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)
} }

View file

@ -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

View file

@ -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]
} }

View file

@ -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"))
@ -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
} }

View file

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

View file

@ -63,8 +63,9 @@ class ChatService(context: ActorContext[ChatService.Command]) extends AbstractBe
case Message(session, message, channel) => 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

View file

@ -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 =>