mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-03-06 13:40:21 +00:00
Packet: WeatherMessage (#112)
* imported WeatherMessage packet and tests * trusting the debug information, converting from Long to Float (Vector3) * field identification for storms * copyright corrections
This commit is contained in:
parent
e84e9d47e6
commit
9275150c4b
3 changed files with 220 additions and 1 deletions
|
|
@ -495,7 +495,7 @@ object GamePacketOpcode extends Enumeration {
|
|||
case 0x92 => noDecoder(PlanetsideStringAttributeMessage)
|
||||
case 0x93 => noDecoder(DataChallengeMessage)
|
||||
case 0x94 => noDecoder(DataChallengeMessageResp)
|
||||
case 0x95 => noDecoder(WeatherMessage)
|
||||
case 0x95 => game.WeatherMessage.decode
|
||||
case 0x96 => noDecoder(SimDataChallenge)
|
||||
case 0x97 => noDecoder(SimDataChallengeResp)
|
||||
// 0x98
|
||||
|
|
|
|||
|
|
@ -0,0 +1,115 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.packet.game
|
||||
|
||||
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
|
||||
import net.psforever.types.Vector3
|
||||
import scodec.Codec
|
||||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
/**
|
||||
* Cloud data.<br>
|
||||
* <br>
|
||||
* The remaining fields should be divided between a "location" and a "velocity" as per debug output.
|
||||
* The values are probably paired.
|
||||
* The converted data, however, seems weird for the kind of information those fields would suggest.
|
||||
* @param id the id of the cloud;
|
||||
* zero-indexed counter (probably)
|
||||
* @param unk1 na;
|
||||
* the z-component is always `0.0f`
|
||||
* @param unk2 na;
|
||||
* the z-component is always `0.0f`
|
||||
*/
|
||||
final case class CloudInfo(id : Int,
|
||||
unk1 : Vector3,
|
||||
unk2 : Vector3)
|
||||
|
||||
/**
|
||||
* Storm data.
|
||||
* @param loc the location of the storm;
|
||||
* the z-component is always `0.0f`
|
||||
* @param intensity na
|
||||
* @param radius na;
|
||||
* 100 is about 819.2
|
||||
*/
|
||||
final case class StormInfo(loc : Vector3,
|
||||
intensity : Int,
|
||||
radius : Int)
|
||||
|
||||
/**
|
||||
* Dispatched by the server to update weather conditions.
|
||||
* On former live (Gemini), the server sent a new packet to connected clients once every ~60s.<br>
|
||||
* <br>
|
||||
* Information about the fields in this packet come from extracted debug information.
|
||||
* It is not necessarily "correct" but it is the best approximation for now.<br>
|
||||
* <br>
|
||||
* `
|
||||
* Message type: %d (%s)\n length: %d\n<br>
|
||||
* Number of Clouds : %d\n<br>
|
||||
* Cloud ID: %d\n<br>
|
||||
* \tCloud Location: %f %f\n<br>
|
||||
* \tCloud Velocity: %f %f\n<br>
|
||||
* Number of Storms : %d\n<br>
|
||||
* Storm:\n<br>
|
||||
* \tStorm Location: %f %f\n<br>
|
||||
* \tStorm Intensity: %d\n<br>
|
||||
* \tStorm Radius: %d\n<br>
|
||||
* `
|
||||
* @param clouds a list of cloud data;
|
||||
* typically, just one entry
|
||||
* @param storms a list of storm data;
|
||||
* typically, fluctuates between nine and eleven entries
|
||||
*/
|
||||
final case class WeatherMessage(clouds : List[CloudInfo],
|
||||
storms : List[StormInfo])
|
||||
extends PlanetSideGamePacket {
|
||||
type Packet = WeatherMessage
|
||||
def opcode = GamePacketOpcode.WeatherMessage
|
||||
def encode = WeatherMessage.encode(this)
|
||||
}
|
||||
|
||||
object WeatherMessage extends Marshallable[WeatherMessage] {
|
||||
/**
|
||||
* `Codec` for `CloudInfo` data.
|
||||
*/
|
||||
private val cloudCodec : Codec[CloudInfo] = (
|
||||
("id" | uint8L) ::
|
||||
("unk1x" | floatL) ::
|
||||
("unk1y" | floatL) ::
|
||||
("unk2x" | floatL) ::
|
||||
("unk2y" | floatL)
|
||||
).xmap[CloudInfo] (
|
||||
{
|
||||
case id :: x1 :: y1 :: x2 :: y2 :: HNil =>
|
||||
CloudInfo(id, Vector3(x1, y1, 0.0f), Vector3(x2, y2, 0.0f))
|
||||
},
|
||||
{
|
||||
case CloudInfo(id, Vector3(x1, y1, _), Vector3(x2, y2, _)) =>
|
||||
id :: x1 :: y1 :: x2 :: y2 :: HNil
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* `Codec` for `StormInfo` data.
|
||||
*/
|
||||
private val stormCodec : Codec[StormInfo] = (
|
||||
("unkx" | floatL) ::
|
||||
("unky" | floatL) ::
|
||||
("i" | uint8L) ::
|
||||
("r" | uint8L)
|
||||
).xmap[StormInfo] (
|
||||
{
|
||||
case x :: y :: i :: r :: HNil =>
|
||||
StormInfo(Vector3(x, y, 0.0f), i, r)
|
||||
},
|
||||
{
|
||||
case StormInfo(Vector3(x, y, _), i, r) =>
|
||||
x :: y :: i :: r :: HNil
|
||||
}
|
||||
)
|
||||
|
||||
implicit val codec : Codec[WeatherMessage] = (
|
||||
("clouds" | PacketHelpers.listOfNAligned(uint32L, 0, cloudCodec)) ::
|
||||
("storms" | PacketHelpers.listOfNAligned(uint32L, 0, stormCodec))
|
||||
).as[WeatherMessage]
|
||||
}
|
||||
104
common/src/test/scala/game/WeatherMessageTest.scala
Normal file
104
common/src/test/scala/game/WeatherMessageTest.scala
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package game
|
||||
|
||||
import org.specs2.mutable._
|
||||
import net.psforever.packet._
|
||||
import net.psforever.packet.game.{WeatherMessage, CloudInfo, StormInfo}
|
||||
import net.psforever.types.Vector3
|
||||
import scodec.bits._
|
||||
|
||||
class WeatherMessageTest extends Specification {
|
||||
val string = hex"9501000000004A0807C0D65B8FBF2427663F178608BE0B000000006CE13E0C390E3F64445CB7BF3E0C2FF23DA46264A3193FBA522E3F597D9A96093F95B99E3D0800096FE53E6CD6523F39198EAF683F9BA0363D01009C35503F9E5F3E3F3C304E46F23EF9668E3E6B56C8277F3FB084F33EB6C10291423FB17F663F00008C077F3E3135D03E320A"
|
||||
|
||||
"decode" in {
|
||||
PacketCoding.DecodePacket(string).require match {
|
||||
case WeatherMessage(clouds, storms) =>
|
||||
clouds.size mustEqual 1
|
||||
clouds.head.id mustEqual 0
|
||||
clouds.head.unk1.x mustEqual -2.109881f
|
||||
clouds.head.unk1.y mustEqual -1.1199901f
|
||||
clouds.head.unk2.x mustEqual 0.89903474f
|
||||
clouds.head.unk2.y mustEqual -0.13332401f
|
||||
|
||||
storms.size mustEqual 11
|
||||
//0
|
||||
storms.head.loc.x mustEqual 0.4402771f
|
||||
storms.head.loc.y mustEqual 0.55555797f
|
||||
storms.head.intensity mustEqual 100
|
||||
storms.head.radius mustEqual 68
|
||||
//1
|
||||
storms(1).loc.x mustEqual 0.3744458f
|
||||
storms(1).loc.y mustEqual 0.1182538f
|
||||
storms(1).intensity mustEqual 164
|
||||
storms(1).radius mustEqual 98
|
||||
//2
|
||||
storms(2).loc.x mustEqual 0.6001494f
|
||||
storms(2).loc.y mustEqual 0.6809498f
|
||||
storms(2).intensity mustEqual 89
|
||||
storms(2).radius mustEqual 125
|
||||
//3
|
||||
storms(3).loc.x mustEqual 0.53745425f
|
||||
storms(3).loc.y mustEqual 0.07750241f
|
||||
storms(3).intensity mustEqual 8
|
||||
storms(3).radius mustEqual 0
|
||||
//4
|
||||
storms(4).loc.x mustEqual 0.44811276f
|
||||
storms(4).loc.y mustEqual 0.8235843f
|
||||
storms(4).intensity mustEqual 57
|
||||
storms(4).radius mustEqual 25
|
||||
//5
|
||||
storms(5).loc.x mustEqual 0.90892875f
|
||||
storms(5).loc.y mustEqual 0.04458676f
|
||||
storms(5).intensity mustEqual 1
|
||||
storms(5).radius mustEqual 0
|
||||
//6
|
||||
storms(6).loc.x mustEqual 0.813318f
|
||||
storms(6).loc.y mustEqual 0.7436465f
|
||||
storms(6).intensity mustEqual 60
|
||||
storms(6).radius mustEqual 48
|
||||
//7
|
||||
storms(7).loc.x mustEqual 0.47319263f
|
||||
storms(7).loc.y mustEqual 0.27812937f
|
||||
storms(7).intensity mustEqual 107
|
||||
storms(7).radius mustEqual 86
|
||||
//8
|
||||
storms(8).loc.x mustEqual 0.99670076f
|
||||
storms(8).loc.y mustEqual 0.4756217f
|
||||
storms(8).intensity mustEqual 182
|
||||
storms(8).radius mustEqual 193
|
||||
//9
|
||||
storms(9).loc.x mustEqual 0.76002514f
|
||||
storms(9).loc.y mustEqual 0.9003859f
|
||||
storms(9).intensity mustEqual 0
|
||||
storms(9).radius mustEqual 0
|
||||
//10
|
||||
storms(10).loc.x mustEqual 0.24905223f
|
||||
storms(10).loc.y mustEqual 0.40665582f
|
||||
storms(10).intensity mustEqual 50
|
||||
storms(10).radius mustEqual 10
|
||||
case _ =>
|
||||
ko
|
||||
}
|
||||
}
|
||||
|
||||
"encode" in {
|
||||
val msg = WeatherMessage(
|
||||
CloudInfo(0, Vector3(-2.109881f, -1.1199901f, 0.0f), Vector3(0.89903474f, -0.13332401f, 0.0f)) ::
|
||||
Nil,
|
||||
StormInfo(Vector3(0.4402771f, 0.55555797f, 0.0f), 100, 68) ::
|
||||
StormInfo(Vector3(0.3744458f, 0.1182538f, 0.0f), 164, 98) ::
|
||||
StormInfo(Vector3(0.6001494f, 0.6809498f, 0.0f), 89, 125) ::
|
||||
StormInfo(Vector3(0.53745425f, 0.07750241f, 0.0f), 8, 0) ::
|
||||
StormInfo(Vector3(0.44811276f, 0.8235843f, 0.0f), 57, 25) ::
|
||||
StormInfo(Vector3(0.90892875f, 0.04458676f, 0.0f),1 ,0) ::
|
||||
StormInfo(Vector3(0.813318f, 0.7436465f, 0.0f), 60, 48) ::
|
||||
StormInfo(Vector3(0.47319263f, 0.27812937f, 0.0f), 107, 86) ::
|
||||
StormInfo(Vector3(0.99670076f, 0.4756217f, 0.0f), 182, 193) ::
|
||||
StormInfo(Vector3(0.76002514f, 0.9003859f, 0.0f), 0, 0) ::
|
||||
StormInfo(Vector3(0.24905223f, 0.40665582f, 0.0f), 50, 10) ::
|
||||
Nil
|
||||
)
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
pkt mustEqual string
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue