Yellow Ownership (#1226)

* just some tinkering and clean-up

* 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

* restoration of yellow ring of ownership around deployables; changes to variant of CommonFieldData transcorder used on certain deployable transcoders; static values are assigned parameter names and public variables are given types for completion

* initial packet for GenericObjectAction2Message and tests; repaired transcoders and tests for TRAP and small turrets

* force redraw of the whole boomer to assert reassignment of ownership; it's heavy-handed but it works

* deployable ownership should be asserted during both re-zoning and revival; refactoring of code in ZoningOperations
This commit is contained in:
Fate-JH 2024-08-22 23:33:47 -04:00 committed by GitHub
parent d2d7c2e09b
commit 9708bf9beb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
49 changed files with 593 additions and 502 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 { class DroptItemTest extends ActorTest {
ServiceManager.boot(system) ServiceManager.boot(system)
val service = system.actorOf(Props(classOf[AvatarService], Zone.Nowhere), "release-test-service") val service = system.actorOf(Props(classOf[AvatarService], Zone.Nowhere), "release-test-service")

View file

@ -563,7 +563,7 @@ class GeneralOperations(
* @param unk2 na * @param unk2 na
*/ */
def hackObject(targetGuid: PlanetSideGUID, unk1: Long, unk2: HackState7): Unit = { 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

@ -8,8 +8,9 @@ import akka.pattern.ask
import akka.util.Timeout import akka.util.Timeout
import net.psforever.actors.session.spectator.SpectatorMode import net.psforever.actors.session.spectator.SpectatorMode
import net.psforever.login.WorldSession import net.psforever.login.WorldSession
import net.psforever.objects.avatar.BattleRank import net.psforever.objects.avatar.{BattleRank, DeployableToolbox}
import net.psforever.objects.avatar.scoring.{CampaignStatistics, ScoreCard, SessionStatistics} import net.psforever.objects.avatar.scoring.{CampaignStatistics, ScoreCard, SessionStatistics}
import net.psforever.objects.definition.converter.OCM
import net.psforever.objects.inventory.InventoryItem import net.psforever.objects.inventory.InventoryItem
import net.psforever.objects.serverobject.interior.Sidedness import net.psforever.objects.serverobject.interior.Sidedness
import net.psforever.objects.serverobject.mount.Seat import net.psforever.objects.serverobject.mount.Seat
@ -28,7 +29,7 @@ import scala.util.Success
// //
import net.psforever.actors.session.{AvatarActor, SessionActor} import net.psforever.actors.session.{AvatarActor, SessionActor}
import net.psforever.login.WorldSession.RemoveOldEquipmentFromInventory import net.psforever.login.WorldSession.RemoveOldEquipmentFromInventory
import net.psforever.objects.avatar.{Avatar, DeployableToolbox} import net.psforever.objects.avatar.Avatar
import net.psforever.objects.avatar.{Award, AwardCategory, PlayerControl, Shortcut => AvatarShortcut} import net.psforever.objects.avatar.{Award, AwardCategory, PlayerControl, Shortcut => AvatarShortcut}
import net.psforever.objects.ce.{Deployable, DeployableCategory, DeployedItem, TelepadLike} import net.psforever.objects.ce.{Deployable, DeployableCategory, DeployedItem, TelepadLike}
import net.psforever.objects.definition.SpecialExoSuitDefinition import net.psforever.objects.definition.SpecialExoSuitDefinition
@ -215,104 +216,13 @@ class ZoningOperations(
sendResponse(ReplicationStreamMessage(5, Some(6), Vector.empty)) //clear squad list sendResponse(ReplicationStreamMessage(5, Some(6), Vector.empty)) //clear squad list
sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(0), 112, 0)) // disable festive backpacks sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(0), 112, 0)) // disable festive backpacks
//find and reclaim own deployables, if any val deployables = continent.DeployableList
val foundDeployables = continent.DeployableList.filter { reclaimOurDeployables(deployables, name, manageDeployablesWith(player.GUID, avatar.deployables))
case _: BoomerDeployable => false //if we do find boomers for any reason, ignore them drawDeployableIconsOnMap(
case dobj => dobj.OwnerName.contains(player.Name) && dobj.Health > 0 depictDeployables(deployables).filter(_.Faction == faction)
}
foundDeployables.collect {
case obj if avatar.deployables.AddOverLimit(obj) =>
obj.Actor ! Deployable.Ownership(player)
}
//render deployable objects
val (turrets, normal) = continent.DeployableList.partition(obj =>
DeployableToolbox.UnifiedType(obj.Definition.Item) == DeployedItem.portable_manned_turret
) )
normal.foreach(obj => {
val definition = obj.Definition
sendResponse(
ObjectCreateMessage(
definition.ObjectId,
obj.GUID,
definition.Packet.ConstructorData(obj).get
)
)
})
turrets.foreach(obj => {
val objGUID = obj.GUID
val definition = obj.Definition
sendResponse(
ObjectCreateMessage(
definition.ObjectId,
objGUID,
definition.Packet.ConstructorData(obj).get
)
)
//seated players
obj
.asInstanceOf[Mountable]
.Seats
.values
.map(_.occupant)
.collect {
case Some(occupant) =>
if (occupant.isAlive) {
val targetDefinition = occupant.avatar.definition
sendResponse(
ObjectCreateMessage(
targetDefinition.ObjectId,
occupant.GUID,
ObjectCreateMessageParent(objGUID, 0),
targetDefinition.Packet.ConstructorData(occupant).get
)
)
}
}
//auto turret behavior
(obj match {
case turret: AutomatedTurret with JammableUnit => turret.Target
case _ => None
}).collect {
target =>
val guid = obj.GUID
val turret = obj.asInstanceOf[AutomatedTurret]
sendResponse(ObjectDetectedMessage(guid, guid, 0, List(target.GUID)))
if (!obj.asInstanceOf[JammableUnit].Jammed) {
sendResponse(ChangeFireStateMessage_Start(turret.Weapons.values.head.Equipment.get.GUID))
}
}
})
//sensor animation
normal
.filter(obj =>
obj.Definition.DeployCategory == DeployableCategory.Sensors &&
!obj.Destroyed &&
(obj match {
case jObj: JammableUnit => !jObj.Jammed
case _ => true
})
)
.foreach(obj => {
sendResponse(TriggerEffectMessage(obj.GUID, "on", unk1=true, 1000))
})
//update the health of our faction's deployables (if necessary)
//draw our faction's deployables on the map
continent.DeployableList
.filter(obj => obj.Faction == faction && !obj.Destroyed)
.foreach(obj => {
if (obj.Health != obj.DefaultHealth) {
sendResponse(PlanetsideAttributeMessage(obj.GUID, 0, obj.Health))
}
val deployInfo = DeployableInfo(
obj.GUID,
Deployable.Icon(obj.Definition.Item),
obj.Position,
obj.OwnerGuid.getOrElse(PlanetSideGUID(0))
)
sendResponse(DeployableObjectsInfoMessage(DeploymentAction.Build, deployInfo))
})
//render Equipment that was dropped into zone before the player arrived //render Equipment that was dropped into zone before the player arrived
continent.EquipmentOnGround.foreach(item => { continent.EquipmentOnGround.foreach { item =>
val definition = item.Definition val definition = item.Definition
sendResponse( sendResponse(
ObjectCreateMessage( ObjectCreateMessage(
@ -324,26 +234,19 @@ class ZoningOperations(
) )
) )
) )
}) }
//load active players in zone (excepting players who are seated or players who are us) //load active players in zone (excepting players who are seated or players who are us)
val live = continent.LivePlayers val live = continent.LivePlayers
live live
.filterNot(tplayer => { .filterNot { tplayer =>
tplayer.GUID == player.GUID || tplayer.VehicleSeated.nonEmpty tplayer.GUID == player.GUID || tplayer.VehicleSeated.nonEmpty
}) }
.foreach(targetPlayer => { .foreach { targetPlayer =>
val targetDefinition = player.avatar.definition sendResponse(OCM.apply(targetPlayer))
sendResponse(
ObjectCreateMessage(
targetDefinition.ObjectId,
targetPlayer.GUID,
targetDefinition.Packet.ConstructorData(targetPlayer).get
)
)
if (targetPlayer.UsingSpecial == SpecialExoSuitDefinition.Mode.Anchored) { if (targetPlayer.UsingSpecial == SpecialExoSuitDefinition.Mode.Anchored) {
sendResponse(PlanetsideAttributeMessage(targetPlayer.GUID, 19, 1)) sendResponse(PlanetsideAttributeMessage(targetPlayer.GUID, 19, 1))
} }
}) }
//load corpses in zone //load corpses in zone
continent.Corpses.foreach { continent.Corpses.foreach {
spawn.DepictPlayerAsCorpse spawn.DepictPlayerAsCorpse
@ -376,10 +279,7 @@ class ZoningOperations(
//active vehicles (and some wreckage) //active vehicles (and some wreckage)
vehicles.foreach { vehicle => vehicles.foreach { vehicle =>
val vguid = vehicle.GUID val vguid = vehicle.GUID
val vdefinition = vehicle.Definition sendResponse(OCM.apply(vehicle))
sendResponse(
ObjectCreateMessage(vdefinition.ObjectId, vguid, vdefinition.Packet.ConstructorData(vehicle).get)
)
//occupants other than driver (with exceptions) //occupants other than driver (with exceptions)
vehicle.Seats vehicle.Seats
.filter { .filter {
@ -393,15 +293,10 @@ class ZoningOperations(
} }
.foreach { .foreach {
case (index, seat) => case (index, seat) =>
val targetPlayer = seat.occupant.get
val targetDefiniton = targetPlayer.avatar.definition
sendResponse( sendResponse(
ObjectCreateMessage( OCM.apply(seat.occupant.get)
targetDefiniton.ObjectId, .asInstanceOf[ObjectCreateMessage]
targetPlayer.GUID, .copy(parentInfo = Some(ObjectCreateMessageParent(vguid, index)))
ObjectCreateMessageParent(vguid, index),
targetDefiniton.Packet.ConstructorData(targetPlayer).get
)
) )
} }
vehicle.SubsystemMessages().foreach { sendResponse } vehicle.SubsystemMessages().foreach { sendResponse }
@ -411,8 +306,8 @@ class ZoningOperations(
Vehicles.ReloadAccessPermissions(vehicle, player.Name) Vehicles.ReloadAccessPermissions(vehicle, player.Name)
} }
//our vehicle would have already been loaded; see NewPlayerLoaded/AvatarCreate //our vehicle would have already been loaded; see NewPlayerLoaded/AvatarCreate
usedVehicle.headOption match { usedVehicle.headOption.collect {
case Some(vehicle) => case vehicle =>
//subsystems //subsystems
vehicle.Actor ! Vehicle.UpdateSubsystemStates(player.Name, Some(false)) vehicle.Actor ! Vehicle.UpdateSubsystemStates(player.Name, Some(false))
//depict any other passengers already in this zone //depict any other passengers already in this zone
@ -430,15 +325,10 @@ class ZoningOperations(
} }
.foreach { .foreach {
case (index, seat) => case (index, seat) =>
val targetPlayer = seat.occupant.get
val targetDefinition = targetPlayer.avatar.definition
sendResponse( sendResponse(
ObjectCreateMessage( OCM.apply(seat.occupant.get)
targetDefinition.ObjectId, .asInstanceOf[ObjectCreateMessage]
targetPlayer.GUID, .copy(parentInfo = Some(ObjectCreateMessageParent(vguid, index)))
ObjectCreateMessageParent(vguid, index),
targetDefinition.Packet.ConstructorData(targetPlayer).get
)
) )
} }
//since we would have only subscribed recently, we need to reload mount access states //since we would have only subscribed recently, we need to reload mount access states
@ -449,10 +339,9 @@ class ZoningOperations(
if (vehicle.Shields > 0) { if (vehicle.Shields > 0) {
sendResponse(PlanetsideAttributeMessage(vguid, vehicle.Definition.shieldUiAttribute, vehicle.Shields)) sendResponse(PlanetsideAttributeMessage(vguid, vehicle.Definition.shieldUiAttribute, vehicle.Shields))
} }
case _ => () //no vehicle
} }
//vehicle wreckages //vehicle wreckages
wreckages.foreach(vehicle => { wreckages.foreach { vehicle =>
sendResponse( sendResponse(
ObjectCreateMessage( ObjectCreateMessage(
vehicle.Definition.DestroyedModel.get.id, vehicle.Definition.DestroyedModel.get.id,
@ -460,17 +349,14 @@ class ZoningOperations(
DestroyedVehicleConverter.converter.ConstructorData(vehicle).get DestroyedVehicleConverter.converter.ConstructorData(vehicle).get
) )
) )
}) }
//cargo occupants (including our own vehicle as cargo) //cargo occupants (including our own vehicle as cargo)
allActiveVehicles.collect { allActiveVehicles.collect {
case vehicle if vehicle.CargoHolds.nonEmpty => case vehicle if vehicle.CargoHolds.nonEmpty =>
vehicle.CargoHolds.collect { vehicle.CargoHolds.collect {
case (_index, hold: Cargo) if hold.isOccupied => case (_index, hold: Cargo) if hold.isOccupied =>
CarrierBehavior.CargoMountBehaviorForAll( //CargoMountBehaviorForUs can fail to attach the cargo vehicle on some clients
vehicle, CarrierBehavior.CargoMountBehaviorForAll(vehicle, hold.occupant.get, _index)
hold.occupant.get,
_index
) //CargoMountBehaviorForUs can fail to attach the cargo vehicle on some clients
} }
} }
//special deploy states //special deploy states
@ -492,8 +378,8 @@ class ZoningOperations(
} }
deployedVehicles.filter(_.Definition == GlobalDefinitions.router).foreach { obj => deployedVehicles.filter(_.Definition == GlobalDefinitions.router).foreach { obj =>
//the router won't work if it doesn't completely deploy //the router won't work if it doesn't completely deploy
sendResponse(DeployRequestMessage(player.GUID, obj.GUID, DriveState.Deploying, 0, unk3=false, Vector3.Zero)) sendResponse(DeployRequestMessage(player.GUID, obj.GUID, DriveState.Deploying, 0, unk3 = false, Vector3.Zero))
sendResponse(DeployRequestMessage(player.GUID, obj.GUID, DriveState.Deployed, 0, unk3=false, Vector3.Zero)) sendResponse(DeployRequestMessage(player.GUID, obj.GUID, DriveState.Deployed, 0, unk3 = false, Vector3.Zero))
sessionLogic.general.toggleTeleportSystem(obj, TelepadLike.AppraiseTeleportationSystem(obj, continent)) sessionLogic.general.toggleTeleportSystem(obj, TelepadLike.AppraiseTeleportationSystem(obj, continent))
} }
ServiceManager.serviceManager ServiceManager.serviceManager
@ -504,41 +390,30 @@ class ZoningOperations(
case _ => case _ =>
} }
//implant terminals //implant terminals
continent.map.terminalToInterface.foreach({ continent.map.terminalToInterface.foreach {
case (terminal_guid, interface_guid) => case (terminal_guid, interface_guid) =>
val parent_guid = PlanetSideGUID(terminal_guid) val parent_guid = PlanetSideGUID(terminal_guid)
continent.GUID(interface_guid) match { continent.GUID(interface_guid).collect {
case Some(obj: Terminal) => case obj: Terminal =>
val objDef = obj.Definition
sendResponse( sendResponse(
ObjectCreateMessage( OCM.apply(obj)
objDef.ObjectId, .asInstanceOf[ObjectCreateMessage]
PlanetSideGUID(interface_guid), .copy(parentInfo = Some(ObjectCreateMessageParent(parent_guid, 1)))
ObjectCreateMessageParent(parent_guid, 1),
objDef.Packet.ConstructorData(obj).get
)
) )
case _ => ()
} }
//mount terminal occupants //mount terminal occupants
continent.GUID(terminal_guid) match { continent.GUID(terminal_guid).collect {
case Some(obj: Mountable) => case obj: Mountable =>
obj.Seats(0).occupant match { obj.Seats(0).occupant.collect {
case Some(targetPlayer: Player) => case occupant: Player =>
val targetDefinition = targetPlayer.avatar.definition
sendResponse( sendResponse(
ObjectCreateMessage( OCM.apply(occupant)
targetDefinition.ObjectId, .asInstanceOf[ObjectCreateMessage]
targetPlayer.GUID, .copy(parentInfo = Some(ObjectCreateMessageParent(parent_guid, 0)))
ObjectCreateMessageParent(parent_guid, 0),
targetDefinition.Packet.ConstructorData(targetPlayer).get
)
) )
case _ => ()
} }
case _ => ()
} }
}) }
//facility turrets //facility turrets
continent.map.turretToWeapon continent.map.turretToWeapon
.map { case (turret_guid: Int, _) => continent.GUID(turret_guid) } .map { case (turret_guid: Int, _) => continent.GUID(turret_guid) }
@ -549,14 +424,10 @@ class ZoningOperations(
if (!turret.isUpgrading) { if (!turret.isUpgrading) {
turret.ControlledWeapon(wepNumber = 1).foreach { turret.ControlledWeapon(wepNumber = 1).foreach {
case obj: Tool => case obj: Tool =>
val objDef = obj.Definition
sendResponse( sendResponse(
ObjectCreateMessage( OCM.apply(obj)
objDef.ObjectId, .asInstanceOf[ObjectCreateMessage]
obj.GUID, .copy(parentInfo = Some(ObjectCreateMessageParent(pguid, 1)))
ObjectCreateMessageParent(pguid, 1),
objDef.Packet.ConstructorData(obj).get
)
) )
case _ => () case _ => ()
} }
@ -564,38 +435,19 @@ class ZoningOperations(
//reserved ammunition? //reserved ammunition?
//TODO need to register if it exists //TODO need to register if it exists
//mount turret occupant //mount turret occupant
turret.Seats(0).occupant match { turret.Seats(0).occupant.collect {
case Some(targetPlayer: Player) => case occupant: Player =>
val targetDefinition = targetPlayer.avatar.definition
sendResponse( sendResponse(
ObjectCreateMessage( OCM.apply(occupant)
targetDefinition.ObjectId, .asInstanceOf[ObjectCreateMessage]
targetPlayer.GUID, .copy(parentInfo = Some(ObjectCreateMessageParent(pguid, 0)))
ObjectCreateMessageParent(pguid, 0),
targetDefinition.Packet.ConstructorData(targetPlayer).get
)
) )
case _ => ()
}
turret.Target.collect {
target =>
val guid = turret.GUID
sendResponse(ObjectDetectedMessage(guid, guid, 0, List(target.GUID)))
if (!turret.Jammed) {
sendResponse(ChangeFireStateMessage_Start(turret.Weapons.values.head.Equipment.get.GUID))
}
} }
triggerAutomatedTurretFire(turret)
} }
//remote projectiles and radiation clouds //remote projectiles and radiation clouds
continent.Projectiles.foreach { projectile => continent.Projectiles.foreach { projectile =>
val definition = projectile.Definition sendResponse(OCM.apply(projectile))
sendResponse(
ObjectCreateMessage(
definition.ObjectId,
projectile.GUID,
definition.Packet.ConstructorData(projectile).get
)
)
} }
//spawn point update request //spawn point update request
continent.VehicleEvents ! VehicleServiceMessage( continent.VehicleEvents ! VehicleServiceMessage(
@ -1166,14 +1018,7 @@ class ZoningOperations(
// sync capture flags // sync capture flags
case llu: CaptureFlag => case llu: CaptureFlag =>
// Create LLU // Create LLU
sendResponse( sendResponse(OCM.apply(llu))
ObjectCreateMessage(
llu.Definition.ObjectId,
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.LocalEvents ! LocalServiceMessage(
@ -1755,6 +1600,188 @@ class ZoningOperations(
*/ */
def NormalKeepAlive(): Unit = {} def NormalKeepAlive(): Unit = {}
/**
* Find all deployables that internally keep track of a player's name as its owner
* and change the GUID associated with that owner (reclaim it).
* @param deployables list of deployables
* @param name owner name
* @param reclamationStrategy what happens to deployables to re-assign ownership
* @return the list of deployables whose owenrship has been re-assigned
*/
def reclaimOurDeployables(
deployables: List[Deployable],
name: String,
reclamationStrategy: Deployable => Option[Deployable]
): List[Deployable] = {
deployables
.filter {
case _: BoomerDeployable => false //always ignore
case obj => obj.OwnerName.contains(name) && !obj.Destroyed && obj.Health > 0
}
.flatMap(reclamationStrategy)
}
/**
* If the deployable was added to a management collection, reassign its internal owner GUID.
* Hence, it can fail.
* @param toGuid anticipated ownership GUID
* @param managedDeployables collection for logically organizing deployables
* @param obj deployable designated for ownership
* @return the deployable, if it was capable of being managed and ownership was re-assigned
*/
def manageDeployablesWith(toGuid: PlanetSideGUID, managedDeployables: DeployableToolbox)(obj: Deployable): Option[Deployable] = {
if (managedDeployables.AddOverLimit(obj)) {
reassignDeployablesTo(toGuid)(obj)
} else {
None
}
}
/**
* Reassign the internal owner GUID on this deployable.
* @param toGuid anticipated ownership GUID
* @param obj deployable designated for ownership
* @return the deployable, but now assigned to the GUID
*/
def reassignDeployablesTo(toGuid: PlanetSideGUID)(obj: Deployable): Option[Deployable] = {
obj.OwnerGuid = toGuid
Some(obj)
}
/**
* Render and animate all provided deployables.
* Animation includes occupants for mountable deployables and ongoing behaviors for automated deployables.
* @param deployables list of deployables
* @return list of working deployables
* @see `OCM.apply`
*/
def depictDeployables(deployables: List[Deployable]): List[Deployable] = {
val (smallTurrets, largeTurrets, sensors, normal, brokenThings) = {
val (broken, working) = deployables.partition { obj =>
obj.Destroyed || obj.Health == 0 || (obj match {
case jammable: JammableUnit => jammable.Jammed
case _ => false
})
}
val (small, remainder1) = working.partition { obj => obj.Definition.DeployCategory == DeployableCategory.SmallTurrets }
val (large, remainder2) = remainder1.partition { obj => obj.Definition.DeployCategory == DeployableCategory.FieldTurrets }
val (sensor, remainder3) = remainder2.partition { obj => obj.Definition.DeployCategory == DeployableCategory.Sensors }
(small, large, sensor, remainder3, broken)
}
val miscThings = normal ++ sensors ++ smallTurrets
(brokenThings ++ miscThings).foreach { obj =>
sendResponse(OCM.apply(obj))
}
largeTurrets.foreach { obj =>
sendResponse(OCM.apply(obj))
//seated players
obj
.asInstanceOf[Mountable]
.Seats
.values
.map(_.occupant)
.collect {
case Some(occupant) if occupant.isAlive =>
sendResponse(
OCM.apply(occupant)
.asInstanceOf[ObjectCreateMessage]
.copy(parentInfo = Some(ObjectCreateMessageParent(obj.GUID, 0)))
)
}
}
triggerAutomatedTurretFire(smallTurrets)
triggerSensorDeployables(sensors)
miscThings ++ largeTurrets
}
/**
* Render and animate all provided deployables.
* Animation includes ongoing behaviors for automated deployables.
* @param deployables list of deployables
* @return list of working deployables
* @see `OCM.apply`
*/
def depictDeployablesUponRevival(deployables: List[Deployable]): List[Deployable] = {
val (smallTurrets, sensors, normal) = {
val (_, working) = deployables.partition { obj =>
obj.Destroyed || obj.Health == 0 || (obj match {
case jammable: JammableUnit => jammable.Jammed
case _ => false
})
}
val (small, remainder1) = working.partition { obj => obj.Definition.DeployCategory == DeployableCategory.SmallTurrets }
val (sensor, remainder2) = remainder1.partition { obj => obj.Definition.DeployCategory == DeployableCategory.Sensors }
(small, sensor, remainder2)
}
val miscThings = normal ++ sensors ++ smallTurrets
miscThings.foreach { obj =>
sendResponse(OCM.apply(obj))
}
triggerAutomatedTurretFire(smallTurrets)
triggerSensorDeployables(sensors)
miscThings
}
/**
* Treat the deployables as sensor-types and provide appropriate animation.
* This animation is the glowing halo-ing effect on its sensor bulb.
* @param sensors list of deployables
* @see `TriggerEffectMessage`
*/
def triggerSensorDeployables(sensors: List[Deployable]): Unit = {
sensors.foreach { obj =>
sendResponse(TriggerEffectMessage(obj.GUID, effect = "on", unk1 = true, unk2 = 1000))
}
}
/**
* Treat the deployables as small turret-types and provide appropriate animation.
* This animation is related to its automation - tracking and shooting.
* @param turrets list of deployables
*/
def triggerAutomatedTurretFire(turrets: List[Deployable]): Unit = {
turrets.collect { case turret: AutomatedTurret =>
triggerAutomatedTurretFire(turret)
}
}
/**
* Provide appropriate animation to the small turret deployable.
* This animation is related to its automation - tracking and shooting.
* @param turret small turret deployable
* @see `ChangeFireStateMessage_Start`
* @see `ObjectDetectedMessage`
*/
def triggerAutomatedTurretFire(turret: AutomatedTurret): Unit = {
turret.Target.foreach { target =>
val guid = turret.GUID
sendResponse(ObjectDetectedMessage(guid, guid, 0, List(target.GUID)))
sendResponse(ChangeFireStateMessage_Start(turret.Weapons.values.head.Equipment.get.GUID))
}
}
/**
* Draw or redraw deployment map icons related to deployable presence and deployable management (if the owner).
* Assert deployable health as a precaution.
* @param deployables list of deployables
* @see `DeployableObjectsInfoMessage`
*/
def drawDeployableIconsOnMap(deployables: List[Deployable]): Unit = {
deployables
.foreach { obj =>
val guid = obj.GUID
val health = obj.Health
if (health != obj.DefaultHealth) {
sendResponse(PlanetsideAttributeMessage(guid, 0, health))
}
sendResponse(DeployableObjectsInfoMessage(DeploymentAction.Build, DeployableInfo(
guid,
Deployable.Icon(obj.Definition.Item),
obj.Position,
obj.OwnerGuid.getOrElse(Service.defaultPlayerGUID)
)))
}
}
/* nested class - spawn operations */ /* nested class - spawn operations */
class SpawnOperations() { class SpawnOperations() {
@ -2298,10 +2325,11 @@ class ZoningOperations(
//vehicle and driver/passenger //vehicle and driver/passenger
interstellarFerry = None interstellarFerry = None
val vdef = vehicle.Definition val vdef = vehicle.Definition
val vObjectId = vdef.ObjectId
val vguid = vehicle.GUID val vguid = vehicle.GUID
vehicle.Position = shiftPosition.getOrElse(vehicle.Position) vehicle.Position = shiftPosition.getOrElse(vehicle.Position)
vehicle.Orientation = shiftOrientation.getOrElse(vehicle.Orientation) vehicle.Orientation = shiftOrientation.getOrElse(vehicle.Orientation)
val vdata = if (seat == 0) { if (seat == 0) {
//driver //driver
if (vehicle.Zone ne continent) { if (vehicle.Zone ne continent) {
continent.Transport ! Zone.Vehicle.Spawn(vehicle) continent.Transport ! Zone.Vehicle.Spawn(vehicle)
@ -2311,7 +2339,7 @@ class ZoningOperations(
mount.unmount(player) mount.unmount(player)
player.VehicleSeated = None player.VehicleSeated = None
val data = vdef.Packet.ConstructorData(vehicle).get val data = vdef.Packet.ConstructorData(vehicle).get
sendResponse(ObjectCreateMessage(vehicle.Definition.ObjectId, vguid, data)) sendResponse(ObjectCreateMessage(vObjectId, vguid, data))
mount.mount(player) mount.mount(player)
player.VehicleSeated = vguid player.VehicleSeated = vguid
Vehicles.Own(vehicle, player) Vehicles.Own(vehicle, player)
@ -2320,7 +2348,7 @@ class ZoningOperations(
.foreach { _.MountedIn = vguid } .foreach { _.MountedIn = vguid }
events ! VehicleServiceMessage( events ! VehicleServiceMessage(
zoneid, zoneid,
VehicleAction.LoadVehicle(player.GUID, vehicle, vdef.ObjectId, vguid, data) VehicleAction.LoadVehicle(player.GUID, vehicle, vObjectId, vguid, data)
) )
carrierInfo match { carrierInfo match {
case (Some(carrier), Some((index, _))) => case (Some(carrier), Some((index, _))) =>
@ -2329,18 +2357,15 @@ class ZoningOperations(
vehicle.MountedIn = None vehicle.MountedIn = None
} }
vehicle.allowInteraction = true vehicle.allowInteraction = true
data
} else { } else {
//passenger //passenger
//non-drivers are not rendered in the vehicle at this time //non-drivers are not rendered in the vehicle at this time
val data = vdef.Packet.ConstructorData(vehicle).get sendResponse(OCM.apply(vehicle))
sendResponse(ObjectCreateMessage(vehicle.Definition.ObjectId, vguid, data))
carrierInfo match { carrierInfo match {
case (Some(carrier), Some((index, _))) => case (Some(carrier), Some((index, _))) =>
CargoMountBehaviorForUs(carrier, vehicle, index) CargoMountBehaviorForUs(carrier, vehicle, index)
case _ => () case _ => ()
} }
data
} }
val originalSeated = player.VehicleSeated val originalSeated = player.VehicleSeated
player.VehicleSeated = vguid player.VehicleSeated = vguid
@ -2357,25 +2382,26 @@ class ZoningOperations(
) )
} }
Vehicles.ReloadAccessPermissions(vehicle, player.Name) Vehicles.ReloadAccessPermissions(vehicle, player.Name)
log.debug(s"AvatarCreate (vehicle): ${player.Name}'s ${vehicle.Definition.Name}") log.debug(s"AvatarCreate (vehicle): ${player.Name}'s ${vdef.Name}")
log.trace(s"AvatarCreate (vehicle): ${player.Name}'s ${vehicle.Definition.Name} - $vguid -> $vdata")
AvatarCreateInVehicle(player, vehicle, seat) AvatarCreateInVehicle(player, vehicle, seat)
case _ => case _ =>
player.VehicleSeated = None player.VehicleSeated = None
val packet = player.avatar.definition.Packet val definition = player.avatar.definition
val data = packet.DetailedConstructorData(player).get val guid = player.GUID
val guid = player.GUID sendResponse(OCM.detailed(player))
sendResponse(ObjectCreateDetailedMessage(ObjectClass.avatar, guid, data))
continent.AvatarEvents ! AvatarServiceMessage( continent.AvatarEvents ! AvatarServiceMessage(
zoneid, zoneid,
AvatarAction.LoadPlayer(guid, ObjectClass.avatar, guid, packet.ConstructorData(player).get, None) AvatarAction.LoadPlayer(guid, definition.ObjectId, guid, definition.Packet.ConstructorData(player).get, None)
) )
log.debug(s"AvatarCreate: ${player.Name}")
log.trace(s"AvatarCreate: ${player.Name} - $guid -> $data")
} }
continent.Population ! Zone.Population.Spawn(avatar, player, avatarActor) continent.Population ! Zone.Population.Spawn(avatar, player, avatarActor)
avatarActor ! AvatarActor.RefreshPurchaseTimes() avatarActor ! AvatarActor.RefreshPurchaseTimes()
drawDeployableIconsOnMap(
depictDeployablesUponRevival(
reclaimOurDeployables(continent.DeployableList, player.Name, reassignDeployablesTo(player.GUID))
)
)
//begin looking for conditions to set the avatar //begin looking for conditions to set the avatar
context.system.scheduler.scheduleOnce(delay = 250 millisecond, context.self, SessionActor.SetCurrentAvatar(player, 200)) context.system.scheduler.scheduleOnce(delay = 250 millisecond, context.self, SessionActor.SetCurrentAvatar(player, 200))
} }
@ -2403,11 +2429,9 @@ class ZoningOperations(
val pguid = tplayer.GUID val pguid = tplayer.GUID
val vguid = vehicle.GUID val vguid = vehicle.GUID
tplayer.VehicleSeated = None tplayer.VehicleSeated = None
val pdata = pdef.Packet.DetailedConstructorData(tplayer).get
tplayer.VehicleSeated = vguid tplayer.VehicleSeated = vguid
log.debug(s"AvatarCreateInVehicle: ${player.Name}") log.debug(s"AvatarCreateInVehicle: ${player.Name}")
log.trace(s"AvatarCreateInVehicle: ${player.Name} - $pguid -> $pdata") sendResponse(OCM.detailed(tplayer))
sendResponse(ObjectCreateDetailedMessage(pdef.ObjectId, pguid, pdata))
if (seat == 0 || vehicle.WeaponControlledFromSeat(seat).nonEmpty) { if (seat == 0 || vehicle.WeaponControlledFromSeat(seat).nonEmpty) {
sendResponse(ObjectAttachMessage(vguid, pguid, seat)) sendResponse(ObjectAttachMessage(vguid, pguid, seat))
sessionLogic.general.accessContainer(vehicle) sessionLogic.general.accessContainer(vehicle)
@ -2459,13 +2483,10 @@ class ZoningOperations(
sessionLogic.vehicles.GetKnownVehicleAndSeat() match { sessionLogic.vehicles.GetKnownVehicleAndSeat() match {
case (Some(vehicle: Vehicle), Some(seat: Int)) => case (Some(vehicle: Vehicle), Some(seat: Int)) =>
//vehicle and driver/passenger //vehicle and driver/passenger
val vdef = vehicle.Definition
val vguid = vehicle.GUID val vguid = vehicle.GUID
val vdata = vdef.Packet.ConstructorData(vehicle).get sendResponse(OCM.apply(vehicle))
sendResponse(ObjectCreateMessage(vehicle.Definition.ObjectId, vguid, vdata))
Vehicles.ReloadAccessPermissions(vehicle, continent.id) Vehicles.ReloadAccessPermissions(vehicle, continent.id)
log.debug(s"AvatarCreate (vehicle): ${player.Name}'s ${vehicle.Definition.Name}") log.debug(s"AvatarCreate (vehicle): ${player.Name}'s ${vehicle.Definition.Name}")
log.trace(s"AvatarCreate (vehicle): ${player.Name}'s ${vehicle.Definition.Name} - $vguid -> $vdata")
val pdef = player.avatar.definition val pdef = player.avatar.definition
val pguid = player.GUID val pguid = player.GUID
player.VehicleSeated = None player.VehicleSeated = None

View file

@ -13,6 +13,7 @@ import net.psforever.objects.vital.interaction.DamageInteraction
import net.psforever.objects.zones.Zone import net.psforever.objects.zones.Zone
import net.psforever.services.Service import net.psforever.services.Service
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
import net.psforever.types.PlanetSideEmpire import net.psforever.types.PlanetSideEmpire
import scala.annotation.unused import scala.annotation.unused
@ -81,8 +82,14 @@ class BoomerDeployableControl(mine: BoomerDeployable)
} }
override def gainOwnership(player: Player): Unit = { override def gainOwnership(player: Player): Unit = {
mine.Faction = PlanetSideEmpire.NEUTRAL //force map icon redraw val originalOwner = mine.OwnerName
super.gainOwnership(player, player.Faction) super.gainOwnership(player, player.Faction)
val events = mine.Zone.LocalEvents
val msg = LocalAction.DeployItem(mine)
originalOwner.collect { name =>
events ! LocalServiceMessage(name, msg)
}
events ! LocalServiceMessage(player.Name, msg)
} }
override def dismissDeployable() : Unit = { override def dismissDeployable() : Unit = {

View file

@ -18,16 +18,11 @@ trait OwnableByPlayer {
def OwnerGuid_=(owner: Player): Option[PlanetSideGUID] = OwnerGuid_=(Some(owner.GUID)) def OwnerGuid_=(owner: Player): Option[PlanetSideGUID] = OwnerGuid_=(Some(owner.GUID))
def OwnerGuid_=(owner: Option[PlanetSideGUID]): Option[PlanetSideGUID] = { def OwnerGuid_=(owner: Option[PlanetSideGUID]): Option[PlanetSideGUID] = {
owner match { ownerGuid = owner
case Some(_) =>
ownerGuid = owner
case None =>
ownerGuid = None
}
OwnerGuid OwnerGuid
} }
def OwnerName: Option[String] = owner.map { _.name } def OwnerName: Option[String] = owner.map(_.name)
def OriginalOwnerName: Option[String] = originalOwnerName def OriginalOwnerName: Option[String] = originalOwnerName
@ -47,7 +42,7 @@ trait OwnableByPlayer {
(originalOwnerName, playerOpt) match { (originalOwnerName, playerOpt) match {
case (None, Some(player)) => case (None, Some(player)) =>
owner = Some(UniquePlayer(player)) owner = Some(UniquePlayer(player))
originalOwnerName = originalOwnerName.orElse { Some(player.Name) } originalOwnerName = originalOwnerName.orElse(Some(player.Name))
OwnerGuid = player OwnerGuid = player
case (_, Some(player)) => case (_, Some(player)) =>
owner = Some(UniquePlayer(player)) owner = Some(UniquePlayer(player))

View file

@ -524,7 +524,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
log.warn(s"${player.Name} failed to pick up an item ($item_guid) from the ground because $reason") log.warn(s"${player.Name} failed to pick up an item ($item_guid) from the ground because $reason")
case Player.BuildDeployable(obj: TelepadDeployable, tool: Telepad) => case Player.BuildDeployable(obj: TelepadDeployable, tool: Telepad) =>
obj.Router = tool.Router //necessary; forwards link to the router that prodcued the telepad obj.Router = tool.Router //necessary; forwards link to the router that produced the telepad
setupDeployable(obj, tool) setupDeployable(obj, tool)
case Player.BuildDeployable(obj, tool) => case Player.BuildDeployable(obj, tool) =>
@ -534,7 +534,6 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
deployablePair match { deployablePair match {
case Some((deployable, tool)) if deployable eq obj => case Some((deployable, tool)) if deployable eq obj =>
val zone = player.Zone val zone = player.Zone
//boomers
val trigger = new BoomerTrigger val trigger = new BoomerTrigger
trigger.Companion = obj.GUID trigger.Companion = obj.GUID
obj.Trigger = trigger obj.Trigger = trigger
@ -552,7 +551,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
TaskWorkflow.execute(PutNewEquipmentInInventoryOrDrop(player)(trigger)) TaskWorkflow.execute(PutNewEquipmentInInventoryOrDrop(player)(trigger))
} }
Players.buildCooldownReset(zone, player.Name, obj) Players.buildCooldownReset(zone, player.Name, obj)
case _ => ; case _ => ()
} }
deployablePair = None deployablePair = None
@ -569,7 +568,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
TelepadControl.TelepadError(zone, player.Name, msg = "@Telepad_NoDeploy_RouterLost") TelepadControl.TelepadError(zone, player.Name, msg = "@Telepad_NoDeploy_RouterLost")
} }
Players.buildCooldownReset(zone, player.Name, obj) Players.buildCooldownReset(zone, player.Name, obj)
case _ => ; case _ => ()
} }
deployablePair = None deployablePair = None
@ -584,7 +583,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
case None => case None =>
log.warn(s"${player.Name} should have destroyed a ${tool.Definition.Name} here, but could not find it") log.warn(s"${player.Name} should have destroyed a ${tool.Definition.Name} here, but could not find it")
} }
case _ => ; case _ => ()
} }
deployablePair = None deployablePair = None
@ -701,16 +700,27 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
if (deployables.Valid(obj) && if (deployables.Valid(obj) &&
!deployables.Contains(obj) && !deployables.Contains(obj) &&
Players.deployableWithinBuildLimits(player, obj)) { Players.deployableWithinBuildLimits(player, obj)) {
//deployables, upon construction, may display an animation effect
tool.Definition match { tool.Definition match {
case GlobalDefinitions.ace | /* animation handled in deployable lifecycle */ case GlobalDefinitions.router_telepad => () /* no special animation */
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 case GlobalDefinitions.advanced_ace
if obj.Definition.deployAnimation == DeployAnimation.Fdu => if obj.Definition.deployAnimation == DeployAnimation.Fdu =>
zone.AvatarEvents ! AvatarServiceMessage(zone.id, AvatarAction.PutDownFDU(player.GUID)) zone.AvatarEvents ! AvatarServiceMessage(zone.id, AvatarAction.PutDownFDU(player.GUID))
case _ => case _ =>
org.log4s.getLogger(name = "Deployables").warn( org.log4s
s"not sure what kind of construction item to animate - ${tool.Definition.Name}" .getLogger(name = "Deployables")
) .warn(s"not sure what kind of construction item to animate - ${tool.Definition.Name}")
} }
deployablePair = Some((obj, tool)) deployablePair = Some((obj, tool))
obj.Faction = player.Faction obj.Faction = player.Faction
@ -1158,10 +1168,14 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
zone.GUID(trigger.Companion) match { zone.GUID(trigger.Companion) match {
case Some(obj: BoomerDeployable) => case Some(obj: BoomerDeployable) =>
val deployables = player.avatar.deployables val deployables = player.avatar.deployables
if (deployables.Valid(obj)) { if (!deployables.Contains(obj) && deployables.Valid(obj)) {
events ! AvatarServiceMessage(toChannel, AvatarAction.SendResponse(
Service.defaultPlayerGUID,
GenericObjectAction2Message(1, player.GUID, trigger.GUID)
))
Players.gainDeployableOwnership(player, obj, deployables.AddOverLimit) Players.gainDeployableOwnership(player, obj, deployables.AddOverLimit)
} }
case _ => ; case _ => ()
} }
case citem: ConstructionItem case citem: ConstructionItem
@ -1172,7 +1186,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
} }
Deployables.initializeConstructionItem(player.avatar.certifications, citem) Deployables.initializeConstructionItem(player.avatar.certifications, citem)
case _ => ; case _ => ()
} }
events ! AvatarServiceMessage( events ! AvatarServiceMessage(
toChannel, toChannel,

View file

@ -4,7 +4,6 @@ package net.psforever.objects.ce
import akka.actor.{Actor, ActorRef, Cancellable} import akka.actor.{Actor, ActorRef, Cancellable}
import net.psforever.objects.guid.{GUIDTask, TaskWorkflow} import net.psforever.objects.guid.{GUIDTask, TaskWorkflow}
import net.psforever.objects._ import net.psforever.objects._
import net.psforever.objects.definition.DeployAnimation
import net.psforever.objects.zones.Zone import net.psforever.objects.zones.Zone
import net.psforever.packet.game._ import net.psforever.packet.game._
import net.psforever.services.Service import net.psforever.services.Service
@ -75,7 +74,7 @@ trait DeployableBehavior {
} }
case Deployable.Ownership(Some(player)) case Deployable.Ownership(Some(player))
if !DeployableObject.Destroyed && DeployableObject.OwnerGuid.isEmpty => if !DeployableObject.Destroyed /*&& DeployableObject.OwnerGuid.isEmpty*/ =>
if (constructed.contains(true)) { if (constructed.contains(true)) {
gainOwnership(player) gainOwnership(player)
} else { } else {
@ -105,6 +104,7 @@ trait DeployableBehavior {
def loseOwnership(obj: Deployable, toFaction: PlanetSideEmpire.Value): Unit = { def loseOwnership(obj: Deployable, toFaction: PlanetSideEmpire.Value): Unit = {
DeployableBehavior.changeOwnership( DeployableBehavior.changeOwnership(
obj, obj,
toOwner = "",
toFaction, toFaction,
DeployableInfo(obj.GUID, Deployable.Icon.apply(obj.Definition.Item), obj.Position, Service.defaultPlayerGUID) 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 = { def gainOwnership(player: Player, toFaction: PlanetSideEmpire.Value): Unit = {
val obj = DeployableObject val obj = DeployableObject
obj.AssignOwnership(player)
decay.cancel() decay.cancel()
DeployableBehavior.changeOwnership( DeployableBehavior.changeOwnership(
obj, obj,
player.Name,
toFaction, toFaction,
DeployableInfo(obj.GUID, Deployable.Icon.apply(obj.Definition.Item), obj.Position, obj.OwnerGuid.get) 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. * 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. * Parameters are required to be passed onto the next stage of the build process and are not used here.
* @see `DeployableDefinition.deployAnimation` * @see `DeployableDefinition.deployAnimation`
* @see `DeployableDefinition.DeployTime` * @see `DeployableDefinition.DeployTime`
@ -157,21 +157,9 @@ trait DeployableBehavior {
* @param callback an `ActorRef` used for confirming the deployable's completion of the process * @param callback an `ActorRef` used for confirming the deployable's completion of the process
*/ */
def setupDeployable(callback: ActorRef): Unit = { def setupDeployable(callback: ActorRef): Unit = {
import scala.concurrent.ExecutionContext.Implicits.global
val obj = DeployableObject val obj = DeployableObject
constructed = Some(false) 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( setup = context.system.scheduler.scheduleOnce(
obj.Definition.DeployTime milliseconds, obj.Definition.DeployTime milliseconds,
self, 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, * 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 * 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. * and can linger in the zone ownerless for as long as it is not destroyed.
* @see `AvatarAction.DeployItem` * @see `LocalAction.DeployItem`
* @see `DeploymentAction` * @see `DeploymentAction`
* @see `DeployableInfo` * @see `DeployableInfo`
* @see `LocalAction.DeployableMapIcon` * @see `LocalAction.DeployableMapIcon`
@ -197,27 +185,22 @@ trait DeployableBehavior {
setup = Default.Cancellable setup = Default.Cancellable
constructed = Some(true) constructed = Some(true)
val obj = DeployableObject val obj = DeployableObject
val zone = obj.Zone val zone = obj.Zone
val localEvents = zone.LocalEvents val localEvents = zone.LocalEvents
val owner = obj.OwnerGuid.getOrElse(Service.defaultPlayerGUID) obj.OwnerName.orElse {
obj.OwnerName match { import scala.concurrent.ExecutionContext.Implicits.global
case Some(_) => decay = context.system.scheduler.scheduleOnce(Deployable.decay, self, Deployable.Deconstruct())
case None => None
import scala.concurrent.ExecutionContext.Implicits.global
decay = context.system.scheduler.scheduleOnce(
Deployable.decay,
self,
Deployable.Deconstruct()
)
} }
//zone build //zone build
zone.AvatarEvents ! AvatarServiceMessage(zone.id, AvatarAction.DeployItem(Service.defaultPlayerGUID, obj)) localEvents ! LocalServiceMessage(zone.id, LocalAction.DeployItem(obj))
//zone map icon //zone map icon
localEvents ! LocalServiceMessage( localEvents ! LocalServiceMessage(
obj.Faction.toString, obj.Faction.toString,
LocalAction.DeployableMapIcon( LocalAction.DeployableMapIcon(
Service.defaultPlayerGUID, 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 //local build management
@ -290,28 +273,28 @@ object DeployableBehavior {
* @param toFaction na * @param toFaction na
* @param info 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 originalFaction = obj.Faction
val zone = obj.Zone
val localEvents = zone.LocalEvents
if (originalFaction != toFaction) { if (originalFaction != toFaction) {
val guid = obj.GUID
val zone = obj.Zone
val localEvents = zone.LocalEvents
obj.Faction = toFaction obj.Faction = toFaction
//visual tells in regards to ownership by faction //visual tells in regards to ownership by faction
zone.AvatarEvents ! AvatarServiceMessage( zone.AvatarEvents ! AvatarServiceMessage(
zone.id, zone.id,
AvatarAction.SetEmpire(Service.defaultPlayerGUID, guid, toFaction) AvatarAction.SetEmpire(Service.defaultPlayerGUID, dGuid, toFaction)
) )
//remove knowledge by the previous owner's faction //remove knowledge by the previous owner's faction
localEvents ! LocalServiceMessage( localEvents ! LocalServiceMessage(
originalFaction.toString, originalFaction.toString,
LocalAction.DeployableMapIcon(Service.defaultPlayerGUID, DeploymentAction.Dismiss, info) LocalAction.DeployableMapIcon(Service.defaultPlayerGUID, DeploymentAction.Dismiss, info)
) )
//display to the given faction
localEvents ! LocalServiceMessage(
toFaction.toString,
LocalAction.DeployableMapIcon(Service.defaultPlayerGUID, DeploymentAction.Build, info)
)
} }
//display to the given faction
localEvents ! LocalServiceMessage(
toFaction.toString,
LocalAction.DeployableMapIcon(Service.defaultPlayerGUID, DeploymentAction.Build, info)
)
} }
} }

View file

@ -24,7 +24,7 @@ class SmallTurretConverter extends ObjectCreateConverter[TurretDeployable]() {
v1 = true, v1 = true,
None, None,
obj.Jammed, obj.Jammed,
Some(true), None,
None, None,
obj.OwnerGuid match { obj.OwnerGuid match {
case Some(owner) => owner case Some(owner) => owner
@ -67,7 +67,7 @@ object SmallTurretConverter {
private def MakeMountings(obj: WeaponTurret): List[InventoryItemData.InventoryItem] = { private def MakeMountings(obj: WeaponTurret): List[InventoryItemData.InventoryItem] = {
obj.Weapons obj.Weapons
.map({ .map({
case ((index, slot)) => case (index, slot) =>
val equip: Equipment = slot.Equipment.get val equip: Equipment = slot.Equipment.get
val equipDef = equip.Definition val equipDef = equip.Definition
InventoryItemData(equipDef.ObjectId, equip.GUID, index, equipDef.Packet.ConstructorData(equip).get) InventoryItemData(equipDef.ObjectId, equip.GUID, index, equipDef.Packet.ConstructorData(equip).get)

View file

@ -19,10 +19,10 @@ class TRAPConverter extends ObjectCreateConverter[TrapDeployable]() {
obj.Faction, obj.Faction,
bops = false, bops = false,
alternate = false, alternate = false,
true, v1 = true,
None,
jammered = false,
None, None,
false,
Some(true),
None, None,
obj.OwnerGuid match { obj.OwnerGuid match {
case Some(owner) => owner case Some(owner) => owner
@ -42,9 +42,9 @@ class TRAPConverter extends ObjectCreateConverter[TrapDeployable]() {
obj.Faction, obj.Faction,
bops = false, bops = false,
alternate = true, alternate = true,
true, v1 = true,
None, None,
false, jammered = false,
Some(true), Some(true),
None, None,
PlanetSideGUID(0) PlanetSideGUID(0)

View file

@ -455,7 +455,7 @@ object GamePacketOpcode extends Enumeration {
case 0x7f => game.AvatarStatisticsMessage.decode case 0x7f => game.AvatarStatisticsMessage.decode
// OPCODES 0x80-8f // OPCODES 0x80-8f
case 0x80 => noDecoder(GenericObjectAction2Message) case 0x80 => game.GenericObjectAction2Message.decode
case 0x81 => game.DestroyDisplayMessage.decode case 0x81 => game.DestroyDisplayMessage.decode
case 0x82 => noDecoder(TriggerBotAction) case 0x82 => noDecoder(TriggerBotAction)
case 0x83 => game.SquadWaypointRequest.decode case 0x83 => game.SquadWaypointRequest.decode

View file

@ -0,0 +1,33 @@
// Copyright (c) 2024 PSForever
package net.psforever.packet.game
import net.psforever.packet.GamePacketOpcode.Type
import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
import net.psforever.types.PlanetSideGUID
import scodec.bits.BitVector
import scodec.codecs._
import scodec.{Attempt, Codec}
/**
* na
* @param unk na
* @param guid1 na
* @param guid2 na
*/
final case class GenericObjectAction2Message(
unk: Int,
guid1: PlanetSideGUID,
guid2: PlanetSideGUID
) extends PlanetSideGamePacket {
type Packet = GenericObjectActionMessage
def opcode: Type = GamePacketOpcode.GenericObjectAction2Message
def encode: Attempt[BitVector] = GenericObjectAction2Message.encode(this)
}
object GenericObjectAction2Message extends Marshallable[GenericObjectAction2Message] {
implicit val codec: Codec[GenericObjectAction2Message] = (
("unk" | uint(bits = 3)) :: //dword_D32FC0
("guid1" | PlanetSideGUID.codec) ::
("guid2" | PlanetSideGUID.codec)
).as[GenericObjectAction2Message]
}

View file

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

View file

@ -21,7 +21,10 @@ object AegisShieldGeneratorData extends Marshallable[AegisShieldGeneratorData] {
implicit val codec: Codec[AegisShieldGeneratorData] = ( implicit val codec: Codec[AegisShieldGeneratorData] = (
("deploy" | CommonFieldDataWithPlacement.codec) :: ("deploy" | CommonFieldDataWithPlacement.codec) ::
("health" | uint8L) :: ("health" | uint8L) ::
uint32 :: uint32 :: uint32 :: uint4L //100 bits uint32 ::
uint32 ::
uint32 ::
uint4L
).exmap[AegisShieldGeneratorData]( ).exmap[AegisShieldGeneratorData](
{ {
case deploy :: health :: 0 :: 0 :: 0 :: 0 :: HNil => case deploy :: health :: 0 :: 0 :: 0 :: 0 :: HNil =>

View file

@ -72,7 +72,7 @@ object BattleFrameRoboticsData extends Marshallable[BattleFrameRoboticsData] {
("proper_anim" | bool) :: //when unflagged, bfr stands, even if unmanned ("proper_anim" | bool) :: //when unflagged, bfr stands, even if unmanned
("unk3" | uint4) :: ("unk3" | uint4) ::
("show_bfr_shield" | bool) :: ("show_bfr_shield" | bool) ::
optional(bool, target = "inventory" | MountableInventory.custom_inventory_codec(pos.vel.isDefined, VehicleFormat.Battleframe)) ("inventory" | optional(bool, MountableInventory.custom_inventory_codec(pos.vel.isDefined, VehicleFormat.Battleframe)))
} }
).exmap[BattleFrameRoboticsData] ( ).exmap[BattleFrameRoboticsData] (
{ {
@ -107,7 +107,7 @@ object BattleFrameRoboticsData extends Marshallable[BattleFrameRoboticsData] {
("unk3" | uint4) :: ("unk3" | uint4) ::
("show_bfr_shield" | bool) :: ("show_bfr_shield" | bool) ::
("unk4" | bool) :: ("unk4" | bool) ::
optional(bool, target = "inventory" | MountableInventory.custom_inventory_codec(pos.vel.isDefined, VehicleFormat.BattleframeFlight)) ("inventory" | optional(bool, MountableInventory.custom_inventory_codec(pos.vel.isDefined, VehicleFormat.BattleframeFlight)))
} }
).exmap[BattleFrameRoboticsData] ( ).exmap[BattleFrameRoboticsData] (
{ {

View file

@ -44,7 +44,7 @@ object CaptureFlagData extends Marshallable[CaptureFlagData] {
("targetBaseGuid" | uint8L) :: ("targetBaseGuid" | uint8L) ::
uint8L :: uint8L ::
("milliseconds_remaining" | uint32L) :: ("milliseconds_remaining" | uint32L) ::
uint(1) uint(bits = 1)
).exmap[CaptureFlagData]( ).exmap[CaptureFlagData](
{ {
case pos :: faction :: false :: 4 :: 0 :: owningBaseGuid :: 0 :: targetBaseGuid :: 0 :: milliseconds_remaining :: 0 :: HNil => case pos :: faction :: false :: 4 :: 0 :: owningBaseGuid :: 0 :: targetBaseGuid :: 0 :: milliseconds_remaining :: 0 :: HNil =>

View file

@ -406,7 +406,7 @@ object CharacterAppearanceData extends Marshallable[CharacterAppearanceData] {
("unk6" | bool) :: //stream misalignment when set ("unk6" | bool) :: //stream misalignment when set
("charging_pose" | bool) :: ("charging_pose" | bool) ::
("unk7" | bool) :: //alternate charging pose? ("unk7" | bool) :: //alternate charging pose?
optional(bool, "on_zipline" | zipline_codec) ("on_zipline" | optional(bool, zipline_codec))
).exmap[CharacterAppearanceB]( ).exmap[CharacterAppearanceB](
{ {
case outfit_id :: outfit :: logo :: u1 :: bpack :: u2 :: u3 :: u4 :: facingPitch :: facingYawUpper :: lfs :: gstate :: cloaking :: u5 :: u6 :: charging :: u7 :: zipline :: HNil => case outfit_id :: outfit :: logo :: u1 :: bpack :: u2 :: u3 :: u4 :: facingPitch :: facingYawUpper :: lfs :: gstate :: cloaking :: u5 :: u6 :: charging :: u7 :: zipline :: HNil =>
@ -493,5 +493,5 @@ object CharacterAppearanceData extends Marshallable[CharacterAppearanceData] {
} }
) )
implicit val codec: Codec[CharacterAppearanceData] = codec(0) implicit val codec: Codec[CharacterAppearanceData] = codec(name_padding = 0)
} }

View file

@ -18,13 +18,13 @@ import shapeless.{::, HNil}
object ImplantEffects extends Enumeration { object ImplantEffects extends Enumeration {
type Type = Value type Type = Value
val SurgeEffects = Value(9) val SurgeEffects: ImplantEffects.Value = Value(9)
val PersonalShieldEffects = Value(5) val PersonalShieldEffects: ImplantEffects.Value = Value(5)
val DarklightEffects = Value(3) val DarklightEffects: ImplantEffects.Value = Value(3)
val RegenEffects = Value(0) val RegenEffects: ImplantEffects.Value = Value(0)
val NoEffects = Value(1) val NoEffects: ImplantEffects.Value = Value(1)
implicit val codec = PacketHelpers.createEnumerationCodec(this, uint4L) implicit val codec: Codec[ImplantEffects.Value] = PacketHelpers.createEnumerationCodec(this, uint4L)
} }
/** /**
@ -120,9 +120,9 @@ object CharacterData extends Marshallable[CharacterData] {
("armor" | uint8L) :: ("armor" | uint8L) ::
(("uniform_upgrade" | UniformStyle.codec) >>:~ { style => (("uniform_upgrade" | UniformStyle.codec) >>:~ { style =>
uint(bits = 3) :: //uniform_upgrade is actually interpreted as a 6u field, but the lower 3u seems to be discarded uint(bits = 3) :: //uniform_upgrade is actually interpreted as a 6u field, but the lower 3u seems to be discarded
("command_rank" | uintL(3)) :: ("command_rank" | uint(bits = 3)) ::
listOfN(uint2, "implant_effects" | ImplantEffects.codec) :: ("implant_effects" | listOfN(uint2, ImplantEffects.codec)) ::
("cosmetics" | conditional(BattleRank.showCosmetics(style), Cosmetic.codec)) ("cosmetics" | conditional(BattleRank.showCosmetics(style), Cosmetic.codec))
}) })
).exmap[CharacterData]( ).exmap[CharacterData](
{ {
@ -158,24 +158,24 @@ object CharacterData extends Marshallable[CharacterData] {
def codec_seated(is_backpack: Boolean): Codec[CharacterData] = def codec_seated(is_backpack: Boolean): Codec[CharacterData] =
( (
("uniform_upgrade" | UniformStyle.codec) >>:~ { style => ("uniform_upgrade" | UniformStyle.codec) >>:~ { style =>
uint(3) :: //uniform_upgrade is actually interpreted as a 6u field, but the lower 3u seems to be discarded uint(bits = 3) :: //uniform_upgrade is actually interpreted as a 6u field, but the lower 3u seems to be discarded
("command_rank" | uintL(3)) :: ("command_rank" | uintL(bits = 3)) ::
listOfN(uint2, "implant_effects" | ImplantEffects.codec) :: ("implant_effects" | listOfN(uint2, ImplantEffects.codec)) ::
("cosmetics" | conditional(BattleRank.showCosmetics(style), Cosmetic.codec)) ("cosmetics" | conditional(BattleRank.showCosmetics(style), Cosmetic.codec))
} }
).exmap[CharacterData]( ).exmap[CharacterData](
{ {
case uniform :: unk :: cr :: implant_effects :: cosmetics :: HNil => case uniform :: unk :: cr :: implant_effects :: cosmetics :: HNil =>
Attempt.Successful( Attempt.Successful(
new CharacterData( new CharacterData(
100, health = 100,
0, armor = 0,
uniform, uniform,
unk, unk,
cr, cr,
implant_effects, implant_effects,
cosmetics cosmetics
)(is_backpack, true) )(is_backpack, is_seated = true)
) )
case _ => case _ =>
@ -195,5 +195,5 @@ object CharacterData extends Marshallable[CharacterData] {
} }
) )
implicit val codec: Codec[CharacterData] = codec(false) implicit val codec: Codec[CharacterData] = codec(is_backpack = false)
} }

View file

@ -75,19 +75,19 @@ object CommonFieldData extends Marshallable[CommonFieldData] {
* @return a `CommonFieldData` object * @return a `CommonFieldData` object
*/ */
def apply(): CommonFieldData = def apply(): CommonFieldData =
CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, false, None, false, None, None, PlanetSideGUID(0)) CommonFieldData(PlanetSideEmpire.NEUTRAL, bops = false, alternate = false, v1 = false, None, jammered = false, None, None, PlanetSideGUID(0))
def apply(faction: PlanetSideEmpire.Value): CommonFieldData = def apply(faction: PlanetSideEmpire.Value): CommonFieldData =
CommonFieldData(faction, false, false, false, None, false, None, None, PlanetSideGUID(0)) CommonFieldData(faction, bops = false, alternate = false, v1 = false, None, jammered = false, None, None, PlanetSideGUID(0))
def apply(faction: PlanetSideEmpire.Value, unk: Int): CommonFieldData = def apply(faction: PlanetSideEmpire.Value, unk: Int): CommonFieldData =
CommonFieldData(faction, false, false, unk > 1, None, unk > 0, None, None, PlanetSideGUID(0)) CommonFieldData(faction, bops = false, alternate = false, v1 = unk > 1, None, jammered = unk > 0, None, None, PlanetSideGUID(0))
def apply(faction: PlanetSideEmpire.Value, unk: Int, player_guid: PlanetSideGUID): CommonFieldData = def apply(faction: PlanetSideEmpire.Value, unk: Int, player_guid: PlanetSideGUID): CommonFieldData =
CommonFieldData(faction, false, false, unk > 1, None, unk > 0, None, None, player_guid) CommonFieldData(faction, bops = false, alternate = false, v1 = unk > 1, None, jammered = unk > 0, None, None, player_guid)
def apply(faction: PlanetSideEmpire.Value, destroyed: Boolean, unk: Int): CommonFieldData = def apply(faction: PlanetSideEmpire.Value, destroyed: Boolean, unk: Int): CommonFieldData =
CommonFieldData(faction, false, destroyed, unk > 1, None, unk > 0, None, None, PlanetSideGUID(0)) CommonFieldData(faction, bops = false, alternate = destroyed, v1 = unk > 1, None, jammered = unk > 0, None, None, PlanetSideGUID(0))
def apply( def apply(
faction: PlanetSideEmpire.Value, faction: PlanetSideEmpire.Value,
@ -95,7 +95,7 @@ object CommonFieldData extends Marshallable[CommonFieldData] {
unk: Int, unk: Int,
player_guid: PlanetSideGUID player_guid: PlanetSideGUID
): CommonFieldData = ): CommonFieldData =
CommonFieldData(faction, false, destroyed, unk > 1, None, unk % 1 == 1, None, None, player_guid) CommonFieldData(faction, bops = false, alternate = destroyed, v1 = unk > 1, None, jammered = false/*unk % 1 == 1*/, None, None, player_guid)
def apply( def apply(
faction: PlanetSideEmpire.Value, faction: PlanetSideEmpire.Value,
@ -107,7 +107,7 @@ object CommonFieldData extends Marshallable[CommonFieldData] {
): CommonFieldData = { ): CommonFieldData = {
val jammeredField = if (jammered) { Some(0) } val jammeredField = if (jammered) { Some(0) }
else { None } else { None }
CommonFieldData(faction, bops, destroyed, unk > 1, None, unk % 1 == 1, None, jammeredField, player_guid) CommonFieldData(faction, bops, destroyed, unk > 1, None, jammered = false/*unk % 1 == 1*/, None, jammeredField, player_guid)
} }
def codec(extra: Boolean): Codec[CommonFieldData] = def codec(extra: Boolean): Codec[CommonFieldData] =
@ -116,9 +116,9 @@ object CommonFieldData extends Marshallable[CommonFieldData] {
("bops" | bool) :: ("bops" | bool) ::
("alternate" | bool) :: ("alternate" | bool) ::
("v1" | bool) :: //the purpose of this bit changes depending on the previous bit ("v1" | bool) :: //the purpose of this bit changes depending on the previous bit
conditional(extra, "v2" | CommonFieldDataExtra.codec(unk1 = false)) :: ("v2" | conditional(extra, CommonFieldDataExtra.codec(unk1 = false))) ::
("jammered" | bool) :: ("jammered" | bool) ::
optional(bool, "v5" | uint16L) :: ("v5" | optional(bool, uint16L)) ::
("guid" | PlanetSideGUID.codec) ("guid" | PlanetSideGUID.codec)
).xmap[CommonFieldData]( ).xmap[CommonFieldData](
{ {
@ -139,9 +139,9 @@ object CommonFieldData extends Marshallable[CommonFieldData] {
("bops" | bool) :: ("bops" | bool) ::
("alternate" | bool) :: ("alternate" | bool) ::
("v1" | bool) :: //though the code path differs depending on the previous bit, this one gets read one way or another ("v1" | bool) :: //though the code path differs depending on the previous bit, this one gets read one way or another
conditional(extra, codec = "v2" | CommonFieldDataExtra.codec(unk1 = false)) :: ("v2" | conditional(extra, CommonFieldDataExtra.codec(unk1 = false))) ::
("jammered" | bool) :: ("jammered" | bool) ::
optional(bool, "v5" | uint16L) :: ("v5" | optional(bool, uint16L)) ::
("v4" | bool) :: ("v4" | bool) ::
("guid" | PlanetSideGUID.codec) ("guid" | PlanetSideGUID.codec)
).exmap[CommonFieldData]( ).exmap[CommonFieldData](
@ -158,5 +158,5 @@ object CommonFieldData extends Marshallable[CommonFieldData] {
} }
) )
val codec2: Codec[CommonFieldData] = codec2(false) val codec2: Codec[CommonFieldData] = codec2(extra = false)
} }

View file

@ -41,7 +41,7 @@ object DetailedAmmoBoxData extends Marshallable[DetailedAmmoBoxData] {
def apply(unk: Int, mag: Int): DetailedAmmoBoxData = { def apply(unk: Int, mag: Int): DetailedAmmoBoxData = {
DetailedAmmoBoxData( DetailedAmmoBoxData(
CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, unk > 0, None, false, None, None, PlanetSideGUID(0)), CommonFieldData(PlanetSideEmpire.NEUTRAL, bops = false, alternate = false, v1 = unk > 0, None, jammered = false, None, None, PlanetSideGUID(0)),
mag mag
) )
} }

View file

@ -23,7 +23,7 @@ final case class ImplantEntry(implant: ImplantType, initialization: Option[Int],
extends StreamBitSize { extends StreamBitSize {
override def bitsize: Long = { override def bitsize: Long = {
val timerSize = initialization match { val timerSize = initialization match {
case Some(_) => 8L; case Some(_) => 8L
case None => 1L case None => 1L
} }
9L + timerSize 9L + timerSize
@ -271,7 +271,7 @@ object DetailedCharacterData extends Marshallable[DetailedCharacterData] {
private val implant_entry_codec: Codec[ImplantEntry] = ( private val implant_entry_codec: Codec[ImplantEntry] = (
("implant" | uint8L) :: ("implant" | uint8L) ::
(bool >>:~ { guard => (bool >>:~ { guard =>
newcodecs.binary_choice(guard, uint(1), uint8L).hlist newcodecs.binary_choice(guard, uint(bits = 1), uint8L).hlist
}) })
).xmap[ImplantEntry]( ).xmap[ImplantEntry](
{ {
@ -305,7 +305,7 @@ object DetailedCharacterData extends Marshallable[DetailedCharacterData] {
( (
uint8 >>:~ { size => uint8 >>:~ { size =>
conditional(size > 0, dcd_extra1_codec(padFunc(size))) :: conditional(size > 0, dcd_extra1_codec(padFunc(size))) ::
PacketHelpers.listOfNSized(size - 1, dcd_extra1_codec(0)) PacketHelpers.listOfNSized(size - 1, dcd_extra1_codec(pad = 0))
} }
).xmap[List[DCDExtra1]]( ).xmap[List[DCDExtra1]](
{ {
@ -376,7 +376,7 @@ object DetailedCharacterData extends Marshallable[DetailedCharacterData] {
* `Codec` for a `ImprintingProgress` object. * `Codec` for a `ImprintingProgress` object.
*/ */
private val imprint_progress_codec: Codec[ImprintingProgress] = ( private val imprint_progress_codec: Codec[ImprintingProgress] = (
uint(5) :: uint(bits = 5) ::
uint8L uint8L
).as[ImprintingProgress] ).as[ImprintingProgress]
@ -464,7 +464,7 @@ object DetailedCharacterData extends Marshallable[DetailedCharacterData] {
def paddingCalculations(contextOffset: Option[Int], implants: List[ImplantEntry], prevLists: List[List[Any]])( def paddingCalculations(contextOffset: Option[Int], implants: List[ImplantEntry], prevLists: List[List[Any]])(
currListLen: Long currListLen: Long
): Int = { ): Int = {
paddingCalculations(3, contextOffset, implants, prevLists)(currListLen) paddingCalculations(base = 3, contextOffset, implants, prevLists)(currListLen)
} }
/** /**
@ -532,9 +532,9 @@ object DetailedCharacterData extends Marshallable[DetailedCharacterData] {
("stamina" | uint16L) :: ("stamina" | uint16L) ::
conditional(suit == ExoSuitType.MAX, uint32L) :: conditional(suit == ExoSuitType.MAX, uint32L) ::
("unk6" | uint16L) :: ("unk6" | uint16L) ::
("unk7" | uint(3)) :: ("unk7" | uint(bits = 3)) ::
("unk8" | uint32L) :: ("unk8" | uint32L) ::
("unk9" | PacketHelpers.listOfNSized(6, uint16L)) :: //always length of 6 ("unk9" | PacketHelpers.listOfNSized(size = 6, uint16L)) :: //always length of 6
("certs" | listOfN(uint8L, Certification.codec)) ("certs" | listOfN(uint8L, Certification.codec))
).exmap[DetailedCharacterA]( ).exmap[DetailedCharacterA](
{ {
@ -591,7 +591,7 @@ object DetailedCharacterData extends Marshallable[DetailedCharacterData] {
def b_codec(bep: Long, pad_length: Option[Int]): Codec[DetailedCharacterB] = def b_codec(bep: Long, pad_length: Option[Int]): Codec[DetailedCharacterB] =
( (
optional(bool, "unk1" | uint32L) :: ("unk1" | optional(bool, uint32L)) ::
(("implants" | PacketHelpers.listOfNSized( (("implants" | PacketHelpers.listOfNSized(
BattleRank.withExperience(bep).implantSlots, BattleRank.withExperience(bep).implantSlots,
implant_entry_codec implant_entry_codec
@ -607,17 +607,17 @@ object DetailedCharacterData extends Marshallable[DetailedCharacterData] {
("unk6" | uint32L) :: ("unk6" | uint32L) ::
("unk7" | uint32L) :: ("unk7" | uint32L) ::
("unk8" | uint32L) :: ("unk8" | uint32L) ::
(optional(isFalse, "imprinting" | imprint_progress_codec) >>:~ { imprinting => (("imprinting" | optional(isFalse, imprint_progress_codec)) >>:~ { imprinting =>
("unkA" | listOfN(uint16L, uint32L)) :: ("unkA" | listOfN(uint16L, uint32L)) ::
("unkB" | unkBCodec( ("unkB" | unkBCodec(
paddingCalculations( paddingCalculations(
displaceByOptionTest(pad_length, imprinting, 5), displaceByOptionTest(pad_length, imprinting, value = 5),
implants, implants,
List(imprinting.toList, tut, fte, unk3, unk2) List(imprinting.toList, tut, fte, unk3, unk2)
) )
)) :: )) ::
("unkC" | bool) :: ("unkC" | bool) ::
conditional(bep >= BattleRank.BR24.experience, "cosmetics" | Cosmetic.codec) ("cosmetics" | conditional(bep >= BattleRank.BR24.experience, Cosmetic.codec))
}) })
} }
} }

View file

@ -27,7 +27,7 @@ object DetailedConstructionToolData extends Marshallable[DetailedConstructionToo
def apply(data: CommonFieldData): DetailedConstructionToolData = DetailedConstructionToolData(data, 0) def apply(data: CommonFieldData): DetailedConstructionToolData = DetailedConstructionToolData(data, 0)
implicit val codec: Codec[DetailedConstructionToolData] = ( implicit val codec: Codec[DetailedConstructionToolData] = (
("data" | CommonFieldData.codec(false)) :: ("data" | CommonFieldData.codec(extra = false)) ::
uint8 :: //n > 1 produces a stack of construction items (tends to crash the client) uint8 :: //n > 1 produces a stack of construction items (tends to crash the client)
("mode" | uint16) :: ("mode" | uint16) ::
uint2 :: uint2 ::

View file

@ -72,7 +72,7 @@ object DetailedPlayerData extends Marshallable[DetailedPlayerData] {
drawn_slot: DrawnSlot.Value drawn_slot: DrawnSlot.Value
): DetailedPlayerData = { ): DetailedPlayerData = {
val appearance = basic_appearance(5) val appearance = basic_appearance(5)
DetailedPlayerData(None, appearance, character_data(appearance.altModelBit), Some(inventory), drawn_slot)(false) DetailedPlayerData(None, appearance, character_data(appearance.altModelBit), Some(inventory), drawn_slot)(position_defined = false)
} }
/** /**
@ -91,7 +91,7 @@ object DetailedPlayerData extends Marshallable[DetailedPlayerData] {
drawn_slot: DrawnSlot.Value drawn_slot: DrawnSlot.Value
): DetailedPlayerData = { ): DetailedPlayerData = {
val appearance = basic_appearance(5) val appearance = basic_appearance(5)
DetailedPlayerData(None, appearance, character_data(appearance.altModelBit), None, drawn_slot)(false) DetailedPlayerData(None, appearance, character_data(appearance.altModelBit), None, drawn_slot)(position_defined = false)
} }
/** /**
@ -113,7 +113,7 @@ object DetailedPlayerData extends Marshallable[DetailedPlayerData] {
drawn_slot: DrawnSlot.Value drawn_slot: DrawnSlot.Value
): DetailedPlayerData = { ): DetailedPlayerData = {
val appearance = basic_appearance(PlayerData.PaddingOffset(Some(pos))) val appearance = basic_appearance(PlayerData.PaddingOffset(Some(pos)))
DetailedPlayerData(Some(pos), appearance, character_data(appearance.altModelBit), Some(inventory), drawn_slot)(true) DetailedPlayerData(Some(pos), appearance, character_data(appearance.altModelBit), Some(inventory), drawn_slot)(position_defined = true)
} }
/** /**
@ -133,14 +133,14 @@ object DetailedPlayerData extends Marshallable[DetailedPlayerData] {
drawn_slot: DrawnSlot.Value drawn_slot: DrawnSlot.Value
): DetailedPlayerData = { ): DetailedPlayerData = {
val appearance = basic_appearance(PlayerData.PaddingOffset(Some(pos))) val appearance = basic_appearance(PlayerData.PaddingOffset(Some(pos)))
DetailedPlayerData(Some(pos), appearance, character_data(appearance.altModelBit), None, drawn_slot)(true) DetailedPlayerData(Some(pos), appearance, character_data(appearance.altModelBit), None, drawn_slot)(position_defined = true)
} }
def codec(position_defined: Boolean): Codec[DetailedPlayerData] = def codec(position_defined: Boolean): Codec[DetailedPlayerData] =
(conditional(position_defined, "pos" | PlacementData.codec) >>:~ { pos => (conditional(position_defined, "pos" | PlacementData.codec) >>:~ { pos =>
("basic_appearance" | CharacterAppearanceData.codec(PlayerData.PaddingOffset(pos))) >>:~ { app => ("basic_appearance" | CharacterAppearanceData.codec(PlayerData.PaddingOffset(pos))) >>:~ { app =>
("character_data" | DetailedCharacterData.codec(app.a.exosuit, app.altModelBit)) :: ("character_data" | DetailedCharacterData.codec(app.a.exosuit, app.altModelBit)) ::
optional(bool, "inventory" | InventoryData.codec_detailed) :: ("inventory" | optional(bool, InventoryData.codec_detailed)) ::
("drawn_slot" | DrawnSlot.codec) :: ("drawn_slot" | DrawnSlot.codec) ::
bool //usually false bool //usually false
} }
@ -155,5 +155,5 @@ object DetailedPlayerData extends Marshallable[DetailedPlayerData] {
} }
) )
implicit val codec: Codec[DetailedPlayerData] = codec(false) implicit val codec: Codec[DetailedPlayerData] = codec(position_defined = false)
} }

View file

@ -28,7 +28,7 @@ object DetailedREKData extends Marshallable[DetailedREKData] {
uint16L :: uint16L ::
uint4L :: uint4L ::
("unk" | uint8) :: ("unk" | uint8) ::
uint(7) uint(bits = 7)
).exmap[DetailedREKData]( ).exmap[DetailedREKData](
{ {
case data :: 2 :: 0 :: 8 :: unk :: 0 :: HNil => case data :: 2 :: 0 :: 8 :: unk :: 0 :: HNil =>

View file

@ -56,11 +56,11 @@ object DetailedWeaponData extends Marshallable[DetailedWeaponData] {
DetailedWeaponData( DetailedWeaponData(
CommonFieldData( CommonFieldData(
PlanetSideEmpire(unk1 & 3), PlanetSideEmpire(unk1 & 3),
false, bops = false,
false, alternate = false,
(unk2 & 8) == 8, v1 = (unk2 & 8) == 8,
None, None,
(unk2 & 4) == 4, jammered = (unk2 & 4) == 4,
None, None,
None, None,
PlanetSideGUID(0) PlanetSideGUID(0)
@ -92,11 +92,11 @@ object DetailedWeaponData extends Marshallable[DetailedWeaponData] {
DetailedWeaponData( DetailedWeaponData(
CommonFieldData( CommonFieldData(
PlanetSideEmpire(unk1 & 3), PlanetSideEmpire(unk1 & 3),
false, bops = false,
false, alternate = false,
(unk2 & 8) == 8, v1 = (unk2 & 8) == 8,
None, None,
(unk2 & 4) == 4, jammered = (unk2 & 4) == 4,
None, None,
None, None,
PlanetSideGUID(0) PlanetSideGUID(0)
@ -112,7 +112,7 @@ object DetailedWeaponData extends Marshallable[DetailedWeaponData] {
uint8 :: uint8 ::
("fire_mode" | uint8) :: ("fire_mode" | uint8) ::
uint2 :: uint2 ::
optional(bool, "ammo" | InventoryData.codec_detailed) :: ("ammo" | optional(bool, InventoryData.codec_detailed)) ::
("unk" | bool) ("unk" | bool)
).exmap[DetailedWeaponData]( ).exmap[DetailedWeaponData](
{ {

View file

@ -46,9 +46,9 @@ object DroppodData extends Marshallable[DroppodData] {
("basic" | CommonFieldDataWithPlacement.codec) :: ("basic" | CommonFieldDataWithPlacement.codec) ::
bool :: bool ::
("health" | uint8L) :: //health ("health" | uint8L) :: //health
uintL(5) :: //0x0 uintL(bits = 5) :: //0x0
uint4L :: //0xF uint4L :: //0xF
uintL(6) :: //0x0 uintL(bits = 6) :: //0x0
("boosters" | uint4L) :: //0x9 on standby, 0x0 when burning and occupied (basic.player_guid?) ("boosters" | uint4L) :: //0x9 on standby, 0x0 when burning and occupied (basic.player_guid?)
("unk" | bool) ("unk" | bool)
).exmap[DroppodData]( ).exmap[DroppodData](

View file

@ -32,7 +32,7 @@ object HandheldData extends Marshallable[HandheldData] {
implicit val codec: Codec[HandheldData] = ( implicit val codec: Codec[HandheldData] = (
("data" | CommonFieldData.codec) :: ("data" | CommonFieldData.codec) ::
("mode" | uint8) :: ("mode" | uint8) ::
("unk" | uint(3)) ("unk" | uint(bits = 3))
).exmap[HandheldData]( ).exmap[HandheldData](
{ {
case data :: mode :: unk :: HNil => case data :: mode :: unk :: HNil =>

View file

@ -37,7 +37,7 @@ object InternalSlot {
* Used for `0x18` `ObjectCreateDetailedMessage` packets * Used for `0x18` `ObjectCreateDetailedMessage` packets
*/ */
val codec_detailed: Codec[InternalSlot] = ( val codec_detailed: Codec[InternalSlot] = (
("objectClass" | uintL(11)) >>:~ { obj_cls => ("objectClass" | uintL(bits = 11)) >>:~ { obj_cls =>
("guid" | PlanetSideGUID.codec) :: ("guid" | PlanetSideGUID.codec) ::
("parentSlot" | PacketHelpers.encodedStringSize) :: ("parentSlot" | PacketHelpers.encodedStringSize) ::
("obj" | ObjectClass.selectDataDetailedCodec(obj_cls)) //it's fine for this call to fail ("obj" | ObjectClass.selectDataDetailedCodec(obj_cls)) //it's fine for this call to fail
@ -57,7 +57,7 @@ object InternalSlot {
* Used for `0x17` `ObjectCreateMessage` packets * Used for `0x17` `ObjectCreateMessage` packets
*/ */
val codec: Codec[InternalSlot] = ( val codec: Codec[InternalSlot] = (
("objectClass" | uintL(11)) >>:~ { obj_cls => ("objectClass" | uintL(bits = 11)) >>:~ { obj_cls =>
("guid" | PlanetSideGUID.codec) :: ("guid" | PlanetSideGUID.codec) ::
("parentSlot" | PacketHelpers.encodedStringSize) :: ("parentSlot" | PacketHelpers.encodedStringSize) ::
("obj" | ObjectClass.selectDataCodec(obj_cls)) //it's fine for this call to fail ("obj" | ObjectClass.selectDataCodec(obj_cls)) //it's fine for this call to fail

View file

@ -30,10 +30,10 @@ object LargeDeployableData extends Marshallable[LargeDeployableData] {
implicit val codec: Codec[LargeDeployableData] = ( implicit val codec: Codec[LargeDeployableData] = (
("deploy" | CommonFieldDataWithPlacement.codec2) :: ("deploy" | CommonFieldDataWithPlacement.codec2) ::
("health" | uint8L) :: ("health" | uint8L) ::
uintL(7) :: uintL(bits = 7) ::
uint4L :: uint4L ::
uint2L :: uint2L ::
optional(bool, "internals" | InventoryData.codec) ("internals" | optional(bool, InventoryData.codec))
).exmap[LargeDeployableData]( ).exmap[LargeDeployableData](
{ {
case deploy :: health :: 0 :: 0xf :: 0 :: internals :: HNil => case deploy :: health :: 0 :: 0xf :: 0 :: internals :: HNil =>

View file

@ -31,9 +31,11 @@ object LockerContainerData extends Marshallable[LockerContainerData] {
new LockerContainerData(Some(InventoryData(inventory))) new LockerContainerData(Some(InventoryData(inventory)))
implicit val codec: Codec[LockerContainerData] = ( implicit val codec: Codec[LockerContainerData] = (
uint32 :: uint32 :: uint(17) :: uint32 ::
uint32 ::
uint(bits = 17) ::
uint2L :: uint2L ::
uint(21) :: uint(bits = 21) ::
("inventory" | optional(bool, InventoryData.codec)) ("inventory" | optional(bool, InventoryData.codec))
).exmap[LockerContainerData]( ).exmap[LockerContainerData](
{ {

View file

@ -1216,22 +1216,21 @@ object ObjectClass {
case ObjectClass.advanced_ace => DroppedItemData(HandheldData.codec, "advanced ace") case ObjectClass.advanced_ace => DroppedItemData(HandheldData.codec, "advanced ace")
case ObjectClass.router_telepad => DroppedItemData(HandheldData.codec, "router telepad") case ObjectClass.router_telepad => DroppedItemData(HandheldData.codec, "router telepad")
case ObjectClass.boomer_trigger => DroppedItemData(HandheldData.codec, "boomer trigger") case ObjectClass.boomer_trigger => DroppedItemData(HandheldData.codec, "boomer trigger")
case ObjectClass.boomer => ConstructorData(CommonFieldDataWithPlacement.codec2, "ace deployable") case ObjectClass.boomer => ConstructorData(CommonFieldDataWithPlacement.codec, "ace deployable")
case ObjectClass.he_mine => ConstructorData(CommonFieldDataWithPlacement.codec2, "ace deployable") case ObjectClass.he_mine => ConstructorData(CommonFieldDataWithPlacement.codec, "ace deployable")
case ObjectClass.jammer_mine => ConstructorData(CommonFieldDataWithPlacement.codec2, "ace deployable") case ObjectClass.jammer_mine => ConstructorData(CommonFieldDataWithPlacement.codec, "ace deployable")
case ObjectClass.motionalarmsensor => ConstructorData(CommonFieldDataWithPlacement.codec2, "ace deployable") case ObjectClass.motionalarmsensor => ConstructorData(CommonFieldDataWithPlacement.codec, "ace deployable")
case ObjectClass.sensor_shield => ConstructorData(CommonFieldDataWithPlacement.codec2, "ace deployable") case ObjectClass.sensor_shield => ConstructorData(CommonFieldDataWithPlacement.codec, "ace deployable")
case ObjectClass.spitfire_aa => ConstructorData(SmallTurretData.codec, "small turret") case ObjectClass.spitfire_aa => ConstructorData(SmallTurretData.codec, "small turret")
case ObjectClass.spitfire_cloaked => ConstructorData(SmallTurretData.codec, "small turret") case ObjectClass.spitfire_cloaked => ConstructorData(SmallTurretData.codec, "small turret")
case ObjectClass.spitfire_turret => ConstructorData(SmallTurretData.codec, "small turret") case ObjectClass.spitfire_turret => ConstructorData(SmallTurretData.codec, "small turret")
case ObjectClass.tank_traps => ConstructorData(TRAPData.codec, "trap") case ObjectClass.tank_traps => ConstructorData(TRAPData.codec, "trap")
case ObjectClass.deployable_shield_generator => case ObjectClass.deployable_shield_generator => ConstructorData(AegisShieldGeneratorData.codec, "shield generator")
ConstructorData(AegisShieldGeneratorData.codec, "shield generator") case ObjectClass.portable_manned_turret => ConstructorData(OneMannedFieldTurretData.codec, "field turret")
case ObjectClass.portable_manned_turret => ConstructorData(OneMannedFieldTurretData.codec, "field turret") case ObjectClass.portable_manned_turret_nc => ConstructorData(OneMannedFieldTurretData.codec, "field turret")
case ObjectClass.portable_manned_turret_nc => ConstructorData(OneMannedFieldTurretData.codec, "field turret") case ObjectClass.portable_manned_turret_tr => ConstructorData(OneMannedFieldTurretData.codec, "field turret")
case ObjectClass.portable_manned_turret_tr => ConstructorData(OneMannedFieldTurretData.codec, "field turret") case ObjectClass.portable_manned_turret_vs => ConstructorData(OneMannedFieldTurretData.codec, "field turret")
case ObjectClass.portable_manned_turret_vs => ConstructorData(OneMannedFieldTurretData.codec, "field turret") case ObjectClass.router_telepad_deployable => DroppedItemData(TelepadDeployableData.codec, "telepad deployable")
case ObjectClass.router_telepad_deployable => DroppedItemData(TelepadDeployableData.codec, "telepad deployable")
//projectiles //projectiles
case ObjectClass.aphelion_plasma_cloud => ConstructorData(RadiationCloudData.codec, "radiation cloud") case ObjectClass.aphelion_plasma_cloud => ConstructorData(RadiationCloudData.codec, "radiation cloud")
case ObjectClass.aphelion_starfire_projectile => ConstructorData(RemoteProjectileData.codec, "projectile") case ObjectClass.aphelion_starfire_projectile => ConstructorData(RemoteProjectileData.codec, "projectile")

View file

@ -39,7 +39,6 @@ final case class OneMannedFieldTurretData(
} }
object OneMannedFieldTurretData extends Marshallable[OneMannedFieldTurretData] { object OneMannedFieldTurretData extends Marshallable[OneMannedFieldTurretData] {
/** /**
* Overloaded constructor that mandates information about the internal weapon of the field turret. * Overloaded constructor that mandates information about the internal weapon of the field turret.
* @param deploy data common to objects spawned by the (advanced) adaptive construction engine * @param deploy data common to objects spawned by the (advanced) adaptive construction engine
@ -55,10 +54,10 @@ object OneMannedFieldTurretData extends Marshallable[OneMannedFieldTurretData] {
PlanetSideGUID.codec :: //hoist/extract with the deploy.owner_guid in field above PlanetSideGUID.codec :: //hoist/extract with the deploy.owner_guid in field above
bool :: bool ::
("health" | uint8L) :: ("health" | uint8L) ::
uint(5) :: uint(bits = 5) ::
uint4 :: uint4 ::
uint2 :: uint2 ::
optional(bool, "internals" | InventoryData.codec) ("internals" | optional(bool, InventoryData.codec))
).exmap[OneMannedFieldTurretData]( ).exmap[OneMannedFieldTurretData](
{ {
case deploy :: player :: false :: health :: 0 :: 0xf :: 0 :: internals :: HNil => case deploy :: player :: false :: health :: 0 :: 0xf :: 0 :: internals :: HNil =>

View file

@ -58,9 +58,9 @@ object OrbitalShuttleData extends Marshallable[OrbitalShuttleData] {
implicit val codec: Codec[OrbitalShuttleData] = ( implicit val codec: Codec[OrbitalShuttleData] = (
("faction" | PlanetSideEmpire.codec) :: ("faction" | PlanetSideEmpire.codec) ::
uintL(25) :: uintL(bits = 25) ::
uint8L :: //255 uint8L :: //255
uintL(5) :: uintL(bits = 5) ::
uint4L :: //7 uint4L :: //7
uint2L uint2L
).exmap[OrbitalShuttleData]( ).exmap[OrbitalShuttleData](

View file

@ -76,7 +76,7 @@ object PlayerData extends Marshallable[PlayerData] {
): PlayerData = { ): PlayerData = {
val appearance = basic_appearance(5) val appearance = basic_appearance(5)
PlayerData(None, appearance, character_data(appearance.altModelBit.isDefined, true), Some(inventory), drawn_slot)( PlayerData(None, appearance, character_data(appearance.altModelBit.isDefined, true), Some(inventory), drawn_slot)(
false position_defined = false
) )
} }
@ -96,7 +96,7 @@ object PlayerData extends Marshallable[PlayerData] {
drawn_slot: DrawnSlot.Type drawn_slot: DrawnSlot.Type
): PlayerData = { ): PlayerData = {
val appearance = basic_appearance(5) val appearance = basic_appearance(5)
PlayerData(None, appearance, character_data(appearance.altModelBit.isDefined, true), None, drawn_slot)(false) PlayerData(None, appearance, character_data(appearance.altModelBit.isDefined, true), None, drawn_slot)(position_defined = false)
} }
/** /**
@ -124,7 +124,7 @@ object PlayerData extends Marshallable[PlayerData] {
character_data(appearance.altModelBit.isDefined, false), character_data(appearance.altModelBit.isDefined, false),
Some(inventory), Some(inventory),
drawn_slot drawn_slot
)(true) )(position_defined = true)
} }
/** /**
@ -144,7 +144,7 @@ object PlayerData extends Marshallable[PlayerData] {
drawn_slot: DrawnSlot.Type drawn_slot: DrawnSlot.Type
): PlayerData = { ): PlayerData = {
val appearance = basic_appearance(PaddingOffset(Some(pos))) val appearance = basic_appearance(PaddingOffset(Some(pos)))
PlayerData(Some(pos), appearance, character_data(appearance.altModelBit.isDefined, false), None, drawn_slot)(true) PlayerData(Some(pos), appearance, character_data(appearance.altModelBit.isDefined, false), None, drawn_slot)(position_defined = true)
} }
/** /**
@ -205,7 +205,7 @@ object PlayerData extends Marshallable[PlayerData] {
CharacterData.codec(app.b.backpack), CharacterData.codec(app.b.backpack),
CharacterData.codec_seated(app.b.backpack) CharacterData.codec_seated(app.b.backpack)
)) :: )) ::
optional(bool, "inventory" | InventoryData.codec) :: ("inventory" | optional(bool, InventoryData.codec)) ::
("drawn_slot" | DrawnSlot.codec) :: ("drawn_slot" | DrawnSlot.codec) ::
bool //usually false bool //usually false
} }
@ -232,14 +232,14 @@ object PlayerData extends Marshallable[PlayerData] {
( (
("basic_appearance" | CharacterAppearanceData.codec(offset)) >>:~ { app => ("basic_appearance" | CharacterAppearanceData.codec(offset)) >>:~ { app =>
("character_data" | CharacterData.codec_seated(app.b.backpack)) :: ("character_data" | CharacterData.codec_seated(app.b.backpack)) ::
optional(bool, "inventory" | InventoryData.codec) :: ("inventory" | optional(bool, InventoryData.codec)) ::
("drawn_slot" | DrawnSlot.codec) :: ("drawn_slot" | DrawnSlot.codec) ::
bool //usually false bool //usually false
} }
).xmap[PlayerData]( ).xmap[PlayerData](
{ {
case app :: data :: inv :: hand :: _ :: HNil => case app :: data :: inv :: hand :: _ :: HNil =>
PlayerData(None, app, data, inv, hand)(false) PlayerData(None, app, data, inv, hand)(position_defined = false)
}, },
{ {
case PlayerData(_, app, data, inv, hand) => case PlayerData(_, app, data, inv, hand) =>
@ -247,5 +247,5 @@ object PlayerData extends Marshallable[PlayerData] {
} }
) )
implicit val codec: Codec[PlayerData] = codec(false) implicit val codec: Codec[PlayerData] = codec(position_defined = false)
} }

View file

@ -23,7 +23,7 @@ object REKData extends Marshallable[REKData] {
implicit val codec: Codec[REKData] = ( implicit val codec: Codec[REKData] = (
("data" | CommonFieldData.codec2) :: ("data" | CommonFieldData.codec2) ::
("unk1" | uint16) :: ("unk1" | uint16) ::
("unk2" | uint(10)) ("unk2" | uint(bits = 10))
).exmap[REKData]( ).exmap[REKData](
{ {
case data :: u1 :: u2 :: HNil => case data :: u1 :: u2 :: HNil =>

View file

@ -26,19 +26,19 @@ object FlightPhysics extends Enumeration {
type Type = Value type Type = Value
//valid (extremely small distance) (requires non-zero unk4, unk5) //valid (extremely small distance) (requires non-zero unk4, unk5)
val State3 = Value(3) val State3: FlightPhysics.Value = Value(3)
//valid (infinite) (if unk4 == 0 unk5 == 0, minimum distance + time) //valid (infinite) (if unk4 == 0 unk5 == 0, minimum distance + time)
val State4 = Value(4) val State4: FlightPhysics.Value = Value(4)
//valid(infinite) //valid(infinite)
val State5 = Value(5) val State5: FlightPhysics.Value = Value(5)
//valid (uses velocity) (infinite) //valid (uses velocity) (infinite)
val State6 = Value(6) val State6: FlightPhysics.Value = Value(6)
//valid (uses velocity) (infinite) //valid (uses velocity) (infinite)
val State7 = Value(7) val State7: FlightPhysics.Value = Value(7)
//valid (uses velocity) (time > 0 is infinite) (unk5 == 2) //valid (uses velocity) (time > 0 is infinite) (unk5 == 2)
val State15 = Value(15) val State15: FlightPhysics.Value = Value(15)
implicit val codec = PacketHelpers.createEnumerationCodec(this, uint4L) implicit val codec: Codec[FlightPhysics.Value] = PacketHelpers.createEnumerationCodec(this, uint4L)
} }
/** /**

View file

@ -32,7 +32,7 @@ final case class SmallTurretData(
case None => case None =>
0 0
} }
22L + deploySize + internalSize //8u + 7u + 4u + 2u + 1u 23L + deploySize + internalSize //1u + 8u + 7u + 4u + 2u + 1u
} }
} }
@ -49,15 +49,16 @@ object SmallTurretData extends Marshallable[SmallTurretData] {
new SmallTurretData(deploy, health, Some(internals)) new SmallTurretData(deploy, health, Some(internals))
implicit val codec: Codec[SmallTurretData] = ( implicit val codec: Codec[SmallTurretData] = (
("deploy" | CommonFieldDataWithPlacement.codec2) :: ("deploy" | CommonFieldDataWithPlacement.codec) ::
ignore(size = 1) ::
("health" | uint8L) :: ("health" | uint8L) ::
uintL(7) :: uintL(bits = 7) ::
uint4L :: uint4L ::
uint2L :: uint2L ::
optional(bool, "internals" | InventoryData.codec) ("internals" | optional(bool, InventoryData.codec))
).exmap[SmallTurretData]( ).exmap[SmallTurretData](
{ {
case deploy :: health :: 0 :: 0xf :: 0 :: internals :: HNil => case deploy :: _ :: health :: 0 :: 0xf :: 0 :: internals :: HNil =>
val (newHealth, newInternals) = if (health == 0 || internals.isEmpty || internals.get.contents.isEmpty) { val (newHealth, newInternals) = if (health == 0 || internals.isEmpty || internals.get.contents.isEmpty) {
(0, None) (0, None)
} else { } else {
@ -75,7 +76,7 @@ object SmallTurretData extends Marshallable[SmallTurretData] {
} else { } else {
(health, internals) (health, internals)
} }
Attempt.successful(deploy :: newHealth :: 0 :: 0xf :: 0 :: newInternals :: HNil) Attempt.successful(deploy :: () :: newHealth :: 0 :: 0xf :: 0 :: newInternals :: HNil)
} }
) )
} }

View file

@ -14,20 +14,21 @@ import shapeless.{::, HNil}
*/ */
final case class TRAPData(deploy: CommonFieldDataWithPlacement, health: Int) extends ConstructorData { final case class TRAPData(deploy: CommonFieldDataWithPlacement, health: Int) extends ConstructorData {
override def bitsize: Long = { override def bitsize: Long = {
22L + deploy.bitsize //8u + 7u + 4u + 3u 23L + deploy.bitsize //1u + 8u + 7u + 4u + 3u
} }
} }
object TRAPData extends Marshallable[TRAPData] { object TRAPData extends Marshallable[TRAPData] {
implicit val codec: Codec[TRAPData] = ( implicit val codec: Codec[TRAPData] = (
("deploy" | CommonFieldDataWithPlacement.codec2) :: ("deploy" | CommonFieldDataWithPlacement.codec) ::
ignore(size = 1) ::
("health" | uint8L) :: ("health" | uint8L) ::
uint(7) :: uint(bits = 7) ::
uint4L :: uint4L ::
uint(3) uint(bits = 3)
).exmap[TRAPData]( ).exmap[TRAPData](
{ {
case deploy :: health :: 0 :: 15 :: 0 :: HNil => case deploy :: _:: health :: 0 :: 15 :: 0 :: HNil =>
Attempt.successful(TRAPData(deploy, health)) Attempt.successful(TRAPData(deploy, health))
case data => case data =>
@ -35,7 +36,7 @@ object TRAPData extends Marshallable[TRAPData] {
}, },
{ {
case TRAPData(deploy, health) => case TRAPData(deploy, health) =>
Attempt.successful(deploy :: health :: 0 :: 15 :: 0 :: HNil) Attempt.successful(deploy :: () :: health :: 0 :: 15 :: 0 :: HNil)
} }
) )
} }

View file

@ -106,7 +106,7 @@ object VehicleData extends Marshallable[VehicleData] {
cloak: Boolean, cloak: Boolean,
inventory: Option[InventoryData] inventory: Option[InventoryData]
): VehicleData = { ): VehicleData = {
VehicleData(pos, basic, false, health, false, false, driveState, false, false, cloak, None, inventory)( VehicleData(pos, basic, unk3 = false, health, unk4 = false, no_mount_points = false, driveState, unk5 = false, unk6 = false, cloak = cloak, None, inventory)(
VehicleFormat.Normal VehicleFormat.Normal
) )
} }
@ -128,7 +128,7 @@ object VehicleData extends Marshallable[VehicleData] {
format: UtilityVehicleData, format: UtilityVehicleData,
inventory: Option[InventoryData] inventory: Option[InventoryData]
): VehicleData = { ): VehicleData = {
VehicleData(pos, basic, false, health, false, false, driveState, false, false, cloak, Some(format), inventory)( VehicleData(pos, basic, unk3 = false, health, unk4 = false, no_mount_points = false, driveState, unk5 = false, unk6 = false, cloak = cloak, Some(format), inventory)(
VehicleFormat.Utility VehicleFormat.Utility
) )
} }
@ -150,7 +150,7 @@ object VehicleData extends Marshallable[VehicleData] {
format: VariantVehicleData, format: VariantVehicleData,
inventory: Option[InventoryData] inventory: Option[InventoryData]
): VehicleData = { ): VehicleData = {
VehicleData(pos, basic, false, health, false, false, driveState, false, false, cloak, Some(format), inventory)( VehicleData(pos, basic, unk3 = false, health, unk4 = false, no_mount_points = false, driveState, unk5 = false, unk6 = false, cloak = cloak, Some(format), inventory)(
VehicleFormat.Variant VehicleFormat.Variant
) )
} }
@ -227,8 +227,8 @@ object VehicleData extends Marshallable[VehicleData] {
("unk5" | bool) :: //unknown but generally false; can cause stream misalignment if set when unexpectedly ("unk5" | bool) :: //unknown but generally false; can cause stream misalignment if set when unexpectedly
("unk6" | bool) :: ("unk6" | bool) ::
("cloak" | bool) :: //cloak as wraith, phantasm ("cloak" | bool) :: //cloak as wraith, phantasm
conditional(vehicle_type != VehicleFormat.Normal,"vehicle_format_data" | selectFormatReader(vehicle_type)) :: ("vehicle_format_data" | conditional(vehicle_type != VehicleFormat.Normal, selectFormatReader(vehicle_type))) ::
optional(bool, target = "inventory" | MountableInventory.custom_inventory_codec(pos.vel.isDefined, vehicle_type)) ("inventory" | optional(bool, MountableInventory.custom_inventory_codec(pos.vel.isDefined, vehicle_type)))
} }
).exmap[VehicleData]( ).exmap[VehicleData](
{ {

View file

@ -54,8 +54,8 @@ object WeaponData extends Marshallable[WeaponData] {
WeaponData( WeaponData(
CommonFieldData( CommonFieldData(
PlanetSideEmpire(unk1 & 3), PlanetSideEmpire(unk1 & 3),
false, bops = false,
false, alternate = false,
(unk2 & 8) == 8, (unk2 & 8) == 8,
None, None,
(unk2 & 4) == 4, (unk2 & 4) == 4,
@ -91,8 +91,8 @@ object WeaponData extends Marshallable[WeaponData] {
WeaponData( WeaponData(
CommonFieldData( CommonFieldData(
PlanetSideEmpire(unk1 & 3), PlanetSideEmpire(unk1 & 3),
false, bops = false,
false, alternate = false,
(unk2 & 8) == 8, (unk2 & 8) == 8,
None, None,
(unk2 & 4) == 4, (unk2 & 4) == 4,
@ -137,8 +137,8 @@ object WeaponData extends Marshallable[WeaponData] {
WeaponData( WeaponData(
CommonFieldData( CommonFieldData(
PlanetSideEmpire(unk1 & 3), PlanetSideEmpire(unk1 & 3),
false, bops = false,
false, alternate = false,
(unk2 & 8) == 8, (unk2 & 8) == 8,
None, None,
(unk2 & 4) == 4, (unk2 & 4) == 4,

View file

@ -87,17 +87,6 @@ class AvatarService(zone: Zone) extends Actor {
AvatarResponse.EnvironmentalDamage(player_guid, source_guid, amount) 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) => case AvatarAction.Destroy(victim, killer, weapon, pos) =>
AvatarEvents.publish( AvatarEvents.publish(
AvatarServiceResponse(s"/$forChannel/Avatar", victim, AvatarResponse.Destroy(victim, killer, weapon, pos)) 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.Player
import net.psforever.objects.avatar.scoring.KDAStat import net.psforever.objects.avatar.scoring.KDAStat
import net.psforever.objects.ballistics.Projectile import net.psforever.objects.ballistics.Projectile
import net.psforever.objects.ce.Deployable
import net.psforever.objects.equipment.Equipment import net.psforever.objects.equipment.Equipment
import net.psforever.objects.inventory.InventoryItem import net.psforever.objects.inventory.InventoryItem
import net.psforever.objects.serverobject.environment.interaction.common.Watery.OxygenStateTarget 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 ConcealPlayer(player_guid: PlanetSideGUID) extends Action
final case class EnvironmentalDamage(player_guid: PlanetSideGUID, source_guid: PlanetSideGUID, amount: Int) final case class EnvironmentalDamage(player_guid: PlanetSideGUID, source_guid: PlanetSideGUID, amount: Int)
extends Action 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 DeactivateImplantSlot(player_guid: PlanetSideGUID, slot: Int) extends Action
final case class ActivateImplantSlot(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) 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 akka.actor.{Actor, Props}
import net.psforever.objects.serverobject.terminals.Terminal import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.zones.Zone 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.services.local.support.CaptureFlagManager
import net.psforever.types.PlanetSideGUID import net.psforever.types.PlanetSideGUID
import net.psforever.services.local.support._ import net.psforever.services.local.support._
@ -45,6 +45,16 @@ class LocalService(zone: Zone) extends Actor {
case LocalServiceMessage(forChannel, action) => case LocalServiceMessage(forChannel, action) =>
action match { 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) => case LocalAction.DeployableMapIcon(player_guid, behavior, deployInfo) =>
LocalEvents.publish( LocalEvents.publish(
LocalServiceResponse( LocalServiceResponse(

View file

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

View file

@ -0,0 +1,30 @@
// Copyright (c) 2017 PSForever
package game
import org.specs2.mutable._
import net.psforever.packet._
import net.psforever.packet.game._
import net.psforever.types.PlanetSideGUID
import scodec.bits._
class GenericObjectAction2MessageTest extends Specification {
val string = hex"80 38C139212 0"
"decode" in {
PacketCoding.decodePacket(string).require match {
case GenericObjectAction2Message(unk, guid1, guid2) =>
unk mustEqual 1
guid1 mustEqual PlanetSideGUID(2502)
guid2 mustEqual PlanetSideGUID(2505)
case _ =>
ko
}
}
"encode" in {
val msg = GenericObjectAction2Message(1, PlanetSideGUID(2502), PlanetSideGUID(2505))
val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual string
}
}

View file

@ -32,9 +32,9 @@ class SmallTurretDataTest extends Specification {
deploy.v1 mustEqual true deploy.v1 mustEqual true
deploy.v2.isEmpty mustEqual true deploy.v2.isEmpty mustEqual true
deploy.jammered mustEqual false deploy.jammered mustEqual false
deploy.v4.contains(false) mustEqual true deploy.v4.isEmpty mustEqual true
deploy.v5.isEmpty mustEqual true deploy.v5.isEmpty mustEqual true
deploy.guid mustEqual PlanetSideGUID(7742) deploy.guid mustEqual PlanetSideGUID(3871)
health mustEqual 0 health mustEqual 0
case _ => case _ =>
@ -63,9 +63,9 @@ class SmallTurretDataTest extends Specification {
deploy.v1 mustEqual true deploy.v1 mustEqual true
deploy.v2.isEmpty mustEqual true deploy.v2.isEmpty mustEqual true
deploy.jammered mustEqual false deploy.jammered mustEqual false
deploy.v4.contains(true) mustEqual true deploy.v4.isEmpty mustEqual true
deploy.v5.isEmpty mustEqual true deploy.v5.isEmpty mustEqual true
deploy.guid mustEqual PlanetSideGUID(8208) deploy.guid mustEqual PlanetSideGUID(4232)
health mustEqual 255 health mustEqual 255
@ -124,7 +124,7 @@ class SmallTurretDataTest extends Specification {
val obj = SmallTurretData( val obj = SmallTurretData(
CommonFieldDataWithPlacement( CommonFieldDataWithPlacement(
PlacementData(Vector3(4577.7812f, 5624.828f, 72.046875f), Vector3(0, 2.8125f, 264.375f)), PlacementData(Vector3(4577.7812f, 5624.828f, 72.046875f), Vector3(0, 2.8125f, 264.375f)),
CommonFieldData(PlanetSideEmpire.NC, false, true, true, None, false, Some(false), None, PlanetSideGUID(7742)) CommonFieldData(PlanetSideEmpire.NC, bops = false, alternate = true, v1 = true, None, jammered = false, None, None, PlanetSideGUID(3871))
), ),
0 0
) )
@ -137,7 +137,7 @@ class SmallTurretDataTest extends Specification {
val obj = SmallTurretData( val obj = SmallTurretData(
CommonFieldDataWithPlacement( CommonFieldDataWithPlacement(
PlacementData(Vector3(4527.633f, 6271.3594f, 70.265625f), Vector3(0, 0, 154.6875f)), PlacementData(Vector3(4527.633f, 6271.3594f, 70.265625f), Vector3(0, 0, 154.6875f)),
CommonFieldData(PlanetSideEmpire.VS, false, false, true, None, false, Some(true), None, PlanetSideGUID(8208)) CommonFieldData(PlanetSideEmpire.VS, bops = false, alternate = false, v1 = true, None, jammered = false, None, None, PlanetSideGUID(4232))
), ),
255, 255,
InventoryData( InventoryData(
@ -149,11 +149,11 @@ class SmallTurretDataTest extends Specification {
WeaponData( WeaponData(
CommonFieldData( CommonFieldData(
PlanetSideEmpire.NEUTRAL, PlanetSideEmpire.NEUTRAL,
false, bops = false,
false, alternate = false,
true, v1 = true,
None, None,
false, jammered = false,
None, None,
None, None,
PlanetSideGUID(0) PlanetSideGUID(0)
@ -166,11 +166,11 @@ class SmallTurretDataTest extends Specification {
0, 0,
CommonFieldData( CommonFieldData(
PlanetSideEmpire.NEUTRAL, PlanetSideEmpire.NEUTRAL,
false, bops = false,
false, alternate = false,
true, v1 = true,
None, None,
false, jammered = false,
Some(false), Some(false),
None, None,
PlanetSideGUID(0) PlanetSideGUID(0)

View file

@ -29,9 +29,9 @@ class TRAPDataTest extends Specification {
deploy.v1 mustEqual true deploy.v1 mustEqual true
deploy.v2.isEmpty mustEqual true deploy.v2.isEmpty mustEqual true
deploy.jammered mustEqual false deploy.jammered mustEqual false
deploy.v4.contains(true) mustEqual true deploy.v4.isEmpty mustEqual true
deploy.v5.isEmpty mustEqual true deploy.v5.isEmpty mustEqual true
deploy.guid mustEqual PlanetSideGUID(4748) deploy.guid mustEqual PlanetSideGUID(2502)
health mustEqual 255 health mustEqual 255
case _ => case _ =>
ko ko
@ -45,7 +45,7 @@ class TRAPDataTest extends Specification {
val obj = TRAPData( val obj = TRAPData(
CommonFieldDataWithPlacement( CommonFieldDataWithPlacement(
PlacementData(Vector3(3572.4453f, 3277.9766f, 114.0f), Vector3.z(90)), PlacementData(Vector3(3572.4453f, 3277.9766f, 114.0f), Vector3.z(90)),
CommonFieldData(PlanetSideEmpire.VS, false, false, true, None, false, Some(true), None, PlanetSideGUID(4748)) CommonFieldData(PlanetSideEmpire.VS, bops = false, alternate = false, v1 = true, None, jammered = false, None, None, PlanetSideGUID(2502))
), ),
255 255
) )

View file

@ -57,7 +57,7 @@ class DeployableBehaviorSetupTest extends ActorTest {
case _ => assert(false, "self-setup test - no spawn fx") case _ => assert(false, "self-setup test - no spawn fx")
} }
eventsMsgs(1) match { 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") assert(obj eq jmine, "self-setup test - not same mine")
case _ => case _ =>
assert( false, "self-setup test - wrong deploy message") 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") case _ => assert(false, "owned setup test, 2 - no spawn fx")
} }
eventsMsgs(3) match { 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") assert(obj eq jmine, "owned setup test, 2 - not same mine")
case _ => case _ =>
assert( false, "owned setup test, 2 - wrong deploy message") 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) val eventsMsgs = eventsProbe.receiveN(4, 10.seconds)
eventsMsgs.head match { 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") assert(obj eq telepad, "no-router telepad deployable testt - not same telepad")
case _ => case _ =>
assert( false, "no-router telepad deployable test - wrong deploy message") 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) val eventsMsgs = eventsProbe.receiveN(4, 10.seconds)
eventsMsgs.head match { 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") assert(obj eq telepad, "no-activate telepad deployable testt - not same telepad")
case _ => case _ =>
assert( false, "no-activate telepad deployable test - wrong deploy message") 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 eventsMsgs = eventsProbe.receiveN(2, 10.seconds)
val routerMsgs = routerProbe.receiveN(1, 10.seconds) val routerMsgs = routerProbe.receiveN(1, 10.seconds)
eventsMsgs.head match { 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") assert(obj eq telepad, "link attempt telepad deployable testt - not same telepad")
case _ => case _ =>
assert( false, "link attempt telepad deployable test - wrong deploy message") 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) val eventsMsgs = eventsProbe.receiveN(9, 10.seconds)
eventsMsgs.head match { 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") assert(obj eq telepad, "link to router test - not same telepad")
case _ => case _ =>
assert( false, "link to router test - wrong deploy message") 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 { class DeployableMapIconTest extends ActorTest {
ServiceManager.boot(system) ServiceManager.boot(system)