about summary refs log tree commit diff
diff options
context:
space:
mode:
authorsternenseemann <git@lukasepple.de>2019-10-29 21:53:54 +0100
committersternenseemann <git@lukasepple.de>2019-10-29 21:53:54 +0100
commitfdbc5fdfe128a96328b0c3d0395053057171e92f (patch)
treee0ac65f3dbe513cc1778462ae0cb3583bd148442
parent8154de8fda9ec556ce08322eeb9cd0ddb63f7b01 (diff)
add protocol mockup for multiplayer
-rw-r--r--grav2ty.cabal5
-rw-r--r--lib/Grav2ty/Protocol.hs103
-rw-r--r--lib/Grav2ty/Util/Serialization.hs20
3 files changed, 128 insertions, 0 deletions
diff --git a/grav2ty.cabal b/grav2ty.cabal
index 97330c1..b244861 100644
--- a/grav2ty.cabal
+++ b/grav2ty.cabal
@@ -22,13 +22,18 @@ library grav2ty-lib
   exposed-modules:     Grav2ty.Core
                      , Grav2ty.Simulation
                      , Grav2ty.Control
+                     , Grav2ty.Protocol
                      , Grav2ty.Util.RelGraph
+                     , Grav2ty.Util.Serialization
   -- other-extensions:
   build-depends:       base ^>=4.12.0.0
                      , containers ^>=0.6.0.1
                      , linear ^>=1.20.8
                      , lens ^>= 4.17.1
                      , transformers^>=0.5.6.2
+                     , bytestring ^>=0.10.8.2
+                     , flat^>=0.3.4
+                     , attoparsec^>=0.13.2.3
   hs-source-dirs:      lib
   default-language:    Haskell2010
 
diff --git a/lib/Grav2ty/Protocol.hs b/lib/Grav2ty/Protocol.hs
new file mode 100644
index 0000000..7ac828a
--- /dev/null
+++ b/lib/Grav2ty/Protocol.hs
@@ -0,0 +1,103 @@
+{-# LANGUAGE DeriveGeneric  #-}
+{-# LANGUAGE DeriveAnyClass #-}
+module Grav2ty.Protocol where
+
+import Prelude hiding (take)
+
+import Grav2ty.Core
+import Grav2ty.Util.Serialization
+
+import Data.Attoparsec.ByteString
+import Data.Bits
+import Data.ByteString (ByteString (..))
+import qualified Data.ByteString as BS
+import Data.Char
+import Data.Flat
+import Data.Flat.Instances
+import Data.Int
+import Data.Word
+import Linear.V2
+
+data Packet
+  = Packet
+  { pMessageType    :: Word8
+  , pPacketContents :: ByteString
+  } deriving (Show, Eq, Ord)
+
+class ToPacket a where
+  toPacket   :: a -> Packet
+  fromPacket :: Packet -> Maybe a
+
+protocolVersion :: Word8
+protocolVersion = 1
+
+data ErrorType
+  = ErrorServerFull
+  | ErrorVersionMismatch
+  | ErrorNoParse
+  deriving (Show, Eq, Ord, Generic, Flat)
+
+-- | Protocol Version 1
+data Message a
+  = ProtocolVersion Word8
+  | Error ErrorType
+  | AssignMods [Id]
+  | NewWorld Tick (World a)
+  | NewObject Tick Id (Object a)
+  | UpdateMod (ModMap a)
+  deriving (Show, Eq, Ord, Generic, Flat)
+
+toMaybe :: Bool -> a -> Maybe a
+toMaybe True x = Just x
+toMaybe False _ = Nothing
+
+rightToMaybe :: Either e a -> Maybe a
+rightToMaybe (Left _) = Nothing
+rightToMaybe (Right x) = Just x
+
+instance Flat a => ToPacket (Message a) where
+  toPacket (ProtocolVersion v) = Packet 0 (BS.singleton v)
+  toPacket (Error e) = Packet 1 (flat e)
+  toPacket (AssignMods ids) = Packet 2 (flat ids)
+  toPacket (NewWorld tick world) = Packet 3 $ flat (tick, world)
+  toPacket (NewObject tick id obj) = Packet 4 $ flat (tick, id, obj)
+  toPacket (UpdateMod modmap) = Packet 5 (flat modmap)
+  fromPacket (Packet 0 v) = toMaybe (BS.length v == 1) (ProtocolVersion $ BS.head v)
+  fromPacket (Packet 1 e) = Error <$> rightToMaybe (unflat e)
+  fromPacket (Packet 2 m) = AssignMods <$> rightToMaybe (unflat m)
+  fromPacket (Packet 3 w) = case unflat w of
+                              Left _ -> Nothing
+                              Right (tick, world) -> Just $ NewWorld tick world
+  fromPacket (Packet 4 o) = case unflat o of
+                              Left _ -> Nothing
+                              Right (tick, id, obj) -> Just $ NewObject tick id obj
+  fromPacket (Packet 5 m) = UpdateMod <$> rightToMaybe (unflat m)
+  fromPacket (Packet _ _) = Nothing
+
+bytes :: Int64 -> [Word8]
+bytes i = bytes' 7
+  where bytes' :: Int -> [Word8]
+        bytes' (-1) = []
+        bytes' n    = (fromIntegral . (flip shift (-8 * n)) $
+            i .&. shift 0xff (8 * n))
+          : bytes' (n - 1)
+
+unbytes :: [Word8] -> Int64
+unbytes l = unbytes' l 7 0
+  where unbytes' [x] 0 acc    = (fromIntegral x) + acc
+        unbytes' [x] n acc    = shift (fromIntegral x + acc) (-8 * n)
+        unbytes' (x:xs) n acc = unbytes' xs (n - 1) (acc + shift (fromIntegral x) (8 * n))
+
+renderPacket :: Packet -> ByteString
+renderPacket (Packet t content) = BS.pack (t : bytes len) `BS.append` content
+  where len :: Int64
+        len = fromIntegral $ BS.length content
+
+parsePacket :: ByteString -> Result Packet
+parsePacket = parse packetParser
+
+packetParser :: Parser Packet
+packetParser = do
+  t <- anyWord8
+  length <- fromIntegral . unbytes . BS.unpack <$> take 8 -- TODO get rid of unpack
+  Packet t <$> take length
diff --git a/lib/Grav2ty/Util/Serialization.hs b/lib/Grav2ty/Util/Serialization.hs
new file mode 100644
index 0000000..42be957
--- /dev/null
+++ b/lib/Grav2ty/Util/Serialization.hs
@@ -0,0 +1,20 @@
+{-# LANGUAGE StandaloneDeriving #-}
+{-# LANGUAGE DeriveGeneric      #-}
+{-# LANGUAGE DeriveAnyClass     #-}
+module Grav2ty.Util.Serialization where
+
+import Data.Flat
+import GHC.Generics
+import Grav2ty.Core (Hitbox (..), Object (..), Modifier (..), Modification (..))
+import Linear.V2 (V2 (..))
+
+deriving instance Generic (Modification a)
+deriving instance Generic (Hitbox a)
+deriving instance Generic (Object a)
+deriving instance Generic Modifier
+
+deriving instance Flat a => Flat (V2 a)
+deriving instance Flat a => Flat (Object a)
+deriving instance Flat a => Flat (Hitbox a)
+deriving instance Flat a => Flat (Modification a)
+deriving instance Flat Modifier