[server] Send partial updates when possible
This commit is contained in:
parent
45a6d1934e
commit
b2b34d551a
2 changed files with 51 additions and 14 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
{-# LANGUAGE DeriveGeneric #-}
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
||||||
|
{-# LANGUAGE TupleSections #-}
|
||||||
|
|
||||||
module Forest.Node
|
module Forest.Node
|
||||||
(
|
(
|
||||||
|
|
@ -8,11 +9,15 @@ module Forest.Node
|
||||||
, Node(..)
|
, Node(..)
|
||||||
, newNode
|
, newNode
|
||||||
, txtNode
|
, txtNode
|
||||||
|
, getChild
|
||||||
, hasChildren
|
, hasChildren
|
||||||
, mapChildren
|
, mapChildren
|
||||||
, applyId
|
, applyId
|
||||||
, applyPath
|
, applyPath
|
||||||
|
, alterAt
|
||||||
|
, editAt
|
||||||
, replaceAt
|
, replaceAt
|
||||||
|
, diffNodes
|
||||||
-- * Path
|
-- * Path
|
||||||
, Path(..)
|
, Path(..)
|
||||||
, localPath
|
, localPath
|
||||||
|
|
@ -25,10 +30,10 @@ module Forest.Node
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
import Data.Aeson
|
import Data.Aeson
|
||||||
import Data.Char
|
import Data.Char
|
||||||
import qualified Data.Map.Strict as Map
|
import qualified Data.Map.Strict as Map
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import qualified Data.Set as Set
|
import qualified Data.Set as Set
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import GHC.Generics
|
import GHC.Generics
|
||||||
|
|
||||||
{- Node -}
|
{- Node -}
|
||||||
|
|
@ -42,7 +47,8 @@ data Node = Node
|
||||||
, nodeReply :: !Bool
|
, nodeReply :: !Bool
|
||||||
, nodeAct :: !Bool
|
, nodeAct :: !Bool
|
||||||
, nodeChildren :: !(Map.Map NodeId Node)
|
, nodeChildren :: !(Map.Map NodeId Node)
|
||||||
} deriving (Show, Generic)
|
}
|
||||||
|
deriving (Show, Generic)
|
||||||
|
|
||||||
nodeOptions :: Options
|
nodeOptions :: Options
|
||||||
nodeOptions = defaultOptions{fieldLabelModifier = map toLower . drop 4}
|
nodeOptions = defaultOptions{fieldLabelModifier = map toLower . drop 4}
|
||||||
|
|
@ -69,6 +75,9 @@ newNode flags text children =
|
||||||
txtNode :: String -> T.Text -> Node
|
txtNode :: String -> T.Text -> Node
|
||||||
txtNode flags text = newNode flags text []
|
txtNode flags text = newNode flags text []
|
||||||
|
|
||||||
|
getChild :: NodeId -> Node -> Maybe Node
|
||||||
|
getChild nodeId node = nodeChildren node Map.!? nodeId
|
||||||
|
|
||||||
hasChildren :: Node -> Bool
|
hasChildren :: Node -> Bool
|
||||||
hasChildren = not . Map.null . nodeChildren
|
hasChildren = not . Map.null . nodeChildren
|
||||||
|
|
||||||
|
|
@ -81,11 +90,35 @@ applyId nodeId node = nodeChildren node Map.!? nodeId
|
||||||
applyPath :: Path -> Node -> Maybe Node
|
applyPath :: Path -> Node -> Maybe Node
|
||||||
applyPath (Path ids) node = foldM (flip applyId) node ids
|
applyPath (Path ids) node = foldM (flip applyId) node ids
|
||||||
|
|
||||||
|
alterChild :: (Maybe Node -> Maybe Node) -> NodeId -> Node -> Node
|
||||||
|
alterChild f nodeId node = node{nodeChildren = Map.alter f nodeId (nodeChildren node)}
|
||||||
|
|
||||||
|
alterAt :: (Maybe Node -> Maybe Node) -> Path -> Node -> Maybe Node
|
||||||
|
alterAt f (Path []) node = f (Just node)
|
||||||
|
alterAt f (Path (x:xs)) node = Just $ alterChild (>>= alterAt f (Path xs)) x node
|
||||||
|
|
||||||
|
editAt :: (Node -> Node) -> Path -> Node -> Node
|
||||||
|
editAt f (Path []) = f
|
||||||
|
editAt f (Path (x:xs)) = alterChild (fmap $ editAt f (Path xs)) x
|
||||||
|
|
||||||
replaceAt :: Node -> Path -> Node -> Node
|
replaceAt :: Node -> Path -> Node -> Node
|
||||||
replaceAt childNode (Path []) _ = childNode
|
replaceAt child = editAt (const child)
|
||||||
replaceAt childNode (Path (x:xs)) node =
|
|
||||||
let newChildren = Map.adjust (replaceAt childNode $ Path xs) x $ nodeChildren node
|
diffNodes :: Node -> Node -> Maybe (Path, Node)
|
||||||
in node{nodeChildren = newChildren}
|
diffNodes a b
|
||||||
|
| nodesDiffer || childrenChanged = Just (Path [], b)
|
||||||
|
| otherwise = case differingChildren of
|
||||||
|
[] -> Nothing
|
||||||
|
[(x, Path xs, node)] -> Just (Path (x:xs), node)
|
||||||
|
_ -> Just (Path [], b)
|
||||||
|
where
|
||||||
|
nodesDiffer = nodeText a /= nodeText b
|
||||||
|
|| any (\f -> f a /= f b) [nodeEdit, nodeDelete, nodeReply, nodeAct]
|
||||||
|
aChildren = nodeChildren a
|
||||||
|
bChildren = nodeChildren b
|
||||||
|
childrenChanged = Map.keysSet aChildren /= Map.keysSet bChildren
|
||||||
|
diffedChildren = Map.toList $ Map.intersectionWith diffNodes aChildren bChildren
|
||||||
|
differingChildren = [(key, path, node) | (key, Just (path, node)) <- diffedChildren]
|
||||||
|
|
||||||
{- Path -}
|
{- Path -}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,16 @@ import Forest.Util
|
||||||
{- Thread that sends updates to the client -}
|
{- Thread that sends updates to the client -}
|
||||||
|
|
||||||
sendUpdatesThread :: WS.Connection -> Chan Node -> Node -> IO ()
|
sendUpdatesThread :: WS.Connection -> Chan Node -> Node -> IO ()
|
||||||
sendUpdatesThread conn nodeChan _ = do
|
sendUpdatesThread conn nodeChan nodeA = do
|
||||||
node' <- readChan nodeChan
|
nodeB <- readChan nodeChan
|
||||||
-- TODO Don't send the whole node every time
|
case diffNodes nodeA nodeB of
|
||||||
putStrLn $ "Sending full node update with " ++ show node'
|
Nothing -> do
|
||||||
sendPacket conn $ ServerUpdate (Path []) node'
|
putStrLn "Sending no update because the node didn't change"
|
||||||
sendUpdatesThread conn nodeChan node'
|
sendUpdatesThread conn nodeChan nodeA
|
||||||
|
Just (path, nextNode) -> do
|
||||||
|
putStrLn $ "Sending partial update for path " ++ show path ++ ": " ++ show nextNode
|
||||||
|
sendPacket conn $ ServerUpdate path nextNode
|
||||||
|
sendUpdatesThread conn nodeChan nodeB
|
||||||
|
|
||||||
{- Main server application that receives and processes client packets -}
|
{- Main server application that receives and processes client packets -}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue