diff --git a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala index 46c4b1ec..21245621 100644 --- a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala +++ b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala @@ -405,7 +405,7 @@ object GamePacketOpcode extends Enumeration { // OPCODE 70 case ChangeFireModeMessage => game.ChangeFireModeMessage.decode case ChangeAmmoMessage => game.ChangeAmmoMessage.decode - case TimeOfDayMessage => noDecoder(opcode) + case TimeOfDayMessage => game.TimeOfDayMessage.decode case UnknownMessage73 => noDecoder(opcode) case SpawnRequestMessage => noDecoder(opcode) case DeployRequestMessage => noDecoder(opcode) diff --git a/common/src/main/scala/net/psforever/packet/game/TimeOfDayMessage.scala b/common/src/main/scala/net/psforever/packet/game/TimeOfDayMessage.scala new file mode 100644 index 00000000..67d5d52a --- /dev/null +++ b/common/src/main/scala/net/psforever/packet/game/TimeOfDayMessage.scala @@ -0,0 +1,147 @@ +// Copyright (c) 2016 PSForever.net to present +package net.psforever.packet.game + +import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket} +import scodec.Codec +import scodec.codecs._ + +/** + * Sets Auraxis time on the client. + * Use the slash-command /time to view the current time in the event window. + * Auraxis time is represented as a standard military twenty-four hour clock, displayed in hours and minutes.
+ *
+ * Time is set per zone on map loading. + * Time affects, primarily, ambient light on surfaces. + * It goes from full daylight, to twilights, to slightly darker nights, though the actual intensity will differ by zone.
+ *
+ * Auraxis time is weird. + * The data from the server is deconstructed into both a current time and a rate of progression. + * The lower the value, the lower the rate; the greater the value, the greater the rate. + * The rate is the product of the number of "cycles" between the current time and an origin time and a base interval. + * The current time is constrained to a looping twenty-four hour interval.
+ *
+ * If no time is set, the client starts counting from 10:00 at an initial rate of about one Auraxis minute every four or five real seconds. + * Setting the current time to 00 00 42 sets the current time to 00:00 with an indeterminate, but slow, rate. + * Time is normally initialized somewhere within an interval between 00 00 46 and FF FF 47. + * Setting the current time extremely high can cause psychedelic rendering as the current time overflows and the rate is too fast. + * (Setting the time to FF FF FF will reduce the rendering system to true gibberish.)
+ *
+ * The interval from 5E 39 46 (4602206, which is ~03:18) to 00 C0 47 (4702208, which is 03:18) is about a full twenty-four hours. + * Coincidentally, that is a count of 100002. + * @param unk1 consistently 00; does nothing? + * @param time Auraxis time + * @param unk2 consistently 00; does nothing? + * @param unk3 consistently 00; does nothing? + * @param unk4 consistently 20; does nothing? + * @param unk5 consistently 41; does nothing? + */ +final case class TimeOfDayMessage(unk1 : Int, + time : Int, + unk2 : Int, + unk3 : Int, + unk4 : Int, + unk5 : Int) + extends PlanetSideGamePacket { + type Packet = TimeOfDayMessage + def opcode = GamePacketOpcode.TimeOfDayMessage + def encode = TimeOfDayMessage.encode(this) +} + +object TimeOfDayMessage extends Marshallable[TimeOfDayMessage] { + implicit val codec : Codec[TimeOfDayMessage] = ( + ("unk1" | uint8L) :: + ("time" | uintL(24)) :: + ("unk2" | uint8L) :: + ("unk3" | uint8L) :: + ("unk4" | uint8L) :: + ("unk5" | uint8L) + ).as[TimeOfDayMessage] +} + +/* +Time Testing Conducted in VS Sanctuary +48 00 __ __ __ 00 00 20 41 +-------------------------- + +01 00 00 +-------------------------- +48 00 1B 00 47 00 00 20 41 //09:06 +48 00 1C 00 47 00 00 20 41 //09:07 +... +48 00 59 00 47 00 00 20 41 //09:08 (+3D 00 00) +-------------------------- + +10 00 00 +-------------------------- +48 00 00 00 47 00 00 20 41 //09:06 <-- +48 00 10 00 47 00 00 20 41 //09:06 +48 00 20 00 47 00 00 20 41 //09:07 +48 00 30 00 47 00 00 20 41 //09:07 +48 00 40 00 47 00 00 20 41 //09:07 +48 00 50 00 47 00 00 20 41 //09:07 +48 00 60 00 47 00 00 20 41 //09:08 +48 00 70 00 47 00 00 20 41 //09:08 +48 00 80 00 47 00 00 20 41 //09:08 +48 00 90 00 47 00 00 20 41 //09:08 +48 00 A0 00 47 00 00 20 41 //09:09 +48 00 B0 00 47 00 00 20 41 //09:09 +48 00 C0 00 47 00 00 20 41 //09:09 +48 00 D0 00 47 00 00 20 41 //09:09 +48 00 E0 00 47 00 00 20 41 //09:10 +48 00 F0 00 47 00 00 20 41 //09:10 +48 00 00 01 47 00 00 20 41 //09:10 +-------------------------- + +00 01 00 +-------------------------- +48 00 00 00 47 00 00 20 41 //09:06 <-- +48 00 00 01 47 00 00 20 41 //09:10 +48 00 00 02 47 00 00 20 41 //09:15 +48 00 00 03 47 00 00 20 41 //09:19 +48 00 00 04 47 00 00 20 41 //09:23 +48 00 00 05 47 00 00 20 41 //09:27 +48 00 00 06 47 00 00 20 41 //09:32 +48 00 00 07 47 00 00 20 41 //09:36 +48 00 00 08 47 00 00 20 41 //09:40 +48 00 00 09 47 00 00 20 41 //09:44 +48 00 00 0A 47 00 00 20 41 //09:49 +48 00 00 0B 47 00 00 20 41 //09:53 +48 00 00 0C 47 00 00 20 41 //09:57 +48 00 00 0D 47 00 00 20 41 //09:01 +48 00 00 0E 47 00 00 20 41 //10:06 +48 00 00 0F 47 00 00 20 41 //10:10 +48 00 00 10 47 00 00 20 41 //10:14 +-------------------------- + +00 10 00 +-------------------------- +48 00 00 00 46 00 00 20 41 //02:17 (-00:17) +48 00 00 10 46 00 00 20 41 //02:34 (-00:17) +48 00 00 20 46 00 00 20 41 //02:51 (-00:17) +48 00 00 30 46 00 00 20 41 //03:08 (-00:17) +48 00 00 40 46 00 00 20 41 //03:25 (-00:17) +48 00 00 50 46 00 00 20 41 //03:42 (-00:17) +48 00 00 60 46 00 00 20 41 //03:59 (-00:17) +48 00 00 70 46 00 00 20 41 //04:16 (-00:17) +48 00 00 80 46 00 00 20 41 //04:33 (-00:34) +48 00 00 90 46 00 00 20 41 //05:07 (-00:34) +48 00 00 A0 46 00 00 20 41 //05:41 (-00:35) +48 00 00 B0 46 00 00 20 41 //06:16 (-00:34) +48 00 00 C0 46 00 00 20 41 //06:50 (-00:34) +48 00 00 D0 46 00 00 20 41 //07:24 (-00:34) +48 00 00 E0 46 00 00 20 41 //07:58 (-00:34) +48 00 00 F0 46 00 00 20 41 //08:32 (-00:34) +48 00 00 00 47 00 00 20 41 //09:06 <-- +48 00 00 10 47 00 00 20 41 //10:14 (+01:08) +48 00 00 20 47 00 00 20 41 //11:23 (+01:09) +48 00 00 30 47 00 00 20 41 //12:31 (+01:08) +48 00 00 40 47 00 00 20 41 //13:39 (+01:08) +48 00 00 50 47 00 00 20 41 //14:47 (+01:08) +48 00 00 60 47 00 00 20 41 //15:56 (+01:08) +48 00 00 70 47 00 00 20 41 //17:04 (+01:08) +48 00 00 80 47 00 00 20 41 //18:12 (+01:08) +48 00 00 90 47 00 00 20 41 //20:29 (+02:16) +48 00 00 A0 47 00 00 20 41 //22:45 (+02:16) +48 00 00 B0 47 00 00 20 41 //01:02 (+02:17) +48 00 00 C0 47 00 00 20 41 //03:18 (+02:16) +48 00 00 D0 47 00 00 20 41 //05:35 (+02:17) +48 00 00 E0 47 00 00 20 41 //07:51 (+02:16) +48 00 00 F0 47 00 00 20 41 //10:08 (+02:17) +48 00 00 00 48 00 00 20 41 //12:24 (+02:16) +*/ diff --git a/common/src/test/scala/GamePacketTest.scala b/common/src/test/scala/GamePacketTest.scala index fc47ab15..f440da2a 100644 --- a/common/src/test/scala/GamePacketTest.scala +++ b/common/src/test/scala/GamePacketTest.scala @@ -469,6 +469,31 @@ class GamePacketTest extends Specification { } } + "TimeOfDayMessage" should { + val string = hex"48 00 00 00 47 00 00 20 41" + + "decode" in { + PacketCoding.DecodePacket(string).require match { + case TimeOfDayMessage(unk1, time, unk2, unk3, unk4, unk5) => + unk1 mustEqual 0 + time mustEqual 4653056 + unk2 mustEqual 0 + unk3 mustEqual 0 + unk4 mustEqual 32 + unk5 mustEqual 65 + case default => + ko + } + } + + "encode" in { + val msg = TimeOfDayMessage(0, 4653056, 0, 0, 32, 65) + val pkt = PacketCoding.EncodePacket(msg).require.toByteVector + + pkt mustEqual string + } + } + "PlayerStateMessageUpstream" should { val string = hex"BD 4B000 E377BA575B616C640A70004014060110007000000" diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index d6b99bef..ac792f4b 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -144,6 +144,7 @@ class WorldSessionActor extends Actor with MDCContextAware { sendResponse(PacketCoding.CreateGamePacket(0, SetEmpireMessage(PlanetSideGUID(2), PlanetSideEmpire.VS))) //HART building C sendResponse(PacketCoding.CreateGamePacket(0, SetEmpireMessage(PlanetSideGUID(29), PlanetSideEmpire.NC))) //South Villa Gun Tower + sendResponse(PacketCoding.CreateGamePacket(0, TimeOfDayMessage(0, 4653056, 0, 0, 32, 65))) sendResponse(PacketCoding.CreateGamePacket(0, ContinentalLockUpdateMessage(PlanetSideGUID(13), PlanetSideEmpire.VS))) // "The VS have captured the VS Sanctuary." sendResponse(PacketCoding.CreateGamePacket(0, BroadcastWarpgateUpdateMessage(PlanetSideGUID(13), PlanetSideGUID(1), 32))) // VS Sanctuary: Inactive Warpgate -> Broadcast Warpgate