diff --git a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala
index 6a3b2b51c..6a4845579 100644
--- a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala
+++ b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala
@@ -389,7 +389,7 @@ object GamePacketOpcode extends Enumeration {
case 0x39 => game.ChangeFireStateMessage_Start.decode
case 0x3a => game.ChangeFireStateMessage_Stop.decode
case 0x3b => noDecoder(UnknownMessage59)
- case 0x3c => noDecoder(GenericCollisionMsg)
+ case 0x3c => game.GenericCollisionMsg.decode
case 0x3d => game.QuantityUpdateMessage.decode
case 0x3e => game.ArmorChangedMessage.decode
case 0x3f => noDecoder(ProjectileStateMessage)
diff --git a/common/src/main/scala/net/psforever/packet/game/GenericCollisionMsg.scala b/common/src/main/scala/net/psforever/packet/game/GenericCollisionMsg.scala
new file mode 100644
index 000000000..33d313023
--- /dev/null
+++ b/common/src/main/scala/net/psforever/packet/game/GenericCollisionMsg.scala
@@ -0,0 +1,62 @@
+// Copyright (c) 2016 PSForever.net to present
+package net.psforever.packet.game
+
+import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
+import net.psforever.types.Vector3
+import scodec.Codec
+import scodec.codecs._
+
+/**
+ * Dispatched by the client when the player has encountered a physical interaction that would cause damage.
+ *
+ * Collision information reports about two subjects who were involved in an altercation.
+ * The first is the `player`, that is, the client's avatar.
+ * The second is the `target` with respect to the `player` - whatever the avatar ran into, or whatever ran into the avatar.
+ * In the case of isolated forms of collision such as fall damage the `target` fields are blank or zero'd.
+ * @param unk1 na
+ * @param player the player or player-controlled vehicle
+ * @param target the other party in the collision
+ * @param player_health the player's health
+ * @param target_health the target's health
+ * @param player_velocity the player's velocity
+ * @param target_velocity the target's velocity
+ * @param player_pos the player's world coordinates
+ * @param target_pos the target's world coordinates
+ * @param unk2 na
+ * @param unk3 na
+ * @param unk4 na
+ */
+final case class GenericCollisionMsg(unk1 : Int,
+ player : PlanetSideGUID,
+ target : PlanetSideGUID,
+ player_health : Int,
+ target_health : Int,
+ player_velocity : Vector3,
+ target_velocity : Vector3,
+ player_pos : Vector3,
+ target_pos : Vector3,
+ unk2 : Long,
+ unk3 : Long,
+ unk4 : Long)
+ extends PlanetSideGamePacket {
+ type Packet = GenericCollisionMsg
+ def opcode = GamePacketOpcode.GenericCollisionMsg
+ def encode = GenericCollisionMsg.encode(this)
+}
+
+object GenericCollisionMsg extends Marshallable[GenericCollisionMsg] {
+ implicit val codec : Codec[GenericCollisionMsg] = (
+ ("unk1" | uint2) ::
+ ("p" | PlanetSideGUID.codec) ::
+ ("t" | PlanetSideGUID.codec) ::
+ ("p_health" | uint16L) ::
+ ("t_health" | uint16L) ::
+ ("p_vel" | Vector3.codec_float) ::
+ ("t_vel" | Vector3.codec_float) ::
+ ("p_pos" | Vector3.codec_pos) ::
+ ("t_pos" | Vector3.codec_pos) ::
+ ("unk2" | uint32L) ::
+ ("unk3" | uint32L) ::
+ ("unk4" | uint32L)
+ ).as[GenericCollisionMsg]
+}
diff --git a/common/src/main/scala/net/psforever/types/Vector3.scala b/common/src/main/scala/net/psforever/types/Vector3.scala
index 3fdae394b..ecb00e8a3 100644
--- a/common/src/main/scala/net/psforever/types/Vector3.scala
+++ b/common/src/main/scala/net/psforever/types/Vector3.scala
@@ -21,4 +21,10 @@ object Vector3 {
("y" | newcodecs.q_float(-256.0, 256.0, 14)) ::
("z" | newcodecs.q_float(-256.0, 256.0, 14))
).as[Vector3]
+
+ implicit val codec_float : Codec[Vector3] = (
+ ("x" | floatL) ::
+ ("y" | floatL) ::
+ ("z" | floatL)
+ ).as[Vector3]
}
diff --git a/common/src/test/scala/game/GenericCollisionMsgTest.scala b/common/src/test/scala/game/GenericCollisionMsgTest.scala
new file mode 100644
index 000000000..970ce4f5b
--- /dev/null
+++ b/common/src/test/scala/game/GenericCollisionMsgTest.scala
@@ -0,0 +1,45 @@
+// Copyright (c) 2016 PSForever.net to present
+package game
+
+import org.specs2.mutable._
+import net.psforever.packet._
+import net.psforever.packet.game._
+import net.psforever.types.Vector3
+import scodec.bits._
+
+class GenericCollisionMsgTest extends Specification {
+ //TODO find a better test later
+ val string = hex"3C 92C00000190000001B2A8010932CEF505C70946F00000000000000000000000017725EBC6D6A058000000000000000000000000000003F8FF45140"
+ "decode" in {
+ PacketCoding.DecodePacket(string).require match {
+ case GenericCollisionMsg(unk1, p, t, php, thp, pv, tv, ppos, tpos, unk2, unk3, unk4) =>
+ unk1 mustEqual 2
+ p mustEqual PlanetSideGUID(75)
+ t mustEqual PlanetSideGUID(0)
+ php mustEqual 100
+ thp mustEqual 0
+ pv.x mustEqual 32.166428f
+ pv.y mustEqual 23.712547f
+ pv.z mustEqual -0.012802706f
+ tv.x mustEqual 0.0f
+ tv.z mustEqual 0.0f
+ tv.x mustEqual 0.0f
+ ppos.x mustEqual 3986.7266f
+ ppos.y mustEqual 2615.3672f
+ ppos.z mustEqual 90.625f
+ tpos.x mustEqual 0.0f
+ tpos.y mustEqual 0.0f
+ tpos.z mustEqual 0.0f
+ unk2 mustEqual 0L
+ unk3 mustEqual 0L
+ unk4 mustEqual 1171341310L
+ case _ =>
+ ko
+ }
+ }
+ "encode" in {
+ val msg = GenericCollisionMsg(2, PlanetSideGUID(75), PlanetSideGUID(0), 100, 0, Vector3(32.166428f, 23.712547f, -0.012802706f), Vector3(0.0f, 0.0f, 0.0f), Vector3(3986.7266f, 2615.3672f, 90.625f), Vector3(0.0f, 0.0f, 0.0f), 0L, 0L, 1171341310L)
+ val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
+ pkt mustEqual string
+ }
+}
diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala
index 1af9aa6fe..f32a9fa9d 100644
--- a/pslogin/src/main/scala/WorldSessionActor.scala
+++ b/pslogin/src/main/scala/WorldSessionActor.scala
@@ -370,6 +370,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
case msg @ SquadDefinitionActionMessage(a, b, c, d, e, f, g, h, i) =>
log.info("SquadDefinitionAction: " + msg)
+ case msg @ GenericCollisionMsg(u1, p, t, php, thp, pvx, pvy, pvz, tvx, tvy, tvz, ppos, tpos, u2, u3, u4) =>
+ log.info("Ouch! " + msg)
+
case msg @ BugReportMessage(version_major,version_minor,version_date,bug_type,repeatable,location,zone,pos,summary,desc) =>
log.info("BugReportMessage: " + msg)
@@ -377,7 +380,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
log.info("BindPlayerMessage: " + msg)
case default => log.error(s"Unhandled GamePacket ${pkt}")
-
}
def failWithError(error : String) = {