mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-07-12 15:04:39 +00:00
Yet Another Corpse Fix (#637)
* numeric session ids now restored; LoginActor knows about connection address; corpses are barren unless searched * session and login id/counter moved under server; function literal definition changed * corpse channel * trying to fix docker as per set-env requirement changes; I don't know what I'm doing
This commit is contained in:
parent
e357663364
commit
babd455753
12 changed files with 196 additions and 169 deletions
2
.github/workflows/publish.yaml
vendored
2
.github/workflows/publish.yaml
vendored
|
|
@ -36,7 +36,7 @@ jobs:
|
||||||
uses: rlespinasse/github-slug-action@v2.x
|
uses: rlespinasse/github-slug-action@v2.x
|
||||||
- name: Set variables
|
- name: Set variables
|
||||||
run: |
|
run: |
|
||||||
echo "::set-env name=REPOSITORY::$(echo $GITHUB_REPOSITORY | tr '[A-Z]' '[a-z]')"
|
echo "REPOSITORY=$(echo $GITHUB_REPOSITORY | tr '[A-Z]' '[a-z]')" >> $GITHUB_ENV
|
||||||
- name: Build and push Docker image
|
- name: Build and push Docker image
|
||||||
uses: docker/build-push-action@v2.2.0
|
uses: docker/build-push-action@v2.2.0
|
||||||
with:
|
with:
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import xerial.sbt.pack.PackPlugin._
|
||||||
lazy val psforeverSettings = Seq(
|
lazy val psforeverSettings = Seq(
|
||||||
organization := "net.psforever",
|
organization := "net.psforever",
|
||||||
version := "1.0.2-SNAPSHOT",
|
version := "1.0.2-SNAPSHOT",
|
||||||
scalaVersion := "2.13.4",
|
scalaVersion := "2.13.3",
|
||||||
Global / cancelable := false,
|
Global / cancelable := false,
|
||||||
semanticdbEnabled := true,
|
semanticdbEnabled := true,
|
||||||
semanticdbVersion := scalafixSemanticdb.revision,
|
semanticdbVersion := scalafixSemanticdb.revision,
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import java.net.{InetAddress, InetSocketAddress}
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.util.UUID.randomUUID
|
import java.util.UUID.randomUUID
|
||||||
|
import java.util.concurrent.atomic.AtomicLong
|
||||||
|
|
||||||
import akka.actor.ActorSystem
|
import akka.actor.ActorSystem
|
||||||
import akka.actor.typed.ActorRef
|
import akka.actor.typed.ActorRef
|
||||||
|
|
@ -19,7 +20,7 @@ import net.psforever.login.psadmin.PsAdminActor
|
||||||
import net.psforever.login._
|
import net.psforever.login._
|
||||||
import net.psforever.objects.Default
|
import net.psforever.objects.Default
|
||||||
import net.psforever.objects.zones._
|
import net.psforever.objects.zones._
|
||||||
import net.psforever.services.account.{AccountIntermediaryService, AccountPersistenceService}
|
import net.psforever.services.account.{AccountIntermediaryService, AccountPersistenceService, ReceiveIPAddress}
|
||||||
import net.psforever.services.chat.ChatService
|
import net.psforever.services.chat.ChatService
|
||||||
import net.psforever.services.galaxy.GalaxyService
|
import net.psforever.services.galaxy.GalaxyService
|
||||||
import net.psforever.services.properties.PropertyOverrideManager
|
import net.psforever.services.properties.PropertyOverrideManager
|
||||||
|
|
@ -95,19 +96,21 @@ object Server {
|
||||||
Default(system)
|
Default(system)
|
||||||
|
|
||||||
// typed to classic wrappers for login and session actors
|
// typed to classic wrappers for login and session actors
|
||||||
val login = (ref: ActorRef[MiddlewareActor.Command], connectionId: String) => {
|
val login = (ref: ActorRef[MiddlewareActor.Command], info: InetSocketAddress, connectionId: String) => {
|
||||||
|
import net.psforever.services.account.IPAddress
|
||||||
Behaviors.setup[PlanetSidePacket](context => {
|
Behaviors.setup[PlanetSidePacket](context => {
|
||||||
val actor = context.actorOf(classic.Props(new LoginActor(ref, connectionId)), "login")
|
val actor = context.actorOf(classic.Props(new LoginActor(ref, connectionId, Login.getNewId())), "login")
|
||||||
|
actor ! ReceiveIPAddress(new IPAddress(info))
|
||||||
Behaviors.receiveMessage(message => {
|
Behaviors.receiveMessage(message => {
|
||||||
actor ! message
|
actor ! message
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
val session = (ref: ActorRef[MiddlewareActor.Command], connectionId: String) => {
|
val session = (ref: ActorRef[MiddlewareActor.Command], info: InetSocketAddress, connectionId: String) => {
|
||||||
Behaviors.setup[PlanetSidePacket](context => {
|
Behaviors.setup[PlanetSidePacket](context => {
|
||||||
val uuid = randomUUID().toString
|
val uuid = randomUUID().toString
|
||||||
val actor = context.actorOf(classic.Props(new SessionActor(ref, connectionId)), s"session-${uuid}")
|
val actor = context.actorOf(classic.Props(new SessionActor(ref, connectionId, Session.getNewId())), s"session-$uuid")
|
||||||
Behaviors.receiveMessage(message => {
|
Behaviors.receiveMessage(message => {
|
||||||
actor ! message
|
actor ! message
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
@ -230,6 +233,30 @@ object Server {
|
||||||
case _ =>
|
case _ =>
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sealed trait AuthoritativeCounter {
|
||||||
|
/** the id accumulator */
|
||||||
|
private val masterIdKeyRing: AtomicLong = new AtomicLong(0L)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Poll the next id.
|
||||||
|
* @return a id
|
||||||
|
*/
|
||||||
|
def nextId(): Long = masterIdKeyRing.get()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the next available id.
|
||||||
|
* Increment the counter.
|
||||||
|
* @return a id
|
||||||
|
*/
|
||||||
|
def getNewId(): Long = {
|
||||||
|
val oldId = masterIdKeyRing.getAndIncrement()
|
||||||
|
oldId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object Session extends AuthoritativeCounter
|
||||||
|
|
||||||
|
object Login extends AuthoritativeCounter
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import net.psforever.objects.serverobject.structures.StructureType
|
||||||
import net.psforever.objects.{GlobalDefinitions, Player, Vehicle}
|
import net.psforever.objects.{GlobalDefinitions, Player, Vehicle}
|
||||||
import net.psforever.objects.zones.Zone
|
import net.psforever.objects.zones.Zone
|
||||||
import net.psforever.types.{PlanetSideGUID, _}
|
import net.psforever.types.{PlanetSideGUID, _}
|
||||||
import net.psforever.services.RemoverActor
|
|
||||||
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||||
import akka.actor.typed.scaladsl.adapter._
|
import akka.actor.typed.scaladsl.adapter._
|
||||||
import net.psforever.actors.zone.ZoneActor
|
import net.psforever.actors.zone.ZoneActor
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import net.psforever.packet.game._
|
||||||
import net.psforever.persistence
|
import net.psforever.persistence
|
||||||
import net.psforever.services.ServiceManager
|
import net.psforever.services.ServiceManager
|
||||||
import net.psforever.services.ServiceManager.Lookup
|
import net.psforever.services.ServiceManager.Lookup
|
||||||
import net.psforever.services.account.{ReceiveIPAddress, RetrieveIPAddress, StoreAccountData}
|
import net.psforever.services.account.{ReceiveIPAddress, StoreAccountData}
|
||||||
import net.psforever.types.PlanetSideEmpire
|
import net.psforever.types.PlanetSideEmpire
|
||||||
import net.psforever.util.Config
|
import net.psforever.util.Config
|
||||||
import net.psforever.util.Database._
|
import net.psforever.util.Database._
|
||||||
|
|
@ -42,7 +42,7 @@ class LoginActor(
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class LoginActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], connectionId: String)
|
class LoginActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], connectionId: String, sessionId: Long)
|
||||||
extends Actor
|
extends Actor
|
||||||
with MDCContextAware {
|
with MDCContextAware {
|
||||||
private[this] val log = org.log4s.getLogger
|
private[this] val log = org.log4s.getLogger
|
||||||
|
|
@ -53,7 +53,6 @@ class LoginActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], conne
|
||||||
|
|
||||||
val usernameRegex = """[A-Za-z0-9]{3,}""".r
|
val usernameRegex = """[A-Za-z0-9]{3,}""".r
|
||||||
|
|
||||||
var sessionId: Long = 0
|
|
||||||
var leftRef: ActorRef = ActorRef.noSender
|
var leftRef: ActorRef = ActorRef.noSender
|
||||||
var rightRef: ActorRef = ActorRef.noSender
|
var rightRef: ActorRef = ActorRef.noSender
|
||||||
var accountIntermediary: ActorRef = ActorRef.noSender
|
var accountIntermediary: ActorRef = ActorRef.noSender
|
||||||
|
|
@ -100,8 +99,6 @@ class LoginActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], conne
|
||||||
|
|
||||||
val clientVersion = s"Client Version: $majorVersion.$minorVersion.$revision, $buildDate"
|
val clientVersion = s"Client Version: $majorVersion.$minorVersion.$revision, $buildDate"
|
||||||
|
|
||||||
accountIntermediary ! RetrieveIPAddress(sessionId)
|
|
||||||
|
|
||||||
if (token.isDefined)
|
if (token.isDefined)
|
||||||
log.info(s"New login UN:$username Token:${token.get}. $clientVersion")
|
log.info(s"New login UN:$username Token:${token.get}. $clientVersion")
|
||||||
else {
|
else {
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ object MiddlewareActor {
|
||||||
def apply(
|
def apply(
|
||||||
socket: ActorRef[Udp.Command],
|
socket: ActorRef[Udp.Command],
|
||||||
sender: InetSocketAddress,
|
sender: InetSocketAddress,
|
||||||
next: (ActorRef[Command], String) => Behavior[PlanetSidePacket],
|
next: (ActorRef[Command], InetSocketAddress, String) => Behavior[PlanetSidePacket],
|
||||||
connectionId: String
|
connectionId: String
|
||||||
): Behavior[Command] =
|
): Behavior[Command] =
|
||||||
Behaviors.setup(context => new MiddlewareActor(context, socket, sender, next, connectionId).start())
|
Behaviors.setup(context => new MiddlewareActor(context, socket, sender, next, connectionId).start())
|
||||||
|
|
@ -83,7 +83,7 @@ class MiddlewareActor(
|
||||||
context: ActorContext[MiddlewareActor.Command],
|
context: ActorContext[MiddlewareActor.Command],
|
||||||
socket: ActorRef[Udp.Command],
|
socket: ActorRef[Udp.Command],
|
||||||
sender: InetSocketAddress,
|
sender: InetSocketAddress,
|
||||||
next: (ActorRef[MiddlewareActor.Command], String) => Behavior[PlanetSidePacket],
|
next: (ActorRef[MiddlewareActor.Command], InetSocketAddress, String) => Behavior[PlanetSidePacket],
|
||||||
connectionId: String
|
connectionId: String
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
@ -102,7 +102,7 @@ class MiddlewareActor(
|
||||||
var crypto: Option[CryptoCoding] = None
|
var crypto: Option[CryptoCoding] = None
|
||||||
|
|
||||||
val nextActor: ActorRef[PlanetSidePacket] =
|
val nextActor: ActorRef[PlanetSidePacket] =
|
||||||
context.spawnAnonymous(next(context.self, connectionId), ActorTags(s"id=${connectionId}"))
|
context.spawnAnonymous(next(context.self, sender, connectionId), ActorTags(s"id=$connectionId"))
|
||||||
|
|
||||||
/** Queue of incoming packets (plus sequence numbers and timestamps) that arrived in the wrong order */
|
/** Queue of incoming packets (plus sequence numbers and timestamps) that arrived in the wrong order */
|
||||||
val inReorderQueue: ListBuffer[(PlanetSidePacket, Int, Long)] = ListBuffer()
|
val inReorderQueue: ListBuffer[(PlanetSidePacket, Int, Long)] = ListBuffer()
|
||||||
|
|
@ -329,7 +329,7 @@ class MiddlewareActor(
|
||||||
connectionClose()
|
connectionClose()
|
||||||
}
|
}
|
||||||
case Failure(e) =>
|
case Failure(e) =>
|
||||||
log.error(s"Could not decode packet in cryptoSetup: ${e}")
|
log.error(s"Could not decode packet in cryptoSetup: $e")
|
||||||
connectionClose()
|
connectionClose()
|
||||||
}
|
}
|
||||||
case other =>
|
case other =>
|
||||||
|
|
@ -420,7 +420,7 @@ class MiddlewareActor(
|
||||||
inReorderQueue.addOne((packet, sequence, System.currentTimeMillis()))
|
inReorderQueue.addOne((packet, sequence, System.currentTimeMillis()))
|
||||||
}
|
}
|
||||||
case Successful((packet, None)) => in(packet)
|
case Successful((packet, None)) => in(packet)
|
||||||
case Failure(e) => log.error(s"Could not decode packet: ${e}")
|
case Failure(e) => log.error(s"Could not decode packet: $e")
|
||||||
}
|
}
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
||||||
|
|
@ -483,7 +483,7 @@ class MiddlewareActor(
|
||||||
case RelatedA(slot, subslot) =>
|
case RelatedA(slot, subslot) =>
|
||||||
log.info(s"Client indicated a packet is missing prior to slot '$slot' and subslot '$subslot'")
|
log.info(s"Client indicated a packet is missing prior to slot '$slot' and subslot '$subslot'")
|
||||||
outSlottedMetaPackets.find(_.subslot == subslot - 1) match {
|
outSlottedMetaPackets.find(_.subslot == subslot - 1) match {
|
||||||
case Some(packet) => outQueueBundled.enqueue(packet)
|
case Some(_packet) => outQueueBundled.enqueue(_packet)
|
||||||
case None => log.warn(s"Client requested unknown subslot '$subslot'")
|
case None => log.warn(s"Client requested unknown subslot '$subslot'")
|
||||||
}
|
}
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
@ -526,7 +526,7 @@ class MiddlewareActor(
|
||||||
|
|
||||||
def in(packet: Attempt[PlanetSidePacket]): Unit = {
|
def in(packet: Attempt[PlanetSidePacket]): Unit = {
|
||||||
packet match {
|
packet match {
|
||||||
case Successful(packet) => in(packet)
|
case Successful(_packet) => in(_packet)
|
||||||
case Failure(cause) => log.error(cause.message)
|
case Failure(cause) => log.error(cause.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ import scala.util.Random
|
||||||
object SocketActor {
|
object SocketActor {
|
||||||
def apply(
|
def apply(
|
||||||
address: InetSocketAddress,
|
address: InetSocketAddress,
|
||||||
next: (ActorRef[MiddlewareActor.Command], String) => Behavior[PlanetSidePacket]
|
next: (ActorRef[MiddlewareActor.Command], InetSocketAddress, String) => Behavior[PlanetSidePacket]
|
||||||
): Behavior[Command] =
|
): Behavior[Command] =
|
||||||
Behaviors.setup(context => new SocketActor(context, address, next).start())
|
Behaviors.setup(context => new SocketActor(context, address, next).start())
|
||||||
|
|
||||||
|
|
@ -64,7 +64,7 @@ object SocketActor {
|
||||||
case _: Udp.Received | _: Udp.Send =>
|
case _: Udp.Received | _: Udp.Send =>
|
||||||
simulate(message)
|
simulate(message)
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
case other =>
|
case _ =>
|
||||||
socketActor ! toSocket(message)
|
socketActor ! toSocket(message)
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
}
|
}
|
||||||
|
|
@ -97,7 +97,7 @@ object SocketActor {
|
||||||
class SocketActor(
|
class SocketActor(
|
||||||
context: ActorContext[SocketActor.Command],
|
context: ActorContext[SocketActor.Command],
|
||||||
address: InetSocketAddress,
|
address: InetSocketAddress,
|
||||||
next: (ActorRef[MiddlewareActor.Command], String) => Behavior[PlanetSidePacket]
|
next: (ActorRef[MiddlewareActor.Command], InetSocketAddress, String) => Behavior[PlanetSidePacket]
|
||||||
) {
|
) {
|
||||||
import SocketActor._
|
import SocketActor._
|
||||||
import SocketActor.Command
|
import SocketActor.Command
|
||||||
|
|
@ -174,13 +174,13 @@ class SocketActor(
|
||||||
case Udp.Received(data, remote) =>
|
case Udp.Received(data, remote) =>
|
||||||
incomingTimes(remote) = System.currentTimeMillis()
|
incomingTimes(remote) = System.currentTimeMillis()
|
||||||
val ref = packetActors.get(remote) match {
|
val ref = packetActors.get(remote) match {
|
||||||
case Some(ref) => ref
|
case Some(_ref) => _ref
|
||||||
case None =>
|
case None =>
|
||||||
val connectionId = randomUUID.toString
|
val connectionId = randomUUID.toString
|
||||||
val ref = context.spawn(
|
val ref = context.spawn(
|
||||||
MiddlewareActor(udpCommandAdapter, remote, next, connectionId),
|
MiddlewareActor(udpCommandAdapter, remote, next, connectionId),
|
||||||
s"middleware-${connectionId}",
|
s"middleware-$connectionId",
|
||||||
ActorTags(s"uuid=${connectionId}")
|
ActorTags(s"uuid=$connectionId")
|
||||||
)
|
)
|
||||||
context.watch(ref)
|
context.watch(ref)
|
||||||
packetActors(remote) = ref
|
packetActors(remote) = ref
|
||||||
|
|
@ -218,7 +218,7 @@ class SocketActor(
|
||||||
case (_, Terminated(ref)) =>
|
case (_, Terminated(ref)) =>
|
||||||
packetActors.find(_._2 == ref) match {
|
packetActors.find(_._2 == ref) match {
|
||||||
case Some((remote, _)) => packetActors.remove(remote)
|
case Some((remote, _)) => packetActors.remove(remote)
|
||||||
case None => log.warn(s"Received Terminated for unknown actor ${ref}")
|
case None => log.warn(s"Received Terminated for unknown actor $ref")
|
||||||
}
|
}
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -158,7 +158,7 @@ object SessionActor {
|
||||||
private final case class LoadedRemoteProjectile(projectile_guid: PlanetSideGUID, projectile: Option[Projectile])
|
private final case class LoadedRemoteProjectile(projectile_guid: PlanetSideGUID, projectile: Option[Projectile])
|
||||||
}
|
}
|
||||||
|
|
||||||
class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], connectionId: String)
|
class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], connectionId: String, sessionId: Long)
|
||||||
extends Actor
|
extends Actor
|
||||||
with MDCContextAware {
|
with MDCContextAware {
|
||||||
|
|
||||||
|
|
@ -5985,6 +5985,8 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
AccessVehicleContents(v)
|
AccessVehicleContents(v)
|
||||||
case o: LockerContainer =>
|
case o: LockerContainer =>
|
||||||
AccessGenericContainer(o)
|
AccessGenericContainer(o)
|
||||||
|
case p: Player if p.isBackpack =>
|
||||||
|
AccessCorpseContents(p)
|
||||||
case p: PlanetSideServerObject with Container =>
|
case p: PlanetSideServerObject with Container =>
|
||||||
accessedContainer = Some(p)
|
accessedContainer = Some(p)
|
||||||
case _ => ;
|
case _ => ;
|
||||||
|
|
@ -5993,27 +5995,59 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
|
|
||||||
def AccessGenericContainer(container: PlanetSideServerObject with Container): Unit = {
|
def AccessGenericContainer(container: PlanetSideServerObject with Container): Unit = {
|
||||||
accessedContainer = Some(container)
|
accessedContainer = Some(container)
|
||||||
DisplayContainerContents(container.GUID, container)
|
DisplayContainerContents(container.GUID, container.Inventory.Items)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Common preparation for interfacing with a vehicle.
|
* Common preparation for interfacing with a vehicle trunk.
|
||||||
* Join a vehicle-specific group for shared updates.
|
* Join a vehicle-specific group for shared updates.
|
||||||
* Construct every object in the vehicle's inventory for shared manipulation updates.
|
* Construct every object in the vehicle's inventory for shared manipulation updates.
|
||||||
|
* @see `Container.Inventory`
|
||||||
|
* @see `GridInventory.Items`
|
||||||
* @param vehicle the vehicle
|
* @param vehicle the vehicle
|
||||||
*/
|
*/
|
||||||
def AccessVehicleContents(vehicle: Vehicle): Unit = {
|
def AccessVehicleContents(vehicle: Vehicle): Unit = {
|
||||||
accessedContainer = Some(vehicle)
|
accessedContainer = Some(vehicle)
|
||||||
AccessVehicleContainerChannel(vehicle)
|
AccessContainerChannel(continent.VehicleEvents, vehicle.Actor.toString)
|
||||||
DisplayContainerContents(vehicle.GUID, vehicle)
|
DisplayContainerContents(vehicle.GUID, vehicle.Inventory.Items)
|
||||||
}
|
}
|
||||||
|
|
||||||
def AccessVehicleContainerChannel(container: PlanetSideServerObject): Unit = {
|
/**
|
||||||
continent.VehicleEvents ! Service.Join(s"${container.Actor}")
|
* Common preparation for interfacing with a corpse (former player's backpack).
|
||||||
|
* Join a corpse-specific group for shared updates.
|
||||||
|
* Construct every object in the player's hands and inventory for shared manipulation updates.
|
||||||
|
* @see `Container.Inventory`
|
||||||
|
* @see `GridInventory.Items`
|
||||||
|
* @see `Player.HolsterItems`
|
||||||
|
* @param tplayer the corpse
|
||||||
|
*/
|
||||||
|
def AccessCorpseContents(tplayer: Player): Unit = {
|
||||||
|
accessedContainer = Some(tplayer)
|
||||||
|
AccessContainerChannel(continent.AvatarEvents, tplayer.Actor.toString)
|
||||||
|
DisplayContainerContents(tplayer.GUID, tplayer.HolsterItems())
|
||||||
|
DisplayContainerContents(tplayer.GUID, tplayer.Inventory.Items)
|
||||||
}
|
}
|
||||||
|
|
||||||
def DisplayContainerContents(containerId: PlanetSideGUID, container: Container): Unit = {
|
/**
|
||||||
container.Inventory.Items.foreach(entry => {
|
* Join an entity-specific group for shared updates.
|
||||||
|
* @param events the event system bus to which to subscribe
|
||||||
|
* @param channel the channel name
|
||||||
|
*/
|
||||||
|
def AccessContainerChannel(events: ActorRef, channel: String): Unit = {
|
||||||
|
events ! Service.Join(channel)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Depict the contents of a container by building them in the local client
|
||||||
|
* in their container as a group of detailed entities.
|
||||||
|
* @see `ObjectCreateDetailedMessage`
|
||||||
|
* @see `ObjectCreateMessageParent`
|
||||||
|
* @see `PacketConverter.DetailedConstructorData`
|
||||||
|
* @param containerId the container's unique identifier
|
||||||
|
* @param items a list of the entities to be depicted
|
||||||
|
*/
|
||||||
|
def DisplayContainerContents(containerId: PlanetSideGUID, items: Iterable[InventoryItem]): Unit = {
|
||||||
|
items.foreach(entry => {
|
||||||
val obj = entry.obj
|
val obj = entry.obj
|
||||||
val objDef = obj.Definition
|
val objDef = obj.Definition
|
||||||
sendResponse(
|
sendResponse(
|
||||||
|
|
@ -6027,6 +6061,10 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For whatever conatiner the character considers itself accessing,
|
||||||
|
* initiate protocol to release it from "access".
|
||||||
|
*/
|
||||||
def UnaccessContainer(): Unit = {
|
def UnaccessContainer(): Unit = {
|
||||||
accessedContainer match {
|
accessedContainer match {
|
||||||
case Some(container) => UnaccessContainer(container)
|
case Some(container) => UnaccessContainer(container)
|
||||||
|
|
@ -6034,12 +6072,17 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For the target container, initiate protocol to release it from "access".
|
||||||
|
*/
|
||||||
def UnaccessContainer(container: Container): Unit = {
|
def UnaccessContainer(container: Container): Unit = {
|
||||||
container match {
|
container match {
|
||||||
case v: Vehicle =>
|
case v: Vehicle =>
|
||||||
UnaccessVehicleContainer(v)
|
UnaccessVehicleContainer(v)
|
||||||
case o: LockerContainer =>
|
case o: LockerContainer =>
|
||||||
UnaccessGenericContainer(o)
|
UnaccessGenericContainer(o)
|
||||||
|
case p: Player if p.isBackpack =>
|
||||||
|
UnaccessCorpseContainer(p)
|
||||||
case _: PlanetSideServerObject with Container =>
|
case _: PlanetSideServerObject with Container =>
|
||||||
accessedContainer = None
|
accessedContainer = None
|
||||||
case _ => ;
|
case _ => ;
|
||||||
|
|
@ -6048,7 +6091,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
|
|
||||||
def UnaccessGenericContainer(container: Container): Unit = {
|
def UnaccessGenericContainer(container: Container): Unit = {
|
||||||
accessedContainer = None
|
accessedContainer = None
|
||||||
HideContainerContents(container)
|
HideContainerContents(container.Inventory.Items)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -6062,18 +6105,42 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
if (vehicle.AccessingTrunk.contains(player.GUID)) {
|
if (vehicle.AccessingTrunk.contains(player.GUID)) {
|
||||||
vehicle.AccessingTrunk = None
|
vehicle.AccessingTrunk = None
|
||||||
}
|
}
|
||||||
UnaccessVehicleContainerChannel(vehicle)
|
UnaccessContainerChannel(continent.VehicleEvents, vehicle.Actor.toString)
|
||||||
HideContainerContents(vehicle)
|
HideContainerContents(vehicle.Inventory.Items)
|
||||||
}
|
}
|
||||||
|
|
||||||
def UnaccessVehicleContainerChannel(container: PlanetSideServerObject): Unit = {
|
/**
|
||||||
continent.VehicleEvents ! Service.Leave(Some(s"${container.Actor}"))
|
* Common preparation for disengaging from a corpse.
|
||||||
|
* Leave the corpse-specific group that was used for shared updates.
|
||||||
|
* Deconstruct every object in the backpack's inventory.
|
||||||
|
* @param vehicle the vehicle
|
||||||
|
*/
|
||||||
|
def UnaccessCorpseContainer(tplayer: Player): Unit = {
|
||||||
|
accessedContainer = None
|
||||||
|
UnaccessContainerChannel(continent.AvatarEvents, tplayer.Actor.toString)
|
||||||
|
HideContainerContents(tplayer.HolsterItems())
|
||||||
|
HideContainerContents(tplayer.Inventory.Items)
|
||||||
}
|
}
|
||||||
|
|
||||||
def HideContainerContents(container: Container): Unit = {
|
/**
|
||||||
container.Inventory.Items.foreach(entry => {
|
* Leave an entity-specific group for shared updates.
|
||||||
|
* @param events the event system bus to which to subscribe
|
||||||
|
* @param channel the channel name
|
||||||
|
*/
|
||||||
|
def UnaccessContainerChannel(events: ActorRef, channel: String): Unit = {
|
||||||
|
events ! Service.Leave(Some(channel))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forget the contents of a container by deleting that content from the local client.
|
||||||
|
* @see `InventoryItem`
|
||||||
|
* @see `ObjectDeleteMessage`
|
||||||
|
* @param items a list of the entities to be depicted
|
||||||
|
*/
|
||||||
|
def HideContainerContents(items: List[InventoryItem]): Unit = {
|
||||||
|
items.foreach { entry =>
|
||||||
sendResponse(ObjectDeleteMessage(entry.obj.GUID, 0))
|
sendResponse(ObjectDeleteMessage(entry.obj.GUID, 0))
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -7189,6 +7256,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
*/
|
*/
|
||||||
def DepictPlayerAsCorpse(tplayer: Player): Unit = {
|
def DepictPlayerAsCorpse(tplayer: Player): Unit = {
|
||||||
val guid = tplayer.GUID
|
val guid = tplayer.GUID
|
||||||
|
//the corpse as a receptacle
|
||||||
sendResponse(
|
sendResponse(
|
||||||
ObjectCreateDetailedMessage(
|
ObjectCreateDetailedMessage(
|
||||||
ObjectClass.avatar,
|
ObjectClass.avatar,
|
||||||
|
|
@ -8241,23 +8309,20 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
||||||
* @return all discovered `BoomTrigger` objects
|
* @return all discovered `BoomTrigger` objects
|
||||||
*/
|
*/
|
||||||
def RemoveBoomerTriggersFromInventory(): List[BoomerTrigger] = {
|
def RemoveBoomerTriggersFromInventory(): List[BoomerTrigger] = {
|
||||||
val holstersWithIndex = player.Holsters().zipWithIndex
|
val events = continent.AvatarEvents
|
||||||
((player.Inventory.Items.collect({ case InventoryItem(obj: BoomerTrigger, index) => (obj, index) })) ++
|
val zoneId = continent.id
|
||||||
(holstersWithIndex
|
(player.Inventory.Items ++ player.HolsterItems())
|
||||||
.map({ case ((slot, index)) => (slot.Equipment, index) })
|
.collect { case InventoryItem(obj: BoomerTrigger, index) =>
|
||||||
.collect { case ((Some(obj: BoomerTrigger), index)) => (obj, index) }))
|
player.Slot(index).Equipment = None
|
||||||
.map({
|
sendResponse(ObjectDeleteMessage(obj.GUID, 0))
|
||||||
case ((obj, index)) =>
|
if (player.HasGUID && player.VisibleSlots.contains(index)) {
|
||||||
player.Slot(index).Equipment = None
|
events ! AvatarServiceMessage(
|
||||||
sendResponse(ObjectDeleteMessage(obj.GUID, 0))
|
zoneId,
|
||||||
if (player.VisibleSlots.contains(index) && player.HasGUID) {
|
AvatarAction.ObjectDelete(player.GUID, obj.GUID)
|
||||||
continent.AvatarEvents ! AvatarServiceMessage(
|
)
|
||||||
continent.id,
|
}
|
||||||
AvatarAction.ObjectDelete(player.GUID, obj.GUID)
|
obj
|
||||||
)
|
}
|
||||||
}
|
|
||||||
obj
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -190,6 +190,19 @@ class Player(var avatar: Avatar)
|
||||||
|
|
||||||
def Holsters(): Array[EquipmentSlot] = holsters
|
def Holsters(): Array[EquipmentSlot] = holsters
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform the holster equipment slots
|
||||||
|
* into a list of the kind of item wrapper found in an inventory.
|
||||||
|
* @see `GridInventory`
|
||||||
|
* @see `InventoryItem`
|
||||||
|
* @return a list of items that would be found in a proper inventory
|
||||||
|
*/
|
||||||
|
def HolsterItems(): List[InventoryItem] = holsters
|
||||||
|
.zipWithIndex
|
||||||
|
.collect {
|
||||||
|
case out @ (slot: EquipmentSlot, index: Int) if slot.Equipment.nonEmpty => InventoryItem(slot.Equipment.get, index)
|
||||||
|
}.toList
|
||||||
|
|
||||||
def Inventory: GridInventory = inventory
|
def Inventory: GridInventory = inventory
|
||||||
|
|
||||||
override def Fit(obj: Equipment): Option[Int] = {
|
override def Fit(obj: Equipment): Option[Int] = {
|
||||||
|
|
|
||||||
|
|
@ -260,12 +260,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
||||||
afterHolsters.foreach({ elem => player.Slot(elem.start).Equipment = elem.obj })
|
afterHolsters.foreach({ elem => player.Slot(elem.start).Equipment = elem.obj })
|
||||||
val remainder = Players.fillEmptyHolsters(player.Holsters().iterator, toInventory ++ beforeInventory)
|
val remainder = Players.fillEmptyHolsters(player.Holsters().iterator, toInventory ++ beforeInventory)
|
||||||
(
|
(
|
||||||
player
|
player.HolsterItems(),
|
||||||
.Holsters()
|
|
||||||
.zipWithIndex
|
|
||||||
.map { case (slot, i) => (slot.Equipment, i) }
|
|
||||||
.collect { case (Some(obj), index) => InventoryItem(obj, index) }
|
|
||||||
.toList,
|
|
||||||
remainder
|
remainder
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -408,11 +403,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
||||||
player.Holsters().iterator,
|
player.Holsters().iterator,
|
||||||
(curatedHolsters ++ curatedInventory).filterNot(dropPred)
|
(curatedHolsters ++ curatedInventory).filterNot(dropPred)
|
||||||
)
|
)
|
||||||
val finalHolsters = player
|
val finalHolsters = player.HolsterItems()
|
||||||
.Holsters()
|
|
||||||
.zipWithIndex
|
|
||||||
.collect { case (slot, index) if slot.Equipment.nonEmpty => InventoryItem(slot.Equipment.get, index) }
|
|
||||||
.toList
|
|
||||||
//inventory
|
//inventory
|
||||||
val (finalInventory, _) = GridInventory.recoverInventory(leftoversForInventory, player.Inventory)
|
val (finalInventory, _) = GridInventory.recoverInventory(leftoversForInventory, player.Inventory)
|
||||||
(finalHolsters, finalInventory)
|
(finalHolsters, finalInventory)
|
||||||
|
|
@ -822,14 +813,11 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
||||||
def MessageDeferredCallback(msg: Any): Unit = {
|
def MessageDeferredCallback(msg: Any): Unit = {
|
||||||
msg match {
|
msg match {
|
||||||
case Containable.MoveItem(_, item, _) =>
|
case Containable.MoveItem(_, item, _) =>
|
||||||
//momentarily put item back where it was originally
|
//momentarily depict item back where it was originally
|
||||||
val obj = ContainerObject
|
val obj = ContainerObject
|
||||||
obj.Find(item) match {
|
obj.Find(item) match {
|
||||||
case Some(slot) =>
|
case Some(slot) =>
|
||||||
obj.Zone.AvatarEvents ! AvatarServiceMessage(
|
PutItemInSlotCallback(item, slot)
|
||||||
player.Name,
|
|
||||||
AvatarAction.SendResponse(Service.defaultPlayerGUID, ObjectAttachMessage(obj.GUID, item.GUID, slot))
|
|
||||||
)
|
|
||||||
case None => ;
|
case None => ;
|
||||||
}
|
}
|
||||||
case _ => ;
|
case _ => ;
|
||||||
|
|
@ -840,7 +828,13 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
||||||
val obj = ContainerObject
|
val obj = ContainerObject
|
||||||
val zone = obj.Zone
|
val zone = obj.Zone
|
||||||
val events = zone.AvatarEvents
|
val events = zone.AvatarEvents
|
||||||
val toChannel = if (obj.VisibleSlots.contains(slot)) zone.id else player.Name
|
val toChannel = if (player.isBackpack) {
|
||||||
|
self.toString
|
||||||
|
} else if (obj.VisibleSlots.contains(slot)) {
|
||||||
|
zone.id
|
||||||
|
} else {
|
||||||
|
player.Name
|
||||||
|
}
|
||||||
item.Faction = PlanetSideEmpire.NEUTRAL
|
item.Faction = PlanetSideEmpire.NEUTRAL
|
||||||
if (slot == obj.DrawnSlot) {
|
if (slot == obj.DrawnSlot) {
|
||||||
obj.DrawnSlot = Player.HandsDownSlot
|
obj.DrawnSlot = Player.HandsDownSlot
|
||||||
|
|
@ -856,9 +850,10 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
||||||
val name = player.Name
|
val name = player.Name
|
||||||
val definition = item.Definition
|
val definition = item.Definition
|
||||||
val faction = obj.Faction
|
val faction = obj.Faction
|
||||||
|
val toChannel = if (player.isBackpack) { self.toString } else { name }
|
||||||
item.Faction = faction
|
item.Faction = faction
|
||||||
events ! AvatarServiceMessage(
|
events ! AvatarServiceMessage(
|
||||||
name,
|
toChannel,
|
||||||
AvatarAction.SendResponse(
|
AvatarAction.SendResponse(
|
||||||
Service.defaultPlayerGUID,
|
Service.defaultPlayerGUID,
|
||||||
ObjectCreateDetailedMessage(
|
ObjectCreateDetailedMessage(
|
||||||
|
|
@ -869,7 +864,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if (obj.VisibleSlots.contains(slot)) {
|
if (!player.isBackpack && obj.VisibleSlots.contains(slot)) {
|
||||||
events ! AvatarServiceMessage(zone.id, AvatarAction.EquipmentInHand(guid, guid, slot, item))
|
events ! AvatarServiceMessage(zone.id, AvatarAction.EquipmentInHand(guid, guid, slot, item))
|
||||||
}
|
}
|
||||||
//handle specific types of items
|
//handle specific types of items
|
||||||
|
|
@ -924,7 +919,13 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
|
||||||
def SwapItemCallback(item: Equipment, fromSlot: Int): Unit = {
|
def SwapItemCallback(item: Equipment, fromSlot: Int): Unit = {
|
||||||
val obj = ContainerObject
|
val obj = ContainerObject
|
||||||
val zone = obj.Zone
|
val zone = obj.Zone
|
||||||
val toChannel = if (obj.VisibleSlots.contains(fromSlot)) zone.id else player.Name
|
val toChannel = if (player.isBackpack) {
|
||||||
|
self.toString
|
||||||
|
} else if (obj.VisibleSlots.contains(fromSlot)) {
|
||||||
|
zone.id
|
||||||
|
} else {
|
||||||
|
player.Name
|
||||||
|
}
|
||||||
zone.AvatarEvents ! AvatarServiceMessage(
|
zone.AvatarEvents ! AvatarServiceMessage(
|
||||||
toChannel,
|
toChannel,
|
||||||
AvatarAction.ObjectDelete(Service.defaultPlayerGUID, item.GUID)
|
AvatarAction.ObjectDelete(Service.defaultPlayerGUID, item.GUID)
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ object AvatarConverter {
|
||||||
obj.Faction,
|
obj.Faction,
|
||||||
bops = false,
|
bops = false,
|
||||||
alt_model_flag,
|
alt_model_flag,
|
||||||
false,
|
v1 = false,
|
||||||
None,
|
None,
|
||||||
obj.Jammed,
|
obj.Jammed,
|
||||||
None,
|
None,
|
||||||
|
|
@ -89,11 +89,11 @@ object AvatarConverter {
|
||||||
0L,
|
0L,
|
||||||
outfit_name = "",
|
outfit_name = "",
|
||||||
outfit_logo = 0,
|
outfit_logo = 0,
|
||||||
false,
|
unk1 = false,
|
||||||
obj.isBackpack,
|
obj.isBackpack,
|
||||||
false,
|
unk2 = false,
|
||||||
false,
|
unk3 = false,
|
||||||
false,
|
unk4 = false,
|
||||||
facingPitch = obj.Orientation.y,
|
facingPitch = obj.Orientation.y,
|
||||||
facingYawUpper = obj.FacingYawUpper,
|
facingYawUpper = obj.FacingYawUpper,
|
||||||
obj.avatar.lookingForSquad,
|
obj.avatar.lookingForSquad,
|
||||||
|
|
@ -163,21 +163,21 @@ object AvatarConverter {
|
||||||
Some(DCDExtra2(0, 0)),
|
Some(DCDExtra2(0, 0)),
|
||||||
Nil,
|
Nil,
|
||||||
Nil,
|
Nil,
|
||||||
false,
|
unkC = false,
|
||||||
obj.avatar.cosmetics
|
obj.avatar.cosmetics
|
||||||
)
|
)
|
||||||
pad_length: Option[Int] => DetailedCharacterData(ba, bb(obj.avatar.bep, pad_length))(pad_length)
|
pad_length: Option[Int] => DetailedCharacterData(ba, bb(obj.avatar.bep, pad_length))(pad_length)
|
||||||
}
|
}
|
||||||
|
|
||||||
def MakeInventoryData(obj: Player): InventoryData = {
|
def MakeInventoryData(obj: Player): InventoryData = {
|
||||||
InventoryData(MakeHolsters(obj, BuildEquipment).sortBy(_.parentSlot))
|
InventoryData(MakeHolsters(obj, BuildEquipment))
|
||||||
}
|
}
|
||||||
|
|
||||||
def MakeDetailedInventoryData(obj: Player): InventoryData = {
|
def MakeDetailedInventoryData(obj: Player): InventoryData = {
|
||||||
InventoryData(
|
InventoryData(
|
||||||
(MakeHolsters(obj, BuildDetailedEquipment) ++
|
MakeHolsters(obj, BuildDetailedEquipment) ++
|
||||||
MakeFifthSlot(obj) ++
|
MakeFifthSlot(obj) ++
|
||||||
MakeInventory(obj)).sortBy(_.parentSlot)
|
MakeInventory(obj)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -210,7 +210,7 @@ object AvatarConverter {
|
||||||
* @param builder the function used to transform to the decoded packet form
|
* @param builder the function used to transform to the decoded packet form
|
||||||
* @return a list of all items that were in the holsters in decoded packet form
|
* @return a list of all items that were in the holsters in decoded packet form
|
||||||
*/
|
*/
|
||||||
private def MakeHolsters(obj: Player, builder: (Int, Equipment) => InternalSlot): List[InternalSlot] = {
|
def MakeHolsters(obj: Player, builder: (Int, Equipment) => InternalSlot): List[InternalSlot] = {
|
||||||
recursiveMakeHolsters(obj.Holsters().iterator, builder)
|
recursiveMakeHolsters(obj.Holsters().iterator, builder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -222,9 +222,8 @@ object AvatarConverter {
|
||||||
* @return a list of any item that was in the fifth holster in decoded packet form
|
* @return a list of any item that was in the fifth holster in decoded packet form
|
||||||
*/
|
*/
|
||||||
private def MakeFifthSlot(obj: Player): List[InternalSlot] = {
|
private def MakeFifthSlot(obj: Player): List[InternalSlot] = {
|
||||||
obj.Slot(5).Equipment match {
|
obj.Slot(slot = 5).Equipment match {
|
||||||
case Some(equip) =>
|
case Some(equip) =>
|
||||||
//List(BuildDetailedEquipment(5, equip))
|
|
||||||
List(InternalSlot(
|
List(InternalSlot(
|
||||||
equip.Definition.ObjectId,
|
equip.Definition.ObjectId,
|
||||||
equip.GUID,
|
equip.GUID,
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,9 @@ package net.psforever.objects.definition.converter
|
||||||
|
|
||||||
import net.psforever.objects.Player
|
import net.psforever.objects.Player
|
||||||
import net.psforever.objects.avatar.Certification
|
import net.psforever.objects.avatar.Certification
|
||||||
import net.psforever.objects.equipment.{Equipment, EquipmentSlot}
|
|
||||||
import net.psforever.packet.game.objectcreate._
|
import net.psforever.packet.game.objectcreate._
|
||||||
import net.psforever.types.{PlanetSideGUID, _}
|
import net.psforever.types.{PlanetSideGUID, _}
|
||||||
|
|
||||||
import scala.annotation.tailrec
|
|
||||||
import scala.util.{Failure, Success, Try}
|
import scala.util.{Failure, Success, Try}
|
||||||
|
|
||||||
class CorpseConverter extends AvatarConverter {
|
class CorpseConverter extends AvatarConverter {
|
||||||
|
|
@ -20,7 +18,6 @@ class CorpseConverter extends AvatarConverter {
|
||||||
PlacementData(obj.Position, Vector3(0, 0, obj.Orientation.z)),
|
PlacementData(obj.Position, Vector3(0, 0, obj.Orientation.z)),
|
||||||
MakeAppearanceData(obj),
|
MakeAppearanceData(obj),
|
||||||
MakeDetailedCharacterData(obj),
|
MakeDetailedCharacterData(obj),
|
||||||
InventoryData((MakeHolsters(obj) ++ MakeInventory(obj)).sortBy(_.parentSlot)),
|
|
||||||
DrawnSlot.None
|
DrawnSlot.None
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
@ -117,78 +114,7 @@ class CorpseConverter extends AvatarConverter {
|
||||||
unkC = false,
|
unkC = false,
|
||||||
cosmetics = None
|
cosmetics = None
|
||||||
)
|
)
|
||||||
(pad_length: Option[Int]) => DetailedCharacterData(ba, bb(0, pad_length))(pad_length)
|
pad_length: Option[Int] => DetailedCharacterData(ba, bb(0, pad_length))(pad_length)
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given a player with an inventory, convert the contents of that inventory into converted-decoded packet data.
|
|
||||||
* The inventory is not represented in a `0x17` `Player`, so the conversion is only valid for `0x18` avatars.
|
|
||||||
* It will always be "`Detailed`".
|
|
||||||
* @param obj the `Player` game object
|
|
||||||
* @return a list of all items that were in the inventory in decoded packet form
|
|
||||||
*/
|
|
||||||
private def MakeInventory(obj: Player): List[InternalSlot] = {
|
|
||||||
obj.Inventory.Items
|
|
||||||
.map(item => {
|
|
||||||
val equip: Equipment = item.obj
|
|
||||||
BuildEquipment(item.start, equip)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given a player with equipment holsters, convert the contents of those holsters into converted-decoded packet data.
|
|
||||||
* The decoded packet form is determined by the function in the parameters as both `0x17` and `0x18` conversions are available,
|
|
||||||
* with exception to the contents of the fifth slot.
|
|
||||||
* The fifth slot is only represented if the `Player` is an `0x18` type.
|
|
||||||
* @param obj the `Player` game object
|
|
||||||
* @return a list of all items that were in the holsters in decoded packet form
|
|
||||||
*/
|
|
||||||
private def MakeHolsters(obj: Player): List[InternalSlot] = {
|
|
||||||
recursiveMakeHolsters(obj.Holsters().iterator)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given some equipment holsters, convert the contents of those holsters into converted-decoded packet data.
|
|
||||||
* @param iter an `Iterator` of `EquipmentSlot` objects that are a part of the player's holsters
|
|
||||||
* @param list the current `List` of transformed data
|
|
||||||
* @param index which holster is currently being explored
|
|
||||||
* @return the `List` of inventory data created from the holsters
|
|
||||||
*/
|
|
||||||
@tailrec private def recursiveMakeHolsters(
|
|
||||||
iter: Iterator[EquipmentSlot],
|
|
||||||
list: List[InternalSlot] = Nil,
|
|
||||||
index: Int = 0
|
|
||||||
): List[InternalSlot] = {
|
|
||||||
if (!iter.hasNext) {
|
|
||||||
list
|
|
||||||
} else {
|
|
||||||
val slot: EquipmentSlot = iter.next()
|
|
||||||
if (slot.Equipment.isDefined) {
|
|
||||||
val equip: Equipment = slot.Equipment.get
|
|
||||||
recursiveMakeHolsters(
|
|
||||||
iter,
|
|
||||||
list :+ BuildEquipment(index, equip),
|
|
||||||
index + 1
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
recursiveMakeHolsters(iter, list, index + 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A builder method for turning an object into `0x17` decoded packet form.
|
|
||||||
* @param index the position of the object
|
|
||||||
* @param equip the game object
|
|
||||||
* @return the game object in decoded packet form
|
|
||||||
*/
|
|
||||||
private def BuildEquipment(index: Int, equip: Equipment): InternalSlot = {
|
|
||||||
InternalSlot(
|
|
||||||
equip.Definition.ObjectId,
|
|
||||||
equip.GUID,
|
|
||||||
index,
|
|
||||||
equip.Definition.Packet.DetailedConstructorData(equip).get
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue