Maxes and Turrets Fixes (#237)

* modified DCData to account for Max suit encoding; modified converters for the avatar that would make use of DCData changes; corrections to enforce that Max/ex-Max always has correct hand showing

* separating initialization-time random GUID assignments from initialization-time fixed GUID assignments by performing them all later
This commit is contained in:
Fate-JH 2018-11-21 20:00:33 -05:00 committed by GitHub
parent 5a67fcd88d
commit 7588166ac0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 452 additions and 28 deletions

View file

@ -4,7 +4,7 @@ package net.psforever.objects.definition.converter
import net.psforever.objects.{EquipmentSlot, Player} import net.psforever.objects.{EquipmentSlot, Player}
import net.psforever.objects.equipment.Equipment import net.psforever.objects.equipment.Equipment
import net.psforever.packet.game.objectcreate._ import net.psforever.packet.game.objectcreate._
import net.psforever.types.{GrenadeState, ImplantType} import net.psforever.types.{ExoSuitType, GrenadeState, ImplantType}
import scala.annotation.tailrec import scala.annotation.tailrec
import scala.util.{Success, Try} import scala.util.{Success, Try}
@ -124,7 +124,8 @@ object AvatarConverter {
} }
def MakeDetailedCharacterData(obj : Player) : (Option[Int])=>DetailedCharacterData = { def MakeDetailedCharacterData(obj : Player) : (Option[Int])=>DetailedCharacterData = {
val bep = obj.BEP val bep : Long = obj.BEP
val maxOpt : Option[Long] = if(obj.ExoSuit == ExoSuitType.MAX) { Some(0L) } else { None }
val ba : DetailedCharacterA = DetailedCharacterA( val ba : DetailedCharacterA = DetailedCharacterA(
bep, bep,
obj.CEP, obj.CEP,
@ -134,6 +135,7 @@ object AvatarConverter {
obj.Armor, obj.Armor,
0L, 0L,
obj.MaxStamina, obj.Stamina, obj.MaxStamina, obj.Stamina,
maxOpt,
0, 0, 0L, 0, 0, 0L,
List(0, 0, 0, 0, 0, 0), List(0, 0, 0, 0, 0, 0),
obj.Certifications.toList.sortBy(_.id) //TODO is sorting necessary? obj.Certifications.toList.sortBy(_.id) //TODO is sorting necessary?

View file

@ -4,7 +4,7 @@ package net.psforever.objects.definition.converter
import net.psforever.objects.{EquipmentSlot, Player} import net.psforever.objects.{EquipmentSlot, Player}
import net.psforever.objects.equipment.Equipment import net.psforever.objects.equipment.Equipment
import net.psforever.packet.game.objectcreate._ import net.psforever.packet.game.objectcreate._
import net.psforever.types.{CertificationType, CharacterVoice, GrenadeState, ImplantType} import net.psforever.types._
import scala.annotation.tailrec import scala.annotation.tailrec
import scala.util.{Failure, Success, Try} import scala.util.{Failure, Success, Try}
@ -77,7 +77,8 @@ class CharacterSelectConverter extends AvatarConverter {
} }
private def MakeDetailedCharacterData(obj : Player) : (Option[Int]=>DetailedCharacterData) = { private def MakeDetailedCharacterData(obj : Player) : (Option[Int]=>DetailedCharacterData) = {
val bep = obj.BEP val bep : Long = obj.BEP
val maxOpt : Option[Long] = if(obj.ExoSuit == ExoSuitType.MAX) { Some(0L) } else { None }
val ba : DetailedCharacterA = DetailedCharacterA( val ba : DetailedCharacterA = DetailedCharacterA(
bep, bep,
obj.CEP, obj.CEP,
@ -87,6 +88,7 @@ class CharacterSelectConverter extends AvatarConverter {
0, 0,
0L, 0L,
1, 1, 1, 1,
maxOpt,
0, 0, 0L, 0, 0, 0L,
List(0, 0, 0, 0, 0, 0), List(0, 0, 0, 0, 0, 0),
certs = List.empty[CertificationType.Value] certs = List.empty[CertificationType.Value]

View file

@ -72,6 +72,7 @@ class CorpseConverter extends AvatarConverter {
} }
private def MakeDetailedCharacterData(obj : Player) : (Option[Int]=>DetailedCharacterData) = { private def MakeDetailedCharacterData(obj : Player) : (Option[Int]=>DetailedCharacterData) = {
val maxOpt : Option[Long] = if(obj.ExoSuit == ExoSuitType.MAX) { Some(0L) } else { None }
val ba : DetailedCharacterA = DetailedCharacterA( val ba : DetailedCharacterA = DetailedCharacterA(
bep = 0L, bep = 0L,
cep = 0L, cep = 0L,
@ -81,6 +82,7 @@ class CorpseConverter extends AvatarConverter {
0, 0,
0L, 0L,
0, 0, 0, 0,
maxOpt,
0, 0, 0L, 0, 0, 0L,
List(0, 0, 0, 0, 0, 0), List(0, 0, 0, 0, 0, 0),
certs = List.empty[CertificationType.Value] certs = List.empty[CertificationType.Value]

View file

@ -6,6 +6,7 @@ import akka.routing.RandomPool
import net.psforever.objects.ballistics.Projectile import net.psforever.objects.ballistics.Projectile
import net.psforever.objects._ import net.psforever.objects._
import net.psforever.objects.ce.Deployable import net.psforever.objects.ce.Deployable
import net.psforever.objects.entity.IdentifiableEntity
import net.psforever.objects.equipment.Equipment import net.psforever.objects.equipment.Equipment
import net.psforever.objects.guid.NumberPoolHub import net.psforever.objects.guid.NumberPoolHub
import net.psforever.objects.guid.actor.UniqueNumberSystem import net.psforever.objects.guid.actor.UniqueNumberSystem
@ -313,6 +314,7 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
private def BuildSupportObjects() : Unit = { private def BuildSupportObjects() : Unit = {
//guard against errors here, but don't worry about specifics; let ZoneActor.ZoneSetupCheck complain about problems //guard against errors here, but don't worry about specifics; let ZoneActor.ZoneSetupCheck complain about problems
val other : ListBuffer[IdentifiableEntity] = new ListBuffer[IdentifiableEntity]()
//turret to weapon //turret to weapon
Map.TurretToWeapon.foreach({ case ((turret_guid, weapon_guid)) => Map.TurretToWeapon.foreach({ case ((turret_guid, weapon_guid)) =>
((GUID(turret_guid) match { ((GUID(turret_guid) match {
@ -333,11 +335,13 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
}) match { }) match {
case Some((obj, Some(weapon : Tool))) => case Some((obj, Some(weapon : Tool))) =>
guid.register(weapon, weapon_guid) guid.register(weapon, weapon_guid)
weapon.AmmoSlots.foreach(slot => guid.register(slot.Box, "dynamic")) other ++= weapon.AmmoSlots.map(slot => slot.Box)
obj.Inventory.Items.foreach(item => guid.register(item.obj, "dynamic")) //internal ammunition reserves, if any other ++= obj.Inventory.Items.map(item => item.obj) //internal ammunition reserves, if any
case _ => ; case _ => ;
} }
}) })
//after all fixed GUID's are defined ...
other.foreach(obj => guid.register(obj, "dynamic"))
} }
private def MakeBuildings(implicit context : ActorContext) : PairMap[Int, Building] = { private def MakeBuildings(implicit context : ActorContext) : PairMap[Int, Building] = {

View file

@ -3,7 +3,7 @@ package net.psforever.packet.game.objectcreate
import net.psforever.newcodecs.newcodecs import net.psforever.newcodecs.newcodecs
import net.psforever.packet.{Marshallable, PacketHelpers} import net.psforever.packet.{Marshallable, PacketHelpers}
import net.psforever.types.{CertificationType, ImplantType} import net.psforever.types.{CertificationType, ExoSuitType, ImplantType}
import scodec.{Attempt, Codec} import scodec.{Attempt, Codec}
import scodec.codecs._ import scodec.codecs._
import shapeless.{::, HNil} import shapeless.{::, HNil}
@ -71,6 +71,8 @@ final case class DCDExtra2(unk1 : Int,
* range is 0-65535 * range is 0-65535
* @param stamina for `x / y` of stamina points, this is the avatar's `x` value; * @param stamina for `x / y` of stamina points, this is the avatar's `x` value;
* range is 0-65535 * range is 0-65535
* @param max_field unk;
* this field exists only when the player is wearing a mechanized assault exo-suit
* @param certs the `List` of certifications * @param certs the `List` of certifications
*/ */
final case class DetailedCharacterA(bep : Long, final case class DetailedCharacterA(bep : Long,
@ -85,14 +87,16 @@ final case class DetailedCharacterA(bep : Long,
unk5 : Long, unk5 : Long,
staminaMax : Int, staminaMax : Int,
stamina : Int, stamina : Int,
max_field : Option[Long],
unk6 : Int, unk6 : Int,
unk7 : Int, unk7 : Int,
unk8 : Long, unk8 : Long,
unk9 : List[Int], unk9 : List[Int],
certs : List[CertificationType.Value]) extends StreamBitSize { certs : List[CertificationType.Value]) extends StreamBitSize {
override def bitsize : Long = { override def bitsize : Long = {
val maxFieldSize = max_field match { case Some(_) => 32L ; case None => 0L }
val certSize : Long = certs.length * 8 val certSize : Long = certs.length * 8
428L + certSize 428L + maxFieldSize + certSize
} }
} }
@ -197,6 +201,7 @@ object DetailedCharacterData extends Marshallable[DetailedCharacterData] {
armor : Int, armor : Int,
staminaMax : Int, staminaMax : Int,
stamina : Int, stamina : Int,
maxField : Option[Long],
certs : List[CertificationType.Value], certs : List[CertificationType.Value],
implants : List[ImplantEntry], implants : List[ImplantEntry],
firstTimeEvents : List[String], firstTimeEvents : List[String],
@ -213,6 +218,7 @@ object DetailedCharacterData extends Marshallable[DetailedCharacterData] {
armor, armor,
0L, 0L,
staminaMax, stamina, staminaMax, stamina,
maxField,
0, 0,
0, 0,
0L, 0L,
@ -527,7 +533,7 @@ object DetailedCharacterData extends Marshallable[DetailedCharacterData] {
*/ */
def isBR24(bep : Long) : Boolean = bep > 2286230 def isBR24(bep : Long) : Boolean = bep > 2286230
val a_codec : Codec[DetailedCharacterA] = ( def a_codec(suit : ExoSuitType.Value) : Codec[DetailedCharacterA] = (
("bep" | uint32L) :: ("bep" | uint32L) ::
("cep" | uint32L) :: ("cep" | uint32L) ::
("unk1" | uint32L) :: ("unk1" | uint32L) ::
@ -540,7 +546,7 @@ object DetailedCharacterData extends Marshallable[DetailedCharacterData] {
("unk5" | uint32) :: //endianness? ("unk5" | uint32) :: //endianness?
("staminaMax" | uint16L) :: ("staminaMax" | uint16L) ::
("stamina" | uint16L) :: ("stamina" | uint16L) ::
conditional(false, uint32L) :: //see ps.c: sub_901150, line#1070692 conditional(suit == ExoSuitType.MAX, uint32L) ::
("unk6" | uint16L) :: ("unk6" | uint16L) ::
("unk7" | uint(3)) :: ("unk7" | uint(3)) ::
("unk8" | uint32L) :: ("unk8" | uint32L) ::
@ -548,13 +554,13 @@ object DetailedCharacterData extends Marshallable[DetailedCharacterData] {
("certs" | listOfN(uint8L, CertificationType.codec)) ("certs" | listOfN(uint8L, CertificationType.codec))
).exmap[DetailedCharacterA] ( ).exmap[DetailedCharacterA] (
{ {
case bep :: cep :: u1 :: u2 :: u3 :: healthMax :: health :: u4 :: armor :: u5 :: staminaMax :: stamina :: None :: u6 :: u7 :: u8 :: u9 :: certs :: HNil => case bep :: cep :: u1 :: u2 :: u3 :: healthMax :: health :: u4 :: armor :: u5 :: staminaMax :: stamina :: max :: u6 :: u7 :: u8 :: u9 :: certs :: HNil =>
Attempt.successful(DetailedCharacterA(bep, cep, u1, u2, u3, healthMax, health, u4, armor, u5, staminaMax, stamina, u6, u7, u8, u9, certs)) Attempt.successful(DetailedCharacterA(bep, cep, u1, u2, u3, healthMax, health, u4, armor, u5, staminaMax, stamina, max, u6, u7, u8, u9, certs))
}, },
{ {
case DetailedCharacterA(bep, cep, u1, u2, u3, healthMax, health, u4, armor, u5, staminaMax, stamina, u6, u7, u8, u9, certs) => case DetailedCharacterA(bep, cep, u1, u2, u3, healthMax, health, u4, armor, u5, staminaMax, stamina, max, u6, u7, u8, u9, certs) =>
Attempt.successful( Attempt.successful(
bep :: cep :: u1 :: u2 :: u3 :: healthMax :: health :: u4 :: armor :: u5 :: staminaMax :: stamina :: None :: u6 :: u7 :: u8 :: u9 :: certs :: HNil bep :: cep :: u1 :: u2 :: u3 :: healthMax :: health :: u4 :: armor :: u5 :: staminaMax :: stamina :: max :: u6 :: u7 :: u8 :: u9 :: certs :: HNil
) )
} }
) )
@ -614,8 +620,8 @@ object DetailedCharacterData extends Marshallable[DetailedCharacterData] {
} }
) )
def codec(pad_length : Option[Int]) : Codec[DetailedCharacterData] = ( def codec(suit : ExoSuitType.Value, pad_length : Option[Int]) : Codec[DetailedCharacterData] = (
("a" | a_codec) >>:~ { a => ("a" | a_codec(suit)) >>:~ { a =>
("b" | b_codec(a.bep, pad_length)).hlist ("b" | b_codec(a.bep, pad_length)).hlist
} }
).exmap[DetailedCharacterData] ( ).exmap[DetailedCharacterData] (
@ -629,5 +635,5 @@ object DetailedCharacterData extends Marshallable[DetailedCharacterData] {
} }
) )
implicit val codec : Codec[DetailedCharacterData] = codec(None) implicit val codec : Codec[DetailedCharacterData] = codec(ExoSuitType.Standard, None)
} }

View file

@ -114,7 +114,7 @@ object DetailedPlayerData extends Marshallable[DetailedPlayerData] {
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.altModelBit)) :: ("character_data" | DetailedCharacterData.codec(app.a.exosuit, app.altModelBit)) ::
optional(bool, "inventory" | InventoryData.codec_detailed) :: optional(bool, "inventory" | InventoryData.codec_detailed) ::
("drawn_slot" | DrawnSlot.codec) :: ("drawn_slot" | DrawnSlot.codec) ::
bool //usually false bool //usually false

File diff suppressed because one or more lines are too long

View file

@ -1460,17 +1460,17 @@ class WorldSessionActor extends Actor with MDCContextAware {
} }
else { else {
//remove potential MAX weapon //remove potential MAX weapon
tplayer.DrawnSlot = Player.HandsDownSlot
sendResponse(ObjectHeldMessage(tplayer.GUID, Player.HandsDownSlot, true))
val normalWeapons = if(originalSuit == ExoSuitType.MAX) { val normalWeapons = if(originalSuit == ExoSuitType.MAX) {
val (maxWeapons, normalWeapons) = beforeHolsters.partition(elem => elem.obj.Size == EquipmentSize.Max) val (maxWeapons, normalWeapons) = beforeHolsters.partition(elem => elem.obj.Size == EquipmentSize.Max)
maxWeapons.foreach(entry => { maxWeapons.foreach(entry => {
taskResolver ! GUIDTask.UnregisterEquipment(entry.obj)(continent.GUID) taskResolver ! GUIDTask.UnregisterEquipment(entry.obj)(continent.GUID)
}) })
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.ObjectHeld(player.GUID, player.LastDrawnSlot))
normalWeapons normalWeapons
} }
else { else {
tplayer.DrawnSlot = Player.HandsDownSlot
sendResponse(ObjectHeldMessage(tplayer.GUID, Player.HandsDownSlot, true))
avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.ObjectHeld(tplayer.GUID, Player.HandsDownSlot))
beforeHolsters beforeHolsters
} }
//fill holsters //fill holsters
@ -2879,7 +2879,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
//we're driving the vehicle //we're driving the vehicle
player.Position = pos //convenient player.Position = pos //convenient
if(seat.ControlledWeapon.isEmpty) { if(seat.ControlledWeapon.isEmpty) {
player.Orientation = Vector3(0f, 0f, ang.z) //convenient player.Orientation = Vector3.z(ang.z) //convenient
} }
obj.Position = pos obj.Position = pos
obj.Orientation = ang obj.Orientation = ang

View file

@ -476,6 +476,7 @@ class PacketCodingActorITest extends ActorTest {
100, 100, 100, 100,
50, 50,
100, 100, 100, 100,
None,
List(CertificationType.StandardAssault, CertificationType.MediumAssault, CertificationType.ATV, CertificationType.Harasser, CertificationType.StandardExoSuit, CertificationType.AgileExoSuit, CertificationType.ReinforcedExoSuit), List(CertificationType.StandardAssault, CertificationType.MediumAssault, CertificationType.ATV, CertificationType.Harasser, CertificationType.StandardExoSuit, CertificationType.AgileExoSuit, CertificationType.ReinforcedExoSuit),
List(), List(),
List(), List(),
@ -601,6 +602,7 @@ class PacketCodingActorKTest extends ActorTest {
50, 50,
32831L, 32831L,
100, 100, 100, 100,
None,
0, 0, 0L, 0, 0, 0L,
List(0, 0, 0, 0, 0, 0), List(0, 0, 0, 0, 0, 0),
List( List(