converted DeployItem from AvatarService to LocalService; attempt at resolving missing overwhip yellow ring is complicated; vehicle ownership packet wqorks on deployables that are mountable, but is less successful on normal simple deployables

This commit is contained in:
Fate-JH 2024-08-06 15:09:34 -04:00
parent 698a5609ab
commit c6860bc718
12 changed files with 109 additions and 92 deletions

View file

@ -130,29 +130,6 @@ class EquipmentInHandTest extends ActorTest {
}
}
class DeployItemTest extends ActorTest {
ServiceManager.boot(system)
val service = system.actorOf(Props(classOf[AvatarService], Zone.Nowhere), "deploy-item-test-service")
val objDef = GlobalDefinitions.motionalarmsensor
val obj = new SensorDeployable(objDef)
obj.Position = Vector3(1, 2, 3)
obj.Orientation = Vector3(4, 5, 6)
obj.GUID = PlanetSideGUID(40)
val pkt = ObjectCreateMessage(
objDef.ObjectId,
obj.GUID,
objDef.Packet.ConstructorData(obj).get
)
"AvatarService" should {
"pass DeployItem" in {
service ! Service.Join("test")
service ! AvatarServiceMessage("test", AvatarAction.DeployItem(PlanetSideGUID(10), obj))
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.DropItem(pkt)))
}
}
}
class DroptItemTest extends ActorTest {
ServiceManager.boot(system)
val service = system.actorOf(Props(classOf[AvatarService], Zone.Nowhere), "release-test-service")

View file

@ -563,7 +563,7 @@ class GeneralOperations(
* @param unk2 na
*/
def hackObject(targetGuid: PlanetSideGUID, unk1: Long, unk2: HackState7): Unit = {
sendResponse(HackMessage(HackState1.Unk0, targetGuid, player_guid=Service.defaultPlayerGUID, progress=100, unk1, HackState.Hacked, unk2))
sendResponse(HackMessage(HackState1.Unk0, targetGuid, player_guid=Service.defaultPlayerGUID, progress=100, unk1.toFloat, HackState.Hacked, unk2))
}
/**

View file

@ -531,6 +531,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
setupDeployable(obj, tool)
case Zone.Deployable.IsBuilt(obj: BoomerDeployable) =>
obj.Actor ! Deployable.Ownership(player)
deployablePair match {
case Some((deployable, tool)) if deployable eq obj =>
val zone = player.Zone
@ -552,11 +553,12 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
TaskWorkflow.execute(PutNewEquipmentInInventoryOrDrop(player)(trigger))
}
Players.buildCooldownReset(zone, player.Name, obj)
case _ => ;
case _ => ()
}
deployablePair = None
case Zone.Deployable.IsBuilt(obj: TelepadDeployable) =>
obj.Actor ! Deployable.Ownership(player)
deployablePair match {
case Some((deployable, tool: Telepad)) if deployable eq obj =>
RemoveOldEquipmentFromInventory(player)(tool)
@ -569,11 +571,12 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
TelepadControl.TelepadError(zone, player.Name, msg = "@Telepad_NoDeploy_RouterLost")
}
Players.buildCooldownReset(zone, player.Name, obj)
case _ => ;
case _ => ()
}
deployablePair = None
case Zone.Deployable.IsBuilt(obj) =>
obj.Actor ! Deployable.Ownership(player)
deployablePair match {
case Some((deployable, tool)) if deployable eq obj =>
Players.buildCooldownReset(player.Zone, player.Name, obj)
@ -584,7 +587,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
case None =>
log.warn(s"${player.Name} should have destroyed a ${tool.Definition.Name} here, but could not find it")
}
case _ => ;
case _ => ()
}
deployablePair = None
@ -701,16 +704,27 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
if (deployables.Valid(obj) &&
!deployables.Contains(obj) &&
Players.deployableWithinBuildLimits(player, obj)) {
//deployables, upon construction, may display an animation effect
tool.Definition match {
case GlobalDefinitions.ace | /* animation handled in deployable lifecycle */
GlobalDefinitions.router_telepad => ; /* no special animation */
case GlobalDefinitions.router_telepad => () /* no special animation */
case GlobalDefinitions.ace
if obj.Definition.deployAnimation == DeployAnimation.Standard =>
zone.LocalEvents ! LocalServiceMessage(
zone.id,
LocalAction.TriggerEffectLocation(
obj.OwnerGuid.getOrElse(Service.defaultPlayerGUID),
"spawn_object_effect",
obj.Position,
obj.Orientation
)
)
case GlobalDefinitions.advanced_ace
if obj.Definition.deployAnimation == DeployAnimation.Fdu =>
zone.AvatarEvents ! AvatarServiceMessage(zone.id, AvatarAction.PutDownFDU(player.GUID))
case _ =>
org.log4s.getLogger(name = "Deployables").warn(
s"not sure what kind of construction item to animate - ${tool.Definition.Name}"
)
org.log4s
.getLogger(name = "Deployables")
.warn(s"not sure what kind of construction item to animate - ${tool.Definition.Name}")
}
deployablePair = Some((obj, tool))
obj.Faction = player.Faction

View file

@ -4,7 +4,6 @@ package net.psforever.objects.ce
import akka.actor.{Actor, ActorRef, Cancellable}
import net.psforever.objects.guid.{GUIDTask, TaskWorkflow}
import net.psforever.objects._
import net.psforever.objects.definition.DeployAnimation
import net.psforever.objects.zones.Zone
import net.psforever.packet.game._
import net.psforever.services.Service
@ -105,6 +104,7 @@ trait DeployableBehavior {
def loseOwnership(obj: Deployable, toFaction: PlanetSideEmpire.Value): Unit = {
DeployableBehavior.changeOwnership(
obj,
toOwner = "",
toFaction,
DeployableInfo(obj.GUID, Deployable.Icon.apply(obj.Definition.Item), obj.Position, Service.defaultPlayerGUID)
)
@ -138,18 +138,18 @@ trait DeployableBehavior {
*/
def gainOwnership(player: Player, toFaction: PlanetSideEmpire.Value): Unit = {
val obj = DeployableObject
obj.AssignOwnership(player)
decay.cancel()
DeployableBehavior.changeOwnership(
obj,
player.Name,
toFaction,
DeployableInfo(obj.GUID, Deployable.Icon.apply(obj.Definition.Item), obj.Position, player.GUID)
)
obj.AssignOwnership(player)
}
/**
* The first stage of the deployable build process, to put the formal process in motion.
* Deployables, upon construction, may display an animation effect.
* Parameters are required to be passed onto the next stage of the build process and are not used here.
* @see `DeployableDefinition.deployAnimation`
* @see `DeployableDefinition.DeployTime`
@ -157,21 +157,9 @@ trait DeployableBehavior {
* @param callback an `ActorRef` used for confirming the deployable's completion of the process
*/
def setupDeployable(callback: ActorRef): Unit = {
import scala.concurrent.ExecutionContext.Implicits.global
val obj = DeployableObject
constructed = Some(false)
if (obj.Definition.deployAnimation == DeployAnimation.Standard) {
val zone = obj.Zone
zone.LocalEvents ! LocalServiceMessage(
zone.id,
LocalAction.TriggerEffectLocation(
obj.OwnerGuid.getOrElse(Service.defaultPlayerGUID),
"spawn_object_effect",
obj.Position,
obj.Orientation
)
)
}
import scala.concurrent.ExecutionContext.Implicits.global
setup = context.system.scheduler.scheduleOnce(
obj.Definition.DeployTime milliseconds,
self,
@ -185,7 +173,7 @@ trait DeployableBehavior {
* Nothing dangerous happens if it does not begin to decay, but, because it is not under a player's management,
* the deployable will not properly transition to a decay state for another reason
* and can linger in the zone ownerless for as long as it is not destroyed.
* @see `AvatarAction.DeployItem`
* @see `LocalAction.DeployItem`
* @see `DeploymentAction`
* @see `DeployableInfo`
* @see `LocalAction.DeployableMapIcon`
@ -197,27 +185,22 @@ trait DeployableBehavior {
setup = Default.Cancellable
constructed = Some(true)
val obj = DeployableObject
val zone = obj.Zone
val zone = obj.Zone
val localEvents = zone.LocalEvents
val owner = obj.OwnerGuid.getOrElse(Service.defaultPlayerGUID)
obj.OwnerName match {
case Some(_) =>
case None =>
import scala.concurrent.ExecutionContext.Implicits.global
decay = context.system.scheduler.scheduleOnce(
Deployable.decay,
self,
Deployable.Deconstruct()
)
obj.OwnerName.orElse {
import scala.concurrent.ExecutionContext.Implicits.global
decay = context.system.scheduler.scheduleOnce(Deployable.decay, self, Deployable.Deconstruct())
None
}
//zone build
zone.AvatarEvents ! AvatarServiceMessage(zone.id, AvatarAction.DeployItem(Service.defaultPlayerGUID, obj))
localEvents ! LocalServiceMessage(zone.id, LocalAction.DeployItem(obj))
//zone map icon
localEvents ! LocalServiceMessage(
obj.Faction.toString,
LocalAction.DeployableMapIcon(
Service.defaultPlayerGUID,
DeploymentAction.Build, DeployableInfo(obj.GUID, Deployable.Icon(obj.Definition.Item), obj.Position, owner)
DeploymentAction.Build,
DeployableInfo(obj.GUID, Deployable.Icon(obj.Definition.Item), obj.Position, obj.OwnerGuid.getOrElse(Service.defaultPlayerGUID))
)
)
//local build management
@ -290,27 +273,44 @@ object DeployableBehavior {
* @param toFaction na
* @param info na
*/
def changeOwnership(obj: Deployable, toFaction: PlanetSideEmpire.Value, info: DeployableInfo): Unit = {
def changeOwnership(obj: Deployable, toOwner: String, toFaction: PlanetSideEmpire.Value, info: DeployableInfo): Unit = {
val dGuid = obj.GUID
val originalFaction = obj.Faction
val zone = obj.Zone
val localEvents = zone.LocalEvents
val ownershipAction = {
val owner = info.player_guid
LocalAction.SendPlanetsideAttributeMessage(
owner,
owner,
PlanetsideAttributeEnum.OwnershipAssignment,
dGuid.guid.toLong
)
}
if (originalFaction != toFaction) {
obj.Faction = toFaction
//visual tells in regards to ownership by faction
zone.AvatarEvents ! AvatarServiceMessage(
zone.id,
AvatarAction.SetEmpire(Service.defaultPlayerGUID, obj.GUID, toFaction)
AvatarAction.SetEmpire(Service.defaultPlayerGUID, dGuid, toFaction)
)
//remove knowledge by the previous owner's faction
localEvents ! LocalServiceMessage(
originalFaction.toString,
LocalAction.DeployableMapIcon(Service.defaultPlayerGUID, DeploymentAction.Dismiss, info)
)
} else {
//old owner no longer owner
obj.OwnerName.collect { case fromOwner if !fromOwner.equals(toOwner) =>
localEvents ! LocalServiceMessage(fromOwner, ownershipAction)
}
}
//display to the given faction
localEvents ! LocalServiceMessage(
toFaction.toString,
LocalAction.DeployableMapIcon(Service.defaultPlayerGUID, DeploymentAction.Build, info)
)
//new owner is owner
localEvents ! LocalServiceMessage(toOwner, ownershipAction)
}
}

View file

@ -1,10 +1,12 @@
// Copyright (c) 2016 PSForever.net to present
package net.psforever.packet.game
import net.psforever.packet.GamePacketOpcode.Type
import net.psforever.packet.game.PlanetsideAttributeEnum.PlanetsideAttributeEnum
import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
import net.psforever.types.PlanetSideGUID
import scodec.Codec
import scodec.bits.BitVector
import scodec.{Attempt, Codec}
import scodec.codecs._
/**
@ -68,8 +70,8 @@ import scodec.codecs._
* `17 - BEP. Value seems to be the same as BattleExperienceMessage`<br>
* `18 - CEP.`<br>
* `19 - Anchors. Value is 0 to disengage, 1 to engage.`<br>
* `20 - Control console hacking, affects CC timer, yellow base warning lights and message "The FactionName has hacked into BaseName".
* Format is: Time left - 2 bytes, faction - 1 byte (1-4), isResecured - 1 byte (0-1)`<br>
* `20 - Control console hacking, affects CC timer, yellow base warning lights and message "The FactionName has hacked into BaseName".`
* Format is: Time left - 2 bytes, faction - 1 byte (1-4), isResecured - 1 byte (0-1)<br>
* <ul>
* <li>65535 segments per faction in deciseconds (seconds * 10)</li>
* <li>0-65535 = Neutral 0 seconds to 1h 49m 14s - 0x0000 to 0xFFFF</li>
@ -134,8 +136,8 @@ import scodec.codecs._
* `31 - Looking for Squad info (marquee and ui):`<br>
* <ul>
* <li>0 - LFS</li>
* <li>1 is LFSM (Looking for Squad Members)`</li>
* <li>`n` is the supplemental squad identifier number; same as "LFS;" for the leader, sets "LFSM" after the first manual flagging`</li>
* <li>1 is LFSM (Looking for Squad Members)</li>
* <li>`n` is the supplemental squad identifier number; same as "LFS;" for the leader, sets "LFSM" after the first manual flagging</li>
* </ul>
* `32 - Maintain the squad role index, when a member of a squad`<br>
* `35 - Battle Rank`<br>
@ -204,8 +206,8 @@ import scodec.codecs._
final case class PlanetsideAttributeMessage(guid: PlanetSideGUID, attribute_type: Int, attribute_value: Long)
extends PlanetSideGamePacket {
type Packet = PlanetsideAttributeMessage
def opcode = GamePacketOpcode.PlanetsideAttributeMessage
def encode = PlanetsideAttributeMessage.encode(this)
def opcode: Type = GamePacketOpcode.PlanetsideAttributeMessage
def encode: Attempt[BitVector] = PlanetsideAttributeMessage.encode(this)
}
object PlanetsideAttributeMessage extends Marshallable[PlanetsideAttributeMessage] {
@ -235,5 +237,6 @@ object PlanetsideAttributeMessage extends Marshallable[PlanetsideAttributeMessag
object PlanetsideAttributeEnum extends Enumeration {
type PlanetsideAttributeEnum = Value
val ControlConsoleHackUpdate = Value(20)
val ControlConsoleHackUpdate: PlanetsideAttributeEnum.Value = Value(20)
val OwnershipAssignment: PlanetsideAttributeEnum.Value = Value(21)
}

View file

@ -87,17 +87,6 @@ class AvatarService(zone: Zone) extends Actor {
AvatarResponse.EnvironmentalDamage(player_guid, source_guid, amount)
)
)
case AvatarAction.DeployItem(player_guid, item) =>
val definition = item.Definition
val objectData = definition.Packet.ConstructorData(item).get
AvatarEvents.publish(
AvatarServiceResponse(
s"/$forChannel/Avatar",
player_guid,
AvatarResponse.DropItem(ObjectCreateMessage(definition.ObjectId, item.GUID, objectData))
)
)
case AvatarAction.Destroy(victim, killer, weapon, pos) =>
AvatarEvents.publish(
AvatarServiceResponse(s"/$forChannel/Avatar", victim, AvatarResponse.Destroy(victim, killer, weapon, pos))

View file

@ -4,7 +4,6 @@ package net.psforever.services.avatar
import net.psforever.objects.Player
import net.psforever.objects.avatar.scoring.KDAStat
import net.psforever.objects.ballistics.Projectile
import net.psforever.objects.ce.Deployable
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.inventory.InventoryItem
import net.psforever.objects.serverobject.environment.interaction.common.Watery.OxygenStateTarget
@ -44,7 +43,6 @@ object AvatarAction {
final case class ConcealPlayer(player_guid: PlanetSideGUID) extends Action
final case class EnvironmentalDamage(player_guid: PlanetSideGUID, source_guid: PlanetSideGUID, amount: Int)
extends Action
final case class DeployItem(player_guid: PlanetSideGUID, item: Deployable) extends Action
final case class DeactivateImplantSlot(player_guid: PlanetSideGUID, slot: Int) extends Action
final case class ActivateImplantSlot(player_guid: PlanetSideGUID, slot: Int) extends Action
final case class Destroy(victim: PlanetSideGUID, killer: PlanetSideGUID, weapon: PlanetSideGUID, pos: Vector3)

View file

@ -4,7 +4,7 @@ package net.psforever.services.local
import akka.actor.{Actor, Props}
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.{TriggeredEffect, TriggeredEffectLocation}
import net.psforever.packet.game.{ObjectCreateMessage, TriggeredEffect, TriggeredEffectLocation}
import net.psforever.services.local.support.CaptureFlagManager
import net.psforever.types.PlanetSideGUID
import net.psforever.services.local.support._
@ -45,6 +45,16 @@ class LocalService(zone: Zone) extends Actor {
case LocalServiceMessage(forChannel, action) =>
action match {
case LocalAction.DeployItem(item) =>
val definition = item.Definition
val objectData = definition.Packet.ConstructorData(item).get
LocalEvents.publish(
LocalServiceResponse(
s"/$forChannel/Local",
Service.defaultPlayerGUID,
LocalResponse.SendResponse(ObjectCreateMessage(definition.ObjectId, item.GUID, objectData))
)
)
case LocalAction.DeployableMapIcon(player_guid, behavior, deployInfo) =>
LocalEvents.publish(
LocalServiceResponse(

View file

@ -27,6 +27,7 @@ object LocalServiceMessage {
object LocalAction {
trait Action
final case class DeployItem(item: Deployable) extends Action
final case class DeployableMapIcon(
player_guid: PlanetSideGUID,
behavior: DeploymentAction.Value,

View file

@ -57,7 +57,7 @@ class DeployableBehaviorSetupTest extends ActorTest {
case _ => assert(false, "self-setup test - no spawn fx")
}
eventsMsgs(1) match {
case AvatarServiceMessage("test", AvatarAction.DeployItem(PlanetSideGUID(0), obj)) =>
case LocalServiceMessage("test", LocalAction.DeployItem(obj)) =>
assert(obj eq jmine, "self-setup test - not same mine")
case _ =>
assert( false, "self-setup test - wrong deploy message")
@ -189,7 +189,7 @@ class DeployableBehaviorSetupOwnedP2Test extends FreedContextActorTest {
case _ => assert(false, "owned setup test, 2 - no spawn fx")
}
eventsMsgs(3) match {
case AvatarServiceMessage("test", AvatarAction.DeployItem(PlanetSideGUID(0), obj)) =>
case LocalServiceMessage("test", LocalAction.DeployItem(obj)) =>
assert(obj eq jmine, "owned setup test, 2 - not same mine")
case _ =>
assert( false, "owned setup test, 2 - wrong deploy message")

View file

@ -48,7 +48,7 @@ class TelepadDeployableNoRouterTest extends ActorTest {
val eventsMsgs = eventsProbe.receiveN(4, 10.seconds)
eventsMsgs.head match {
case AvatarServiceMessage("test", AvatarAction.DeployItem(PlanetSideGUID(0), obj)) =>
case LocalServiceMessage("test", LocalAction.DeployItem(obj)) =>
assert(obj eq telepad, "no-router telepad deployable testt - not same telepad")
case _ =>
assert( false, "no-router telepad deployable test - wrong deploy message")
@ -117,7 +117,7 @@ class TelepadDeployableNoActivationTest extends ActorTest {
val eventsMsgs = eventsProbe.receiveN(4, 10.seconds)
eventsMsgs.head match {
case AvatarServiceMessage("test", AvatarAction.DeployItem(PlanetSideGUID(0), obj)) =>
case LocalServiceMessage("test", LocalAction.DeployItem(obj)) =>
assert(obj eq telepad, "no-activate telepad deployable testt - not same telepad")
case _ =>
assert( false, "no-activate telepad deployable test - wrong deploy message")
@ -190,7 +190,7 @@ class TelepadDeployableAttemptTest extends ActorTest {
val eventsMsgs = eventsProbe.receiveN(2, 10.seconds)
val routerMsgs = routerProbe.receiveN(1, 10.seconds)
eventsMsgs.head match {
case AvatarServiceMessage("test", AvatarAction.DeployItem(PlanetSideGUID(0), obj)) =>
case LocalServiceMessage("test", LocalAction.DeployItem(obj)) =>
assert(obj eq telepad, "link attempt telepad deployable testt - not same telepad")
case _ =>
assert( false, "link attempt telepad deployable test - wrong deploy message")
@ -263,7 +263,7 @@ class TelepadDeployableResponseFromRouterTest extends FreedContextActorTest {
val eventsMsgs = eventsProbe.receiveN(9, 10.seconds)
eventsMsgs.head match {
case AvatarServiceMessage("test", AvatarAction.DeployItem(PlanetSideGUID(0), obj)) =>
case LocalServiceMessage("test", LocalAction.DeployItem(obj)) =>
assert(obj eq telepad, "link to router test - not same telepad")
case _ =>
assert( false, "link to router test - wrong deploy message")

View file

@ -78,6 +78,31 @@ class LocalService5Test extends ActorTest {
}
}
class DeployItemTest extends ActorTest {
ServiceManager.boot(system)
val service = system.actorOf(Props(classOf[LocalService], Zone.Nowhere), "deploy-item-test-service")
val objDef = GlobalDefinitions.motionalarmsensor
val obj = new SensorDeployable(objDef)
obj.Position = Vector3(1, 2, 3)
obj.Orientation = Vector3(4, 5, 6)
obj.GUID = PlanetSideGUID(40)
val pkt = ObjectCreateMessage(
objDef.ObjectId,
obj.GUID,
objDef.Packet.ConstructorData(obj).get
)
"AvatarService" should {
"pass DeployItem" in {
service ! Service.Join("test")
service ! LocalServiceMessage("test", LocalAction.DeployItem(obj))
expectMsg(LocalServiceResponse("/test/Local", PlanetSideGUID(0), LocalResponse.SendResponse(pkt)))
}
}
}
class DeployableMapIconTest extends ActorTest {
ServiceManager.boot(system)