module Octane.Type.Frame (Frame(..)) where
import Data.Aeson ((.=))
import Data.Function ((&))
import Data.Monoid ((<>))
import qualified Control.DeepSeq as DeepSeq
import qualified Data.Aeson as Aeson
import qualified Data.Bimap as Bimap
import qualified Data.Map.Strict as Map
import qualified Data.Text as StrictText
import qualified GHC.Generics as Generics
import qualified Octane.Data as Data
import qualified Octane.Type.CompressedWord as CompressedWord
import qualified Octane.Type.Float32 as Float32
import qualified Octane.Type.Initialization as Initialization
import qualified Octane.Type.Replication as Replication
import qualified Octane.Type.State as State
import qualified Octane.Type.Value as Value
import qualified Octane.Type.Word32 as Word32
import qualified Octane.Type.Word8 as Word8
data Frame = Frame
{ number :: Word
, isKeyFrame :: Bool
, time :: Float32.Float32
, delta :: Float32.Float32
, replications :: [Replication.Replication]
} deriving (Eq, Generics.Generic, Show)
instance DeepSeq.NFData Frame where
instance Aeson.ToJSON Frame where
toJSON frame = Aeson.object
[ "Number" .= number frame
, "IsKeyFrame" .= isKeyFrame frame
, "Time" .= time frame
, "Delta" .= delta frame
, "Spawned" .= (frame & replications & getSpawned)
, "Updated" .= (frame & replications & getUpdated)
, "Destroyed" .= (frame & replications & getDestroyed)
]
newtype Spawned = Spawned [Replication.Replication]
instance Aeson.ToJSON Spawned where
toJSON (Spawned xs) = xs
& map (\ x -> do
let k = x & Replication.actorId & CompressedWord.value & show & StrictText.pack
let v = Aeson.object
[ "Name" .= Replication.objectName x
, "Class" .= Replication.className x
, "Position" .= (x & Replication.initialization & fmap Initialization.location)
, "Rotation" .= (x & Replication.initialization & fmap Initialization.rotation)
]
(k, v))
& Map.fromList
& Aeson.toJSON
getSpawned :: [Replication.Replication] -> Spawned
getSpawned xs = xs
& filter (\ x -> x
& Replication.state
& (== State.SOpening))
& Spawned
newtype Updated = Updated [Replication.Replication]
instance Aeson.ToJSON Updated where
toJSON (Updated xs) = xs
& map (\ x -> do
let k = x
& Replication.actorId
& CompressedWord.value
& show
& StrictText.pack
let v = x
& Replication.properties
& Map.map (\ value -> Aeson.object
[ "Type" .= getType value
, "Value" .= getValue value
])
(k, v))
& Map.fromList
& Aeson.toJSON
getUpdated :: [Replication.Replication] -> Updated
getUpdated xs = xs
& filter (\ x -> x
& Replication.state
& (== State.SExisting))
& filter (\ x -> x
& Replication.properties
& null
& not)
& Updated
newtype Destroyed = Destroyed [Replication.Replication]
instance Aeson.ToJSON Destroyed where
toJSON (Destroyed xs) = xs
& map Replication.actorId
& map CompressedWord.value
& Aeson.toJSON
getDestroyed :: [Replication.Replication] -> Destroyed
getDestroyed xs = xs
& filter (\ x -> x
& Replication.state
& (== State.SClosing))
& Destroyed
getType :: Value.Value -> StrictText.Text
getType value = case value of
Value.VBoolean _ -> "Boolean"
Value.VByte _ -> "Byte"
Value.VCamSettings _ _ _ _ _ _ -> "CameraSettings"
Value.VDemolish _ _ _ _ _ _ -> "Demolition"
Value.VEnum _ _ -> "Enum"
Value.VExplosion _ _ _ -> "Explosion"
Value.VFlaggedInt _ _ -> "FlaggedInt"
Value.VFloat _ -> "Float"
Value.VGameMode _ -> "GameMode"
Value.VInt _ -> "Int"
Value.VLoadout _ _ _ _ _ _ _ _ _ -> "Loadout"
Value.VLoadoutOnline _ -> "OnlineLoadout"
Value.VLocation _ -> "Position"
Value.VMusicStinger _ _ _ -> "MusicStinger"
Value.VPickup _ _ _ -> "Pickup"
Value.VPrivateMatchSettings _ _ _ _ _ _ -> "PrivateMatchSettings"
Value.VQWord _ -> "QWord"
Value.VRelativeRotation _ -> "RelativeRotation"
Value.VReservation _ _ _ _ _ _ _ -> "Reservation"
Value.VRigidBodyState _ _ _ _ _ -> "RigidBodyState"
Value.VString _ -> "String"
Value.VTeamPaint _ _ _ _ _ -> "Paint"
Value.VUniqueId _ _ _ -> "UniqueId"
getValue :: Value.Value -> Aeson.Value
getValue value = case value of
Value.VBoolean x -> Aeson.toJSON x
Value.VByte x -> Aeson.toJSON x
Value.VCamSettings fov height angle distance stiffness swivelSpeed -> Aeson.object
[ ("FOV", Aeson.toJSON fov)
, ("Height", Aeson.toJSON height)
, ("Angle", Aeson.toJSON angle)
, ("Distance", Aeson.toJSON distance)
, ("Stiffness", Aeson.toJSON stiffness)
, ("SwivelSpeed", Aeson.toJSON swivelSpeed)
]
Value.VDemolish a b c d e f -> Aeson.toJSON (a, b, c, d, e, f)
Value.VEnum x y -> Aeson.toJSON (x, y)
Value.VExplosion a b c -> Aeson.toJSON (a, b, c)
Value.VFlaggedInt x y -> Aeson.toJSON (x, y)
Value.VFloat x -> Aeson.toJSON x
Value.VGameMode gameMode -> Aeson.object
[ ("Id", Aeson.toJSON gameMode)
, ("Name", gameMode & getGameMode & Aeson.toJSON)
]
Value.VInt x -> Aeson.toJSON x
Value.VLoadout version body decal wheels rocketTrail antenna topper x y -> Aeson.object
[ ("Version", Aeson.toJSON version)
, ("Body", Aeson.object
[ ("Id", Aeson.toJSON body)
, ("Name", body & getProduct & Aeson.toJSON)
])
, ("Decal", Aeson.object
[ ("Id", Aeson.toJSON decal)
, ("Name", decal & getProduct & Aeson.toJSON)
])
, ("Wheels", Aeson.object
[ ("Id", Aeson.toJSON wheels)
, ("Name", wheels & getProduct & Aeson.toJSON)
])
, ("RocketTrail", Aeson.object
[ ("Id", Aeson.toJSON rocketTrail)
, ("Name", rocketTrail & getProduct & Aeson.toJSON)
])
, ("Antenna", Aeson.object
[ ("Id", Aeson.toJSON antenna)
, ("Name", antenna & getProduct & Aeson.toJSON)
])
, ("Topper", Aeson.object
[ ("Id", Aeson.toJSON topper)
, ("Name", topper & getProduct & Aeson.toJSON)
])
, ("Unknown1", Aeson.toJSON x)
, ("Unknown2", Aeson.toJSON y)
]
Value.VLoadoutOnline a -> Aeson.toJSON a
Value.VLocation x -> Aeson.toJSON x
Value.VMusicStinger a b c -> Aeson.toJSON (a, b, c)
Value.VPickup a b c -> Aeson.toJSON (a, b, c)
Value.VPrivateMatchSettings mutators joinableBy maxPlayers name password x -> Aeson.object
[ ("Mutators", Aeson.toJSON mutators)
, ("JoinableBy", Aeson.toJSON joinableBy)
, ("MaxPlayers", Aeson.toJSON maxPlayers)
, ("Name", Aeson.toJSON name)
, ("Password", Aeson.toJSON password)
, ("Unknown", Aeson.toJSON x)
]
Value.VQWord x -> Aeson.toJSON x
Value.VRelativeRotation x -> Aeson.toJSON x
Value.VReservation num systemId remoteId localId name x y -> Aeson.object
[ ("Number", Aeson.toJSON num)
, ("SystemId", Aeson.toJSON systemId)
, ("RemoteId", Aeson.toJSON remoteId)
, ("LocalId", Aeson.toJSON localId)
, ("Name", Aeson.toJSON name)
, ("Unknown1", Aeson.toJSON x)
, ("Unknown2", Aeson.toJSON y)
]
Value.VRigidBodyState sleeping position rotation linear angular -> Aeson.object
[ ("Sleeping", Aeson.toJSON sleeping)
, ("Position", Aeson.toJSON position)
, ("Rotation", Aeson.toJSON rotation)
, ("LinearVelocity", Aeson.toJSON linear)
, ("AngularVelocity", Aeson.toJSON angular)
]
Value.VString x -> Aeson.toJSON x
Value.VTeamPaint team color1 color2 finish1 finish2 -> Aeson.object
[ ("Team", Aeson.toJSON team)
, ("PrimaryColor", Aeson.toJSON color1)
, ("AccentColor", Aeson.toJSON color2)
, ("PrimaryFinish", Aeson.object
[ ("Id", Aeson.toJSON finish1)
, ("Name", finish1 & getProduct & Aeson.toJSON)
])
, ("AccentFinish", Aeson.object
[ ("Id", Aeson.toJSON finish2)
, ("Name", finish2 & getProduct & Aeson.toJSON)
])
]
Value.VUniqueId systemId remoteId localId -> Aeson.object
[ ("System", case systemId of
0 -> "Local"
1 -> "Steam"
2 -> "PlayStation"
4 -> "Xbox"
_ -> Aeson.String ("Unknown system " <> StrictText.pack (show systemId)))
, ("Remote", Aeson.toJSON remoteId)
, ("Local", Aeson.toJSON localId)
]
getGameMode :: Word8.Word8 -> Maybe StrictText.Text
getGameMode x = Bimap.lookup (Word8.fromWord8 x) Data.gameModes
getProduct :: Word32.Word32 -> Maybe StrictText.Text
getProduct x = Bimap.lookup (Word32.fromWord32 x) Data.products