mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-01-20 02:24:45 +00:00
Merge pull request #857 from Fate-JH/plockers
lockers interact with the database by loading and storing contents
This commit is contained in:
commit
79f452259f
|
|
@ -9,9 +9,10 @@ import net.psforever.objects.avatar._
|
|||
import net.psforever.objects.definition.converter.CharacterSelectConverter
|
||||
import net.psforever.objects.definition._
|
||||
import net.psforever.objects.equipment.Equipment
|
||||
import net.psforever.objects.inventory.InventoryItem
|
||||
import net.psforever.objects.inventory.{Container, InventoryItem}
|
||||
import net.psforever.objects.loadouts.{InfantryLoadout, Loadout}
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.locker.LockerContainer
|
||||
import net.psforever.packet.game.objectcreate.ObjectClass
|
||||
import net.psforever.packet.game._
|
||||
import net.psforever.types._
|
||||
|
|
@ -98,6 +99,9 @@ object AvatarActor {
|
|||
/** Refresh the client's loadouts */
|
||||
final case class RefreshLoadouts() extends Command
|
||||
|
||||
/** Take all the entries in the player's locker and write it to the database */
|
||||
final case class SaveLocker() extends Command
|
||||
|
||||
/** Set purchase time for the use of calculating cooldowns */
|
||||
final case class UpdatePurchaseTime(definition: BasicDefinition, time: LocalDateTime = LocalDateTime.now())
|
||||
extends Command
|
||||
|
|
@ -190,6 +194,7 @@ class AvatarActor(
|
|||
val implantTimers: mutable.Map[Int, Cancellable] = mutable.Map()
|
||||
var staminaRegenTimer: Cancellable = Cancellable.alreadyCancelled
|
||||
var _avatar: Option[Avatar] = None
|
||||
var saveLockerFunc: () => Unit = storeNewLocker
|
||||
//val topic: ActorRef[Topic.Command[Avatar]] = context.spawnAnonymous(Topic[Avatar]("avatar"))
|
||||
|
||||
def avatar: Avatar = _avatar.get
|
||||
|
|
@ -347,16 +352,18 @@ class AvatarActor(
|
|||
loadouts <- loadLoadouts()
|
||||
implants <- ctx.run(query[persistence.Implant].filter(_.avatarId == lift(avatar.id)))
|
||||
certs <- ctx.run(query[persistence.Certification].filter(_.avatarId == lift(avatar.id)))
|
||||
} yield (loadouts, implants, certs)
|
||||
locker <- loadLocker()
|
||||
} yield (loadouts, implants, certs, locker)
|
||||
|
||||
result.onComplete {
|
||||
case Success((loadouts, implants, certs)) =>
|
||||
case Success((loadouts, implants, certs, locker)) =>
|
||||
avatar = avatar.copy(
|
||||
loadouts = loadouts,
|
||||
// make sure we always have the base certifications
|
||||
certifications =
|
||||
certs.map(cert => Certification.withValue(cert.id)).toSet ++ Config.app.game.baseCertifications,
|
||||
implants = implants.map(implant => Some(Implant(implant.toImplantDefinition))).padTo(3, None)
|
||||
implants = implants.map(implant => Some(Implant(implant.toImplantDefinition))).padTo(3, None),
|
||||
locker = locker
|
||||
)
|
||||
|
||||
staminaRegenTimer.cancel()
|
||||
|
|
@ -372,6 +379,7 @@ class AvatarActor(
|
|||
updateDeployableUIElements(
|
||||
avatar.deployables.UpdateUI()
|
||||
)
|
||||
|
||||
Behaviors.same
|
||||
|
||||
case AddFirstTimeEvent(event) =>
|
||||
|
|
@ -695,6 +703,10 @@ class AvatarActor(
|
|||
}
|
||||
Behaviors.same
|
||||
|
||||
case SaveLocker() =>
|
||||
saveLockerFunc()
|
||||
Behaviors.same
|
||||
|
||||
case UpdatePurchaseTime(definition, time) =>
|
||||
// TODO save to db
|
||||
var newTimes = avatar.purchaseTimes
|
||||
|
|
@ -938,6 +950,7 @@ class AvatarActor(
|
|||
case (_, PostStop) =>
|
||||
staminaRegenTimer.cancel()
|
||||
implantTimers.values.foreach(_.cancel())
|
||||
saveLockerFunc()
|
||||
Behaviors.same
|
||||
}
|
||||
}
|
||||
|
|
@ -1216,6 +1229,57 @@ class AvatarActor(
|
|||
} yield ()
|
||||
}
|
||||
|
||||
def storeNewLocker(): Unit = {
|
||||
if (_avatar.nonEmpty) {
|
||||
val items : String = {
|
||||
val clobber : StringBuilder = new StringBuilder()
|
||||
avatar.locker.Inventory.Items.foreach {
|
||||
case InventoryItem(obj, index) =>
|
||||
clobber.append(encodeLoadoutClobFragment(obj, index))
|
||||
}
|
||||
clobber.mkString.drop(1)
|
||||
}
|
||||
if (items.nonEmpty) {
|
||||
saveLockerFunc = storeLocker
|
||||
import ctx._
|
||||
ctx.run(
|
||||
query[persistence.Locker].insert(
|
||||
_.avatarId -> lift(avatar.id),
|
||||
_.items -> lift(items)
|
||||
)
|
||||
).onComplete {
|
||||
case Success(_) =>
|
||||
log.debug(s"saving locker contents belonging to ${avatar.name}")
|
||||
case Failure(e) =>
|
||||
saveLockerFunc = doNotStoreLocker
|
||||
log.error(e)("db failure")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def doNotStoreLocker(): Unit = {
|
||||
/* most likely the database encountered an error; don't do anything with it until the restart */
|
||||
}
|
||||
|
||||
def storeLocker(): Unit = {
|
||||
import ctx._
|
||||
val items : String = {
|
||||
val clobber : StringBuilder = new StringBuilder()
|
||||
avatar.locker.Inventory.Items.foreach {
|
||||
case InventoryItem(obj, index) =>
|
||||
clobber.append(encodeLoadoutClobFragment(obj, index))
|
||||
}
|
||||
clobber.mkString.drop(1)
|
||||
}
|
||||
log.debug(s"saving locker contents belonging to ${avatar.name}")
|
||||
ctx.run(
|
||||
query[persistence.Locker]
|
||||
.filter(_.avatarId == lift(avatar.id))
|
||||
.update(_.items -> lift(items))
|
||||
)
|
||||
}
|
||||
|
||||
def encodeLoadoutClobFragment(equipment: Equipment, index: Int): String = {
|
||||
val ammoInfo: String = equipment match {
|
||||
case tool: Tool =>
|
||||
|
|
@ -1237,63 +1301,76 @@ class AvatarActor(
|
|||
loadouts.map { loadout =>
|
||||
val doll = new Player(Avatar(0, "doll", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute))
|
||||
doll.ExoSuit = ExoSuitType(loadout.exosuitId)
|
||||
|
||||
loadout.items.split("/").foreach {
|
||||
value =>
|
||||
val (objectType, objectIndex, objectId, toolAmmo) = value.split(",") match {
|
||||
case Array(a, b: String, c: String) => (a, b.toInt, c.toInt, None)
|
||||
case Array(a, b: String, c: String, d) => (a, b.toInt, c.toInt, Some(d))
|
||||
}
|
||||
|
||||
objectType match {
|
||||
case "Tool" =>
|
||||
doll.Slot(objectIndex).Equipment =
|
||||
Tool(DefinitionUtil.idToDefinition(objectId).asInstanceOf[ToolDefinition])
|
||||
case "AmmoBox" =>
|
||||
doll.Slot(objectIndex).Equipment =
|
||||
AmmoBox(DefinitionUtil.idToDefinition(objectId).asInstanceOf[AmmoBoxDefinition])
|
||||
case "ConstructionItem" =>
|
||||
doll.Slot(objectIndex).Equipment = ConstructionItem(
|
||||
DefinitionUtil.idToDefinition(objectId).asInstanceOf[ConstructionItemDefinition]
|
||||
)
|
||||
case "SimpleItem" =>
|
||||
doll.Slot(objectIndex).Equipment =
|
||||
SimpleItem(DefinitionUtil.idToDefinition(objectId).asInstanceOf[SimpleItemDefinition])
|
||||
case "Kit" =>
|
||||
doll.Slot(objectIndex).Equipment =
|
||||
Kit(DefinitionUtil.idToDefinition(objectId).asInstanceOf[KitDefinition])
|
||||
case "Telepad" | "BoomerTrigger" => ;
|
||||
//special types of equipment that are not actually loaded
|
||||
case name =>
|
||||
log.error(s"failing to add unknown equipment to a loadout - $name")
|
||||
}
|
||||
|
||||
toolAmmo foreach { toolAmmo =>
|
||||
toolAmmo.toString.split("_").drop(1).foreach { value =>
|
||||
val (ammoSlots, ammoTypeIndex, ammoBoxDefinition) = value.split("-") match {
|
||||
case Array(a: String, b: String, c: String) => (a.toInt, b.toInt, c.toInt)
|
||||
}
|
||||
doll.Slot(objectIndex).Equipment.get.asInstanceOf[Tool].AmmoSlots(ammoSlots).AmmoTypeIndex =
|
||||
ammoTypeIndex
|
||||
doll.Slot(objectIndex).Equipment.get.asInstanceOf[Tool].AmmoSlots(ammoSlots).Box =
|
||||
AmmoBox(AmmoBoxDefinition(ammoBoxDefinition))
|
||||
}
|
||||
}
|
||||
}
|
||||
buildContainedEquipmentFromClob(doll, loadout.items)
|
||||
|
||||
val result = (loadout.loadoutNumber, Loadout.Create(doll, loadout.name))
|
||||
|
||||
(0 until 4).foreach(index => {
|
||||
doll.Slot(index).Equipment = None
|
||||
})
|
||||
doll.Inventory.Clear()
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
.map { loadouts => (0 until 15).map { index => loadouts.find(_._1 == index).map(_._2) } }
|
||||
}
|
||||
|
||||
def loadLocker(): Future[LockerContainer] = {
|
||||
val locker = Avatar.makeLocker()
|
||||
import ctx._
|
||||
ctx
|
||||
.run(query[persistence.Locker].filter(_.avatarId == lift(avatar.id)))
|
||||
.map { entry =>
|
||||
saveLockerFunc = storeLocker
|
||||
entry.foreach { contents => buildContainedEquipmentFromClob(locker, contents.items) }
|
||||
}
|
||||
.map { _ => locker }
|
||||
}
|
||||
|
||||
def buildContainedEquipmentFromClob(container: Container, clob: String): Unit = {
|
||||
clob.split("/").foreach {
|
||||
value =>
|
||||
val (objectType, objectIndex, objectId, toolAmmo) = value.split(",") match {
|
||||
case Array(a, b: String, c: String) => (a, b.toInt, c.toInt, None)
|
||||
case Array(a, b: String, c: String, d) => (a, b.toInt, c.toInt, Some(d))
|
||||
}
|
||||
|
||||
objectType match {
|
||||
case "Tool" =>
|
||||
container.Slot(objectIndex).Equipment =
|
||||
Tool(DefinitionUtil.idToDefinition(objectId).asInstanceOf[ToolDefinition])
|
||||
case "AmmoBox" =>
|
||||
container.Slot(objectIndex).Equipment =
|
||||
AmmoBox(DefinitionUtil.idToDefinition(objectId).asInstanceOf[AmmoBoxDefinition])
|
||||
case "ConstructionItem" =>
|
||||
container.Slot(objectIndex).Equipment = ConstructionItem(
|
||||
DefinitionUtil.idToDefinition(objectId).asInstanceOf[ConstructionItemDefinition]
|
||||
)
|
||||
case "SimpleItem" =>
|
||||
container.Slot(objectIndex).Equipment =
|
||||
SimpleItem(DefinitionUtil.idToDefinition(objectId).asInstanceOf[SimpleItemDefinition])
|
||||
case "Kit" =>
|
||||
container.Slot(objectIndex).Equipment =
|
||||
Kit(DefinitionUtil.idToDefinition(objectId).asInstanceOf[KitDefinition])
|
||||
case "Telepad" | "BoomerTrigger" => ;
|
||||
//special types of equipment that are not actually loaded
|
||||
case name =>
|
||||
log.error(s"failing to add unknown equipment to a locker - $name")
|
||||
}
|
||||
|
||||
toolAmmo foreach { toolAmmo =>
|
||||
toolAmmo.toString.split("_").drop(1).foreach { value =>
|
||||
val (ammoSlots, ammoTypeIndex, ammoBoxDefinition) = value.split("-") match {
|
||||
case Array(a: String, b: String, c: String) => (a.toInt, b.toInt, c.toInt)
|
||||
}
|
||||
container.Slot(objectIndex).Equipment.get.asInstanceOf[Tool].AmmoSlots(ammoSlots).AmmoTypeIndex =
|
||||
ammoTypeIndex
|
||||
container.Slot(objectIndex).Equipment.get.asInstanceOf[Tool].AmmoSlots(ammoSlots).Box =
|
||||
AmmoBox(AmmoBoxDefinition(ammoBoxDefinition))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def defaultStaminaRegen(): Cancellable = {
|
||||
context.system.scheduler.scheduleWithFixedDelay(0.5 seconds, 0.5 seconds)(() => {
|
||||
(session, _avatar) match {
|
||||
|
|
|
|||
|
|
@ -317,10 +317,6 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
|||
continent.LocalEvents ! Service.Leave()
|
||||
continent.VehicleEvents ! Service.Leave()
|
||||
|
||||
// when going from classic -> typed this seems necessary
|
||||
context.stop(avatarActor)
|
||||
context.stop(chatActor)
|
||||
|
||||
if (avatar != null) {
|
||||
//TODO put any temporary values back into the avatar
|
||||
squadService ! Service.Leave(Some(s"${avatar.faction}"))
|
||||
|
|
@ -335,6 +331,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
|||
}
|
||||
}
|
||||
}
|
||||
// when going from classic -> typed this seems necessary
|
||||
akka.actor.TypedActor(context.system).poisonPill(avatarActor)
|
||||
akka.actor.TypedActor(context.system).poisonPill(chatActor)
|
||||
}
|
||||
|
||||
def ValidObject(id: Int): Option[PlanetSideGameObject] = ValidObject(Some(PlanetSideGUID(id)))
|
||||
|
|
@ -6116,6 +6115,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
|||
UnaccessVehicleContainer(v)
|
||||
case o: LockerContainer =>
|
||||
UnaccessGenericContainer(o)
|
||||
avatarActor ! AvatarActor.SaveLocker()
|
||||
case p: Player if p.isBackpack =>
|
||||
UnaccessCorpseContainer(p)
|
||||
case _: PlanetSideServerObject with Container =>
|
||||
|
|
|
|||
|
|
@ -65,6 +65,14 @@ object Avatar {
|
|||
GlobalDefinitions.super_medkit -> 20.minutes,
|
||||
GlobalDefinitions.super_staminakit -> 5.minutes // Temporary - Default value is 20 minutes
|
||||
)
|
||||
|
||||
def makeLocker(): LockerContainer = {
|
||||
new LockerContainer({
|
||||
val inv = new LocallyRegisteredInventory(numbers = 40150 until 40450) // TODO var bad
|
||||
inv.Resize(30,20)
|
||||
inv
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
case class Avatar(
|
||||
|
|
@ -84,11 +92,7 @@ case class Avatar(
|
|||
loadouts: Seq[Option[Loadout]] = Seq.fill(15)(None),
|
||||
squadLoadouts: Seq[Option[SquadLoadout]] = Seq.fill(10)(None),
|
||||
implants: Seq[Option[Implant]] = Seq(None, None, None),
|
||||
locker: LockerContainer = new LockerContainer({
|
||||
val inv = new LocallyRegisteredInventory(numbers = 40150 until 40450) // TODO var bad
|
||||
inv.Resize(30,20)
|
||||
inv
|
||||
}),
|
||||
locker: LockerContainer = Avatar.makeLocker(),
|
||||
deployables: DeployableToolbox = new DeployableToolbox(), // TODO var bad
|
||||
lookingForSquad: Boolean = false,
|
||||
var vehicle: Option[PlanetSideGUID] = None, // TODO var bad
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
// Copyright (c) 2020 PSForever
|
||||
package net.psforever.persistence
|
||||
|
||||
case class Locker(id: Int, avatarId: Int, items: String)
|
||||
|
|
|
|||
Loading…
Reference in a new issue