diff --git a/common/src/main/scala/net/psforever/packet/game/PlayerStateMessageUpstream.scala b/common/src/main/scala/net/psforever/packet/game/PlayerStateMessageUpstream.scala index 3895dce2f..1f68dca9a 100644 --- a/common/src/main/scala/net/psforever/packet/game/PlayerStateMessageUpstream.scala +++ b/common/src/main/scala/net/psforever/packet/game/PlayerStateMessageUpstream.scala @@ -20,7 +20,8 @@ import scodec.codecs._ * @param facingYawUpper a "yaw" angle that represents the angle of the avatar's upper body with respect to its forward-facing direction; * this number is normally 0 for forward facing; * the range is limited between approximately 61 degrees of center turned to left or right - * @param seq_time na + * @param seq_time the "time frame" according to the server; + * starts at 0; max value is 1023 before resetting * @param unk1 na * @param is_crouching avatar is crouching * @param is_jumping avatar is jumping; @@ -45,6 +46,8 @@ final case class PlayerStateMessageUpstream(avatar_guid : PlanetSideGUID, unk2 : Int, unk3 : Int) extends PlanetSideGamePacket { + assert(seq_time > -1 && seq_time < 1024) + type Packet = PlayerStateMessageUpstream def opcode = GamePacketOpcode.PlayerStateMessageUpstream def encode = PlayerStateMessageUpstream.encode(this) diff --git a/common/src/main/scala/net/psforever/packet/game/VehicleStateMessage.scala b/common/src/main/scala/net/psforever/packet/game/VehicleStateMessage.scala index c7a26b3b8..0263d3896 100644 --- a/common/src/main/scala/net/psforever/packet/game/VehicleStateMessage.scala +++ b/common/src/main/scala/net/psforever/packet/game/VehicleStateMessage.scala @@ -26,7 +26,7 @@ import scodec.codecs._ * values in between are possible; * vehicles that hover also influence this field as expected * @param unk5 na - Possibly a flag to indicate the vehicle is attached to something else e.g. is in a galaxy/lodestar cargo bay - * @param is_cloaked vehicle is cloaked + * @param is_cloaked vehicle is cloaked by virtue of being a Wraith or a Phantasm * @see `PlacementData` */ diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/Cosmetics.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/Cosmetics.scala index cd0ece4a5..29b6ecd54 100644 --- a/common/src/main/scala/net/psforever/packet/game/objectcreate/Cosmetics.scala +++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/Cosmetics.scala @@ -114,6 +114,7 @@ object Cosmetics { sunglasses : Boolean, earpiece : Boolean, brimmed_cap : Boolean) : Cosmetics = { + import scala.language.implicitConversions implicit def bool2int(b : Boolean) : Int = if(b) 1 else 0 Cosmetics( (no_helmet * 16) + diff --git a/common/src/main/scala/services/avatar/AvatarAction.scala b/common/src/main/scala/services/avatar/AvatarAction.scala deleted file mode 100644 index c9042cbe5..000000000 --- a/common/src/main/scala/services/avatar/AvatarAction.scala +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) 2017 PSForever -package services.avatar - -import net.psforever.objects.ballistics.SourceEntry -import net.psforever.objects.ce.Deployable -import net.psforever.objects.{PlanetSideGameObject, Player} -import net.psforever.objects.equipment.Equipment -import net.psforever.objects.inventory.Container -import net.psforever.objects.zones.Zone -import net.psforever.packet.PlanetSideGamePacket -import net.psforever.packet.game.{PlanetSideGUID, PlayerStateMessageUpstream} -import net.psforever.packet.game.objectcreate.{ConstructorData, ObjectCreateMessageParent} -import net.psforever.types.{ExoSuitType, PlanetSideEmpire, Vector3} - -import scala.concurrent.duration.FiniteDuration - -object AvatarAction { - trait Action - - final case class ArmorChanged(player_guid : PlanetSideGUID, suit : ExoSuitType.Value, subtype : Int) extends Action - final case class ChangeAmmo(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID, weapon_slot : Int, old_ammo_guid : PlanetSideGUID, ammo_id : Int, ammo_guid : PlanetSideGUID, ammo_data : ConstructorData) extends Action - final case class ChangeFireMode(player_guid : PlanetSideGUID, item_guid : PlanetSideGUID, mode : Int) extends Action - final case class ChangeFireState_Start(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action - final case class ChangeFireState_Stop(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action - final case class ConcealPlayer(player_guid : PlanetSideGUID) extends Action - final case class EnvironmentalDamage(player_guid : PlanetSideGUID, amont: Int) extends Action - final case class Damage(player_guid : PlanetSideGUID, target : Player, resolution_function : (Any)=>Unit) extends Action - final case class DeployItem(player_guid : PlanetSideGUID, item : PlanetSideGameObject with Deployable) extends Action - final case class Destroy(victim : PlanetSideGUID, killer : PlanetSideGUID, weapon : PlanetSideGUID, pos : Vector3) extends Action - final case class DestroyDisplay(killer : SourceEntry, victim : SourceEntry, method : Int, unk : Int = 121) extends Action - final case class DropItem(player_guid : PlanetSideGUID, item : Equipment, zone : Zone) extends Action - final case class EquipmentInHand(player_guid : PlanetSideGUID, target_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Action - final case class HitHint(source_guid : PlanetSideGUID, player_guid : PlanetSideGUID) extends Action - final case class KilledWhileInVehicle(player_guid : PlanetSideGUID) extends Action - final case class LoadPlayer(player_guid : PlanetSideGUID, object_id : Int, target_guid : PlanetSideGUID, cdata : ConstructorData, pdata : Option[ObjectCreateMessageParent]) extends Action - final case class ObjectDelete(player_guid : PlanetSideGUID, item_guid : PlanetSideGUID, unk : Int = 0) extends Action - final case class ObjectHeld(player_guid : PlanetSideGUID, slot : Int) extends Action - final case class PlanetsideAttribute(player_guid : PlanetSideGUID, attribute_type : Int, attribute_value : Long) extends Action - final case class PlanetsideAttributeSelf(player_guid : PlanetSideGUID, attribute_type : Int, attribute_value : Long) extends Action - final case class PlayerState(player_guid : PlanetSideGUID, msg : PlayerStateMessageUpstream, spectator : Boolean, weaponInHand : Boolean) extends Action - final case class PickupItem(player_guid : PlanetSideGUID, zone : Zone, target : PlanetSideGameObject with Container, slot : Int, item : Equipment, unk : Int = 0) extends Action - final case class PutDownFDU(player_guid : PlanetSideGUID) extends Action - final case class Release(player : Player, zone : Zone, time : Option[FiniteDuration] = None) extends Action - final case class Reload(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action - final case class SetEmpire(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, faction : PlanetSideEmpire.Value) extends Action - final case class StowEquipment(player_guid : PlanetSideGUID, target_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Action - final case class WeaponDryFire(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action - - final case class SendResponse(player_guid: PlanetSideGUID, msg: PlanetSideGamePacket) extends Action - - // final case class PlayerStateShift(killer : PlanetSideGUID, victim : PlanetSideGUID) extends Action -// final case class DestroyDisplay(killer : PlanetSideGUID, victim : PlanetSideGUID) extends Action -} diff --git a/common/src/main/scala/services/avatar/AvatarResponse.scala b/common/src/main/scala/services/avatar/AvatarResponse.scala deleted file mode 100644 index f7f1a3b32..000000000 --- a/common/src/main/scala/services/avatar/AvatarResponse.scala +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) 2017 PSForever -package services.avatar - -import net.psforever.objects.Player -import net.psforever.objects.ballistics.SourceEntry -import net.psforever.objects.equipment.Equipment -import net.psforever.packet.PlanetSideGamePacket -import net.psforever.packet.game._ -import net.psforever.packet.game.objectcreate.ConstructorData -import net.psforever.types.{ExoSuitType, PlanetSideEmpire, Vector3} - -object AvatarResponse { - trait Response - - final case class ArmorChanged(suit : ExoSuitType.Value, subtype : Int) extends Response - final case class ChangeAmmo(weapon_guid : PlanetSideGUID, weapon_slot : Int, old_ammo_guid : PlanetSideGUID, ammo_id : Int, ammo_guid : PlanetSideGUID, ammo_data : ConstructorData) extends Response - final case class ChangeFireMode(item_guid : PlanetSideGUID, mode : Int) extends Response - final case class ChangeFireState_Start(weapon_guid : PlanetSideGUID) extends Response - final case class ChangeFireState_Stop(weapon_guid : PlanetSideGUID) extends Response - final case class ConcealPlayer() extends Response - final case class EnvironmentalDamage(target : PlanetSideGUID, amount : Int) extends Response - final case class DamageResolution(target : Player, resolution_function : (Any)=>Unit) extends Response - final case class Destroy(victim : PlanetSideGUID, killer : PlanetSideGUID, weapon : PlanetSideGUID, pos : Vector3) extends Response - final case class DestroyDisplay(killer : SourceEntry, victim : SourceEntry, method : Int, unk : Int) extends Response - final case class DropItem(pkt : ObjectCreateMessage) extends Response - final case class EquipmentInHand(pkt : ObjectCreateMessage) extends Response - final case class HitHint(source_guid : PlanetSideGUID) extends Response - final case class KilledWhileInVehicle() extends Response - final case class LoadPlayer(pkt : ObjectCreateMessage) extends Response - final case class ObjectDelete(item_guid : PlanetSideGUID, unk : Int) extends Response - final case class ObjectHeld(slot : Int) extends Response - final case class PlanetsideAttribute(attribute_type : Int, attribute_value : Long) extends Response - final case class PlanetsideAttributeSelf(attribute_type : Int, attribute_value : Long) extends Response - final case class PlayerState(msg : PlayerStateMessageUpstream, spectator : Boolean, weaponInHand : Boolean) extends Response - final case class PutDownFDU(target_guid : PlanetSideGUID) extends Response - final case class Release(player : Player) extends Response - final case class Reload(weapon_guid : PlanetSideGUID) extends Response - final case class SetEmpire(object_guid : PlanetSideGUID, faction : PlanetSideEmpire.Value) extends Response - final case class StowEquipment(target_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Response - final case class WeaponDryFire(weapon_guid : PlanetSideGUID) extends Response - - final case class SendResponse(msg: PlanetSideGamePacket) extends Response - // final case class PlayerStateShift(itemID : PlanetSideGUID) extends Response -} diff --git a/common/src/main/scala/services/avatar/AvatarService.scala b/common/src/main/scala/services/avatar/AvatarService.scala index 18f5c7c02..0e5bf3e0f 100644 --- a/common/src/main/scala/services/avatar/AvatarService.scala +++ b/common/src/main/scala/services/avatar/AvatarService.scala @@ -143,9 +143,9 @@ class AvatarService extends Actor { AvatarEvents.publish( AvatarServiceResponse(s"/$forChannel/Avatar", guid, AvatarResponse.PlanetsideAttributeSelf(attribute_type, attribute_value)) ) - case AvatarAction.PlayerState(guid, msg, spectator, weapon) => + case AvatarAction.PlayerState(guid, pos, vel, yaw, pitch, yaw_upper, seq_time, is_crouching, is_jumping, jump_thrust, is_cloaking, spectating, weaponInHand) => AvatarEvents.publish( - AvatarServiceResponse(s"/$forChannel/Avatar", guid, AvatarResponse.PlayerState(msg, spectator, weapon)) + AvatarServiceResponse(s"/$forChannel/Avatar", guid, AvatarResponse.PlayerState(pos, vel, yaw, pitch, yaw_upper, seq_time, is_crouching, is_jumping, jump_thrust, is_cloaking, spectating, weaponInHand)) ) case AvatarAction.PickupItem(player_guid, zone, target, slot, item, unk) => janitor forward RemoverActor.ClearSpecific(List(item), zone) diff --git a/common/src/main/scala/services/avatar/AvatarServiceMessage.scala b/common/src/main/scala/services/avatar/AvatarServiceMessage.scala index a600c0c37..3c89ab069 100644 --- a/common/src/main/scala/services/avatar/AvatarServiceMessage.scala +++ b/common/src/main/scala/services/avatar/AvatarServiceMessage.scala @@ -1,9 +1,60 @@ // Copyright (c) 2017 PSForever package services.avatar +import net.psforever.objects.{PlanetSideGameObject, Player} +import net.psforever.objects.ballistics.SourceEntry +import net.psforever.objects.ce.Deployable +import net.psforever.objects.equipment.Equipment +import net.psforever.objects.inventory.Container +import net.psforever.objects.zones.Zone +import net.psforever.packet.PlanetSideGamePacket +import net.psforever.packet.game.PlanetSideGUID +import net.psforever.packet.game.objectcreate.{ConstructorData, ObjectCreateMessageParent} +import net.psforever.types.{ExoSuitType, PlanetSideEmpire, Vector3} + +import scala.concurrent.duration.FiniteDuration + final case class AvatarServiceMessage(forChannel : String, actionMessage : AvatarAction.Action) object AvatarServiceMessage { final case class Corpse(msg : Any) final case class Ground(msg : Any) } + +object AvatarAction { + sealed trait Action + + final case class ArmorChanged(player_guid : PlanetSideGUID, suit : ExoSuitType.Value, subtype : Int) extends Action + final case class ChangeAmmo(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID, weapon_slot : Int, old_ammo_guid : PlanetSideGUID, ammo_id : Int, ammo_guid : PlanetSideGUID, ammo_data : ConstructorData) extends Action + final case class ChangeFireMode(player_guid : PlanetSideGUID, item_guid : PlanetSideGUID, mode : Int) extends Action + final case class ChangeFireState_Start(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action + final case class ChangeFireState_Stop(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action + final case class ConcealPlayer(player_guid : PlanetSideGUID) extends Action + final case class EnvironmentalDamage(player_guid : PlanetSideGUID, amont: Int) extends Action + final case class Damage(player_guid : PlanetSideGUID, target : Player, resolution_function : Any=>Unit) extends Action + final case class DeployItem(player_guid : PlanetSideGUID, item : PlanetSideGameObject with Deployable) extends Action + final case class Destroy(victim : PlanetSideGUID, killer : PlanetSideGUID, weapon : PlanetSideGUID, pos : Vector3) extends Action + final case class DestroyDisplay(killer : SourceEntry, victim : SourceEntry, method : Int, unk : Int = 121) extends Action + final case class DropItem(player_guid : PlanetSideGUID, item : Equipment, zone : Zone) extends Action + final case class EquipmentInHand(player_guid : PlanetSideGUID, target_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Action + final case class HitHint(source_guid : PlanetSideGUID, player_guid : PlanetSideGUID) extends Action + final case class KilledWhileInVehicle(player_guid : PlanetSideGUID) extends Action + final case class LoadPlayer(player_guid : PlanetSideGUID, object_id : Int, target_guid : PlanetSideGUID, cdata : ConstructorData, pdata : Option[ObjectCreateMessageParent]) extends Action + final case class ObjectDelete(player_guid : PlanetSideGUID, item_guid : PlanetSideGUID, unk : Int = 0) extends Action + final case class ObjectHeld(player_guid : PlanetSideGUID, slot : Int) extends Action + final case class PlanetsideAttribute(player_guid : PlanetSideGUID, attribute_type : Int, attribute_value : Long) extends Action + final case class PlanetsideAttributeSelf(player_guid : PlanetSideGUID, attribute_type : Int, attribute_value : Long) extends Action + final case class PlayerState(player_guid : PlanetSideGUID, pos : Vector3, vel : Option[Vector3], facingYaw : Float, facingPitch : Float, facingYawUpper : Float, timestamp : Int, is_crouching : Boolean, is_jumping : Boolean, jump_thrust : Boolean, is_cloaked : Boolean, spectator : Boolean, weaponInHand : Boolean) extends Action + final case class PickupItem(player_guid : PlanetSideGUID, zone : Zone, target : PlanetSideGameObject with Container, slot : Int, item : Equipment, unk : Int = 0) extends Action + final case class PutDownFDU(player_guid : PlanetSideGUID) extends Action + final case class Release(player : Player, zone : Zone, time : Option[FiniteDuration] = None) extends Action + final case class Reload(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action + final case class SetEmpire(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, faction : PlanetSideEmpire.Value) extends Action + final case class StowEquipment(player_guid : PlanetSideGUID, target_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Action + final case class WeaponDryFire(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action + + final case class SendResponse(player_guid: PlanetSideGUID, msg: PlanetSideGamePacket) extends Action + + // final case class PlayerStateShift(killer : PlanetSideGUID, victim : PlanetSideGUID) extends Action + // final case class DestroyDisplay(killer : PlanetSideGUID, victim : PlanetSideGUID) extends Action +} diff --git a/common/src/main/scala/services/avatar/AvatarServiceResponse.scala b/common/src/main/scala/services/avatar/AvatarServiceResponse.scala index 0d31425eb..5ce1c402c 100644 --- a/common/src/main/scala/services/avatar/AvatarServiceResponse.scala +++ b/common/src/main/scala/services/avatar/AvatarServiceResponse.scala @@ -1,10 +1,50 @@ // Copyright (c) 2017 PSForever package services.avatar -import net.psforever.packet.game.PlanetSideGUID +import net.psforever.objects.Player +import net.psforever.objects.ballistics.SourceEntry +import net.psforever.objects.equipment.Equipment +import net.psforever.packet.PlanetSideGamePacket +import net.psforever.packet.game.objectcreate.ConstructorData +import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID} +import net.psforever.types.{ExoSuitType, PlanetSideEmpire, Vector3} import services.GenericEventBusMsg final case class AvatarServiceResponse(toChannel : String, avatar_guid : PlanetSideGUID, replyMessage : AvatarResponse.Response ) extends GenericEventBusMsg + +object AvatarResponse { + sealed trait Response + + final case class ArmorChanged(suit : ExoSuitType.Value, subtype : Int) extends Response + final case class ChangeAmmo(weapon_guid : PlanetSideGUID, weapon_slot : Int, old_ammo_guid : PlanetSideGUID, ammo_id : Int, ammo_guid : PlanetSideGUID, ammo_data : ConstructorData) extends Response + final case class ChangeFireMode(item_guid : PlanetSideGUID, mode : Int) extends Response + final case class ChangeFireState_Start(weapon_guid : PlanetSideGUID) extends Response + final case class ChangeFireState_Stop(weapon_guid : PlanetSideGUID) extends Response + final case class ConcealPlayer() extends Response + final case class EnvironmentalDamage(target : PlanetSideGUID, amount : Int) extends Response + final case class DamageResolution(target : Player, resolution_function : Any=>Unit) extends Response + final case class Destroy(victim : PlanetSideGUID, killer : PlanetSideGUID, weapon : PlanetSideGUID, pos : Vector3) extends Response + final case class DestroyDisplay(killer : SourceEntry, victim : SourceEntry, method : Int, unk : Int) extends Response + final case class DropItem(pkt : ObjectCreateMessage) extends Response + final case class EquipmentInHand(pkt : ObjectCreateMessage) extends Response + final case class HitHint(source_guid : PlanetSideGUID) extends Response + final case class KilledWhileInVehicle() extends Response + final case class LoadPlayer(pkt : ObjectCreateMessage) extends Response + final case class ObjectDelete(item_guid : PlanetSideGUID, unk : Int) extends Response + final case class ObjectHeld(slot : Int) extends Response + final case class PlanetsideAttribute(attribute_type : Int, attribute_value : Long) extends Response + final case class PlanetsideAttributeSelf(attribute_type : Int, attribute_value : Long) extends Response + final case class PlayerState(pos : Vector3, vel : Option[Vector3], facingYaw : Float, facingPitch : Float, facingYawUpper : Float, timestamp : Int, is_crouching : Boolean, is_jumping : Boolean, jump_thrust : Boolean, is_cloaked : Boolean, spectator : Boolean, weaponInHand : Boolean) extends Response + final case class PutDownFDU(target_guid : PlanetSideGUID) extends Response + final case class Release(player : Player) extends Response + final case class Reload(weapon_guid : PlanetSideGUID) extends Response + final case class SetEmpire(object_guid : PlanetSideGUID, faction : PlanetSideEmpire.Value) extends Response + final case class StowEquipment(target_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Response + final case class WeaponDryFire(weapon_guid : PlanetSideGUID) extends Response + + final case class SendResponse(msg: PlanetSideGamePacket) extends Response + // final case class PlayerStateShift(itemID : PlanetSideGUID) extends Response +} diff --git a/common/src/test/scala/service/AvatarServiceTest.scala b/common/src/test/scala/service/AvatarServiceTest.scala index 64e6ebd28..d7bf7da42 100644 --- a/common/src/test/scala/service/AvatarServiceTest.scala +++ b/common/src/test/scala/service/AvatarServiceTest.scala @@ -266,8 +266,8 @@ class PlayerStateTest extends ActorTest { ServiceManager.boot(system) val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName) service ! Service.Join("test") - service ! AvatarServiceMessage("test", AvatarAction.PlayerState(PlanetSideGUID(10), msg, false, false)) - expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.PlayerState(msg, false, false))) + service ! AvatarServiceMessage("test", AvatarAction.PlayerState(PlanetSideGUID(10), Vector3(3694.1094f, 2735.4531f, 90.84375f), Some(Vector3(4.375f, 2.59375f, 0.0f)), 61.875f, 351.5625f, 0.0f, 136, false, false, false, false, false, false)) + expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.PlayerState(Vector3(3694.1094f, 2735.4531f, 90.84375f), Some(Vector3(4.375f, 2.59375f, 0.0f)), 61.875f, 351.5625f, 0.0f, 136, false, false, false, false, false, false))) } } } diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index b4237a447..4ef67f99e 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -90,6 +90,7 @@ class WorldSessionActor extends Actor with MDCContextAware { var progressBarValue : Option[Float] = None var shooting : Option[PlanetSideGUID] = None //ChangeFireStateMessage_Start var prefire : Option[PlanetSideGUID] = None //if WeaponFireMessage precedes ChangeFireStateMessage_Start + var shotsWhileDead : Int = 0 var accessedContainer : Option[PlanetSideGameObject with Container] = None var flying : Boolean = false var speed : Float = 1.0f @@ -149,6 +150,8 @@ class WorldSessionActor extends Actor with MDCContextAware { var timeDL : Long = 0 var timeSurge : Long = 0 + lazy val unsignedIntMaxValue : Long = Int.MaxValue.toLong * 2L + 1L + var serverTime : Long = 0 var amsSpawnPoints : List[SpawnPoint] = Nil var clientKeepAlive : Cancellable = DefaultCancellable.obj @@ -166,6 +169,7 @@ class WorldSessionActor extends Actor with MDCContextAware { * @param b `true` or `false` (or `null`) * @return 1 for `true`; 0 for `false` */ + import scala.language.implicitConversions implicit def boolToInt(b : Boolean) : Int = if(b) 1 else 0 override def postStop() : Unit = { @@ -1406,7 +1410,7 @@ class WorldSessionActor extends Actor with MDCContextAware { sendResponse(PlanetsideAttributeMessage(guid, attribute_type, attribute_value)) } - case AvatarResponse.PlayerState(msg, spectating, weaponInHand) => + case AvatarResponse.PlayerState(pos, vel, yaw, pitch, yaw_upper, seq_time, is_crouching, is_jumping, jump_thrust, is_cloaking, spectating, weaponInHand) => if(tplayer_guid != guid) { val now = System.currentTimeMillis() val (location, time, distanceSq) : (Vector3, Long, Float) = if(spectating) { @@ -1414,30 +1418,30 @@ class WorldSessionActor extends Actor with MDCContextAware { } else { val before = player.lastSeenStreamMessage(guid.guid) - val dist = Vector3.DistanceSquared(player.Position, msg.pos) - (msg.pos, now - before, dist) + val dist = Vector3.DistanceSquared(player.Position, pos) + (pos, now - before, dist) } if(spectating || ((distanceSq < 900 || weaponInHand) && time > 200) || (distanceSq < 10000 && time > 500) || (distanceSq < 160000 && ( - (msg.is_jumping || time < 200)) || - ((msg.vel.isEmpty || Vector3.MagnitudeSquared(msg.vel.get).toInt == 0) && time > 2000) || + (is_jumping || time < 200)) || + ((vel.isEmpty || Vector3.MagnitudeSquared(vel.get).toInt == 0) && time > 2000) || (time > 1000)) || (distanceSq > 160000 && time > 5000)) { sendResponse( PlayerStateMessage( guid, location, - msg.vel, - msg.facingYaw, - msg.facingPitch, - msg.facingYawUpper, + vel, + yaw, + pitch, + yaw_upper, timestamp = 0, - msg.is_crouching, - msg.is_jumping, - msg.jump_thrust, - msg.is_cloaked + is_crouching, + is_jumping, + jump_thrust, + is_cloaking ) ) player.lastSeenStreamMessage(guid.guid) = now @@ -3388,8 +3392,9 @@ class WorldSessionActor extends Actor with MDCContextAware { pkt match { case sync @ ControlSync(diff, _, _, _, _, _, fa, fb) => log.trace(s"SYNC: $sync") - val serverTick = Math.abs(System.nanoTime().toInt) // limit the size to prevent encoding error - sendResponse(ControlSyncResp(diff, serverTick, fa, fb, fb, fa)) + val nextDiff = if(diff == 65535) { 0 } else { diff + 1 } + val serverTick = ServerTick + sendResponse(ControlSyncResp(nextDiff, serverTick, fa, fb, fb, fa)) case TeardownConnection(_) => log.info("Good bye") @@ -3399,6 +3404,19 @@ class WorldSessionActor extends Actor with MDCContextAware { } } + /** + * Return a measure of server time as an unsigned 32-bit integer. + * The server time started at 0 back at the beginning (POSIX time). + * The server time will loop around to 0 again to maintain datatype integrity. + * @see `Int.MaxValue` + * @see `System.nanoTime` + * @return a number that indicates server tick time + */ + def ServerTick : Long = { + serverTime = System.currentTimeMillis() & unsignedIntMaxValue + serverTime + } + def handleGamePkt(pkt : PlanetSideGamePacket) = pkt match { case ConnectToWorldRequestMessage(server, token, majorVersion, minorVersion, revision, buildDate, unk) => val clientVersion = s"Client Version: $majorVersion.$minorVersion.$revision, $buildDate" @@ -3805,7 +3823,7 @@ class WorldSessionActor extends Actor with MDCContextAware { self ! SetCurrentAvatar(player) - case msg @ PlayerStateMessageUpstream(avatar_guid, pos, vel, yaw, pitch, yaw_upper, seq_time, unk3, is_crouching, is_jumping, unk4, is_cloaking, unk5, unk6) => + case msg @ PlayerStateMessageUpstream(avatar_guid, pos, vel, yaw, pitch, yaw_upper, seq_time, unk3, is_crouching, is_jumping, jump_thrust, is_cloaking, unk5, unk6) => if(deadState == DeadState.Alive) { if (timeDL != 0) { if (System.currentTimeMillis() - timeDL > 500) { @@ -3891,7 +3909,7 @@ class WorldSessionActor extends Actor with MDCContextAware { case Some(item) => item.Definition == GlobalDefinitions.bolt_driver case None => false } - avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlayerState(avatar_guid, msg, spectator, wepInHand)) + avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlayerState(avatar_guid, player.Position, player.Velocity, yaw, pitch, yaw_upper, seq_time, is_crouching, is_jumping, jump_thrust, is_cloaking, spectator, wepInHand)) updateSquad() } else { @@ -3918,7 +3936,7 @@ class WorldSessionActor extends Actor with MDCContextAware { //log.warn(s"ChildObjectState: player $player not related to anything with a controllable agent") } - case msg @ VehicleStateMessage(vehicle_guid, unk1, pos, ang, vel, flight, unk6, unk7, wheels, unk9, is_cloaked) => + case msg @ VehicleStateMessage(vehicle_guid, unk1, pos, ang, vel, flying, unk6, unk7, wheels, unk9, is_cloaked) => if(deadState == DeadState.Alive) { GetVehicleAndSeat() match { case (Some(obj), Some(0)) => @@ -3933,11 +3951,15 @@ class WorldSessionActor extends Actor with MDCContextAware { if(obj.MountedIn.isEmpty) { obj.Velocity = vel if(obj.Definition.CanFly) { - obj.Flying = flight.nonEmpty //usually Some(7) + obj.Flying = flying.nonEmpty //usually Some(7) } obj.Cloaked = obj.Definition.CanCloak && is_cloaked - vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.VehicleState(player.GUID, vehicle_guid, unk1, pos, ang, vel, flight, unk6, unk7, wheels, unk9, is_cloaked)) } + else { + obj.Velocity = None + obj.Flying = false + } + vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.VehicleState(player.GUID, vehicle_guid, unk1, obj.Position, ang, obj.Velocity, if(obj.Flying) { flying } else { None }, unk6, unk7, wheels, unk9, obj.Cloaked)) updateSquad() case (None, _) => //log.error(s"VehicleState: no vehicle $vehicle_guid found in zone") @@ -5387,6 +5409,12 @@ class WorldSessionActor extends Actor with MDCContextAware { prefire = None EmptyMagazine(weapon_guid, tool) } + else if(!player.isAlive) { //proper internal accounting, but no projectile + prefire = shooting.orElse(Some(weapon_guid)) + tool.Discharge + projectiles(projectile_guid.guid - Projectile.BaseUID) = None + shotsWhileDead += 1 + } else { //shooting if (tool.FireModeIndex == 1 && (tool.Definition.Name == "anniversary_guna" || tool.Definition.Name == "anniversary_gun" || tool.Definition.Name == "anniversary_gunb")) { player.Stamina = 0 @@ -7634,6 +7662,10 @@ class WorldSessionActor extends Actor with MDCContextAware { case None => avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.DestroyDisplay(pentry, pentry, 0)) } + if(shotsWhileDead > 0) { + log.warn(s"KillPlayer/SHOTS_WHILE_DEAD: client of ${avatar.name} fired $shotsWhileDead rounds while character was dead on server") + shotsWhileDead = 0 + } import scala.concurrent.ExecutionContext.Implicits.global reviveTimer = context.system.scheduler.scheduleOnce(respawnTimer milliseconds, cluster, Zone.Lattice.RequestSpawnPoint(Zones.SanctuaryZoneNumber(tplayer.Faction), tplayer, 7))