F# at GameSys

254
F# at GameSys (how we used F# to build games for millions)

Transcript of F# at GameSys

Page 1: F# at GameSys

F# at GameSys!(how we used F# to build games for millions)

Page 2: F# at GameSys

Hi, my name is Yan Cui.

Page 3: F# at GameSys
Page 4: F# at GameSys

agenda

Page 5: F# at GameSys

Domain Modelling!Infrastructure!Game Logic!Algorithms!

DSLs!OSS

Page 6: F# at GameSys

1MILLION USERS

ACTIVEDAILY

Page 7: F# at GameSys

250MILLION DAY

PERREQUEST

Page 8: F# at GameSys

2MONTH

TBP E R

secops25,000

Page 9: F# at GameSys

we are polyglot

C#

Page 10: F# at GameSys

why F#?

Page 11: F# at GameSys

time to market

Page 12: F# at GameSys

correctness

Page 13: F# at GameSys

efficient

Page 14: F# at GameSys

tame complexity

Page 15: F# at GameSys
Page 16: F# at GameSys

Collectables

Wager Size

Special SymbolAvg Wager Size

Web Server call

Page 17: F# at GameSys

• Line Win!– X number of matching symbols on adjacent columns!– Positions have to be a ‘line’!– Wild symbols substitute for other symbols!

!• Scatter Win!– X number of matching symbols anywhere!– Triggers bonus game

Page 18: F# at GameSys

What symbols should land?!Any special symbol wins?!

Did the player win anything?

Page 19: F# at GameSys

What the player’s new average wager?

Page 20: F# at GameSys

Should the player receive collectables?

Page 21: F# at GameSys

type Symbol = Standard! of string!! ! ! | Wild

Page 22: F# at GameSys

type Symbol = Standard! of string!! ! ! | Wild

e.g.! Standard “hat”!! Standard “shoe”!! Standard “bonus”!! …

Page 23: F# at GameSys

type Symbol = Standard! of string!! ! ! | Wild

i.e.! Wild

Page 24: F# at GameSys

!

!

!

type Win = LineWin of int * Symbol * int!! | ScatterWin of Symbol * int

Page 25: F# at GameSys

!

!

!

type Win = LineWin of int * Symbol * int!! | ScatterWin of Symbol * int

e.g.! LineWin (5, Standard “shoe”, 4)

Page 26: F# at GameSys

!

!

!

type Win = LineWin of int * Symbol * int!! | ScatterWin of Symbol * int

e.g.! ScatterWin (Standard “bonus”, 3)

Page 27: F# at GameSys

type LineNum = int!type Count! = int!!

type Win = LineWin of LineNum * Symbol * Count!! | ScatterWin of Symbol * Count

Page 28: F# at GameSys

type LineNum = int!type Count! = int!!

type Win = LineWin of LineNum * Symbol * Count!! | ScatterWin of Symbol * Count

e.g.! LineWin (5, Standard “shoe”, 4)

Page 29: F# at GameSys

type LineNum = int!type Count! = int!!

type Win = LineWin of LineNum * Symbol * Count!! | ScatterWin of Symbol * Count

e.g.! ScatterWin (Standard “bonus”, 3)

Page 30: F# at GameSys

make invalid states unrepresentable

Page 31: F# at GameSys

[<Measure>]!type Pence

e.g.! 42<Pence>!! 153<Pence>!! …

Page 32: F# at GameSys

10<Meter> / 2<Second> ! = 5<Meter/Second>!10<Meter> * 2<Second> ! = 20<Meter Second> !10<Meter> + 10<Meter> ! = 20<Meter>!10<Meter> * 10! ! ! = 100<Meter>!10<Meter> * 10<Meter> ! = 100<Meter2>!10<Meter> + 2<Second> ! // error!10<Meter> + 2 ! ! ! // error

Page 33: F# at GameSys

10<Meter> / 2<Second> ! = 5<Meter/Second>!10<Meter> * 2<Second> ! = 20<Meter Second> !10<Meter> + 10<Meter> ! = 20<Meter>!10<Meter> * 10! ! ! = 100<Meter>!10<Meter> * 10<Meter> ! = 100<Meter2>!10<Meter> + 2<Second> ! // error!10<Meter> + 2 ! ! ! // error

Page 34: F# at GameSys

type Wager = int64<Pence>!type Multiplier = int!!

type Payout = Coins! of Wager!! ! | MultipliedCoins of Multiplier * Wager!! ! | Multi! of Payout list!! ! | BonusGame

Page 35: F# at GameSys

type Wager = int64<Pence>!type Multiplier = int!!

type Payout = Coins! of Wager!! ! | MultipliedCoins of Multiplier * Wager!! ! | Multi! of Payout list!! ! | BonusGame

Page 36: F# at GameSys

type Wager = int64<Pence>!type Multiplier = int!!

type Payout = Coins! of Wager!! ! | MultipliedCoins of Multiplier * Wager!! ! | Multi! of Payout list!! ! | BonusGame

Page 37: F# at GameSys

type State = ! {!

AvgWager! : Wager!SpecialSymbol : Symbol!Collectables : Map<Collectable, Count>!

}

Page 38: F# at GameSys
Page 39: F# at GameSys

New avg wager Got a Collectable!

A pay line win!

Page 40: F# at GameSys
Page 41: F# at GameSys

Recap

Page 42: F# at GameSys

lightweight syntax for types & hierarchies

Page 43: F# at GameSys

great for domain modelling

Page 44: F# at GameSys

make invalid states unrepresentable

Page 45: F# at GameSys

better correctness

Page 46: F# at GameSys

order of magnitude increase in productivity

Page 47: F# at GameSys
Page 48: F# at GameSys
Page 49: F# at GameSys

player states are big

Page 50: F# at GameSys

Stateless Server DatabaseClient

Page 51: F# at GameSys

1:1 read-write ratio

Page 52: F# at GameSys
Page 53: F# at GameSys

ServerClient

Session 1

Session 2

Page 54: F# at GameSys

ServerClient Database

Page 55: F# at GameSys

Elastic Load Balancer

S3

Auto scaling Group

Server A Server B

...

EC2

CloudFront

Stateful Server

Page 56: F# at GameSys

The Actor Model

An actor is the fundamental unit of computation which embodies the 3 things!

• Processing!• Storage!• Communication!

that are essential to computation.!!

-Carl Hewitt** http://bit.ly/HoNHbG

Page 57: F# at GameSys

The Actor Model

• Everything is an actor!• An actor has a mailbox!• When an actor receives a message it can:!– Create new actors!– Send messages to actors!– Designate how to handle the next message

Page 58: F# at GameSys

Stateful Server

• Gatekeeper!– Manages the local list of active workers!– Spawns new workers!

!• Worker!– Manages the states for a player!– Optimistic locking!– Persist state after period of inactivity

Page 59: F# at GameSys

Stateful Server

Game Server

Player A

Player B

S3Worker C

Worker B

GatekeeperR

eque

st H

andl

ers

Asynchronous

Page 60: F# at GameSys

Stateful Server

Game Server

Worker C

Worker B

Gatekeeper

Worker Aok

Req

uest

Han

dler

sPlayer A

Player B

Asynchronous

S3

Page 61: F# at GameSys

Stateful Server

Game Server

S3Worker C

Worker B

Gatekeeper

Worker AR

eque

st H

andl

ers

Player A

Player B

Asynchronous

Page 62: F# at GameSys

Stateful Server

Game Server

S3Worker C

Gatekeeper

Worker AR

eque

st H

andl

ers

Player A

Player B

Asynchronous

Page 63: F# at GameSys

Stateful Server

Game Server

Worker C

Worker A

Gatekeeper

error

Req

uest

Han

dler

sPlayer A

Player B

S3

Asynchronous

Page 64: F# at GameSys

type Agent<‘T> = MailboxProcessor<‘T>!!!!

Page 65: F# at GameSys

type Agent<‘T> = MailboxProcessor<‘T>!!type Message = ! | Get of AsyncReplyChannel<…>! | Put of State * Version * AsyncReplyChannel<…>

Page 66: F# at GameSys

type Agent<‘T> = MailboxProcessor<‘T>!!type Message = ! | Get of AsyncReplyChannel<…>! | Put of State * Version * AsyncReplyChannel<…>

Page 67: F# at GameSys

type Result<‘T> =! | Success! of ’T! | Failure! of Exception!!!!

Page 68: F# at GameSys

type Result<‘T> =! | Success! of ’T! | Failure! of Exception!!type GetResult = Result<State * Version>!type PutResult = Result<unit>!

Page 69: F# at GameSys

type Agent<‘T> = MailboxProcessor<‘T>!!type Message = ! | Get of AsyncReplyChannel<GetResult>! | Put of State * Version * AsyncReplyChannel<PutResult>

Page 70: F# at GameSys

type Agent<‘T> = MailboxProcessor<‘T>!!type Message = ! | Get of AsyncReplyChannel<GetResult>! | Put of State * Version * AsyncReplyChannel<PutResult>

Page 71: F# at GameSys

type Worker (playerId) =! let agent = Agent<Message>.Start(fun inbox ->! let state = getCurrentState playerId!! let rec workingState (state, version) = ! async { … }! and closedState () =! async { … }!! workingState (state, 1))

Page 72: F# at GameSys

type Worker (playerId) =! let agent = Agent<Message>.Start(fun inbox ->! let state = getCurrentState playerId!! let rec workingState (state, version) = ! async { … }! and closedState () =! async { … }!! workingState (state, 1))

Page 73: F# at GameSys

type Worker (playerId) =! let agent = Agent<Message>.Start(fun inbox ->! let state = getCurrentState playerId!! let rec workingState (state, version) = ! async { … }! and closedState () =! async { … }!! workingState (state, 1))

Page 74: F# at GameSys

type Worker (playerId) =! let agent = Agent<Message>.Start(fun inbox ->! let state = getCurrentState playerId!! let rec workingState (state, version) = ! async { … }! and closedState () =! async { … }!! workingState (state, 1))

Page 75: F# at GameSys

type Worker (playerId) =! let agent = Agent<Message>.Start(fun inbox ->! let state = getCurrentState playerId!! let rec workingState (state, version) = ! async { … }! and closedState () =! async { … }!! workingState (state, 1))

Page 76: F# at GameSys

let rec workingState (state, version) = ! async { ! let! msg = inbox.TryReceive(60000)! match msg with! | None -> ! do! persist state! return! closedState()! …! }!

Page 77: F# at GameSys

let rec workingState (state, version) = ! async { ! let! msg = inbox.TryReceive(60000)! match msg with! | None -> ! do! persist state! return! closedState()! …! }!

Page 78: F# at GameSys

let rec workingState (state, version) = ! async { ! let! msg = inbox.TryReceive(60000)! match msg with! | None -> ! do! persist state! return! closedState()! …! }!

Page 79: F# at GameSys

non-blocking I/O

Page 80: F# at GameSys

let rec workingState (state, version) = ! async { ! let! msg = inbox.TryReceive(60000)! match msg with! | None -> ! do! persist state! return! closedState()! …! }!

Page 81: F# at GameSys

let rec workingState (state, version) = ! async { ! let! msg = inbox.TryReceive(60000)! match msg with! | None -> ! do! persist state! return! closedState()! …! }!

Page 82: F# at GameSys

let rec workingState (state, version) = ! async { ! let! msg = inbox.TryReceive(60000)! match msg with! …! | Some(Get(reply)) ->! reply.Reply <| Success(state, version)! return! workingState(state, version)! …! }!

Page 83: F# at GameSys

let rec workingState (state, version) = ! async { ! let! msg = inbox.TryReceive(60000)! match msg with! …! | Some(Put(newState, v, reply)) when version = v ->! reply.Reply <| Success()! return! workingState(newState, version+1)! …! }

Page 84: F# at GameSys

let rec workingState (state, version) = ! async { ! let! msg = inbox.TryReceive(60000)! match msg with! …! | Some(Put(_, v, reply)) ->! reply.Reply <| Failure(VersionMismatch(version, v))! return! workingState(state, version)! }

Page 85: F# at GameSys

let rec workingState (state, version) = ! async { ! let! msg = inbox.TryReceive(60000)! match msg with! | None ! ! ! ! ! ! -> …! | Some(Get(reply)) ! ! ! ! -> …! | Some(Put(newState, v, reply)) when version = v -> …! | Some(Put(_, v, reply)) ! ! ! -> …! }

Page 86: F# at GameSys

and closedState () = ! async { ! let! msg = inbox.Receive()! match msg with! | Get(reply) ->! reply.Reply <| Failure(WorkerStopped)! return! closedState()! | Put(_, _, reply) ->! reply.Reply <| Failure(WorkerStopped)! return! closedState()! }

Page 87: F# at GameSys

and closedState () = ! async { ! let! msg = inbox.Receive()! match msg with! | Get(reply) ->! reply.Reply <| Failure(WorkerStopped)! return! closedState()! | Put(_, _, reply) ->! reply.Reply <| Failure(WorkerStopped)! return! closedState()! }

Page 88: F# at GameSys
Page 89: F# at GameSys
Page 90: F# at GameSys

5x efficient improvement

Page 91: F# at GameSys

60% latency drop

Page 92: F# at GameSys

no databases

Page 93: F# at GameSys

90% cost saving

Page 94: F# at GameSys
Page 95: F# at GameSys

need to ensure server affinity

Page 96: F# at GameSys

need to balance load

Page 97: F# at GameSys

need to avoid hotspots

Page 98: F# at GameSys

• Persist player state after short inactivity!• Move player to another server after persistence

Page 99: F# at GameSys

need to gracefully scale down

Page 100: F# at GameSys

Recap

Page 101: F# at GameSys

Agents!• no locks!• async message passing!

!

Page 102: F# at GameSys

Agents!• no locks!• async message passing!• self-contained!• easy to reason with

Page 103: F# at GameSys

Agents!• low level!• exceptions kills agents!• primitive error monitoring!• no supervision!• no distribution

Page 104: F# at GameSys

MS Orleans

Cricket

akka.Net

Page 105: F# at GameSys
Page 106: F# at GameSys

Async Workflow!• non-blocking I/O!• no callbacks

Page 107: F# at GameSys

Pattern Matching!• clear & concise

Page 108: F# at GameSys
Page 109: F# at GameSys
Page 110: F# at GameSys

Caught a Gnome

Page 111: F# at GameSys

EXP Item Gold

Quest Progress

Caught a Gnome

Page 112: F# at GameSys

Level Up

Quest Progress

EXP Item Gold

Caught a Gnome

Quest Complete

Page 113: F# at GameSys

Level Up

Quest Progress

EXP Item Gold

Caught a Gnome

New Quest

Quest Complete

Page 114: F# at GameSys

Quest Progress

EXP Item Gold

Caught a Gnome

Quest Complete

New Quest

Level Up

Page 115: F# at GameSys

Quest Progress

New QuestAchievement

Progress

EXP Item Gold

Quest CompleteLevel Up

Caught a Gnome

Page 116: F# at GameSys

Level Up

Quest Progress

EXP Item Gold

Caught a Gnome

Quest Complete

New QuestAchievement

Progress

Achievement Complete

Page 117: F# at GameSys

Level Up

Quest Progress

EXP Item Gold

Caught a Gnome

Quest Complete

New QuestAchievement

Progress

Achievement Complete

Page 118: F# at GameSys

100+ actions

Page 119: F# at GameSys
Page 120: F# at GameSys

triggered by different abstraction layers

Page 121: F# at GameSys

non-functional requirements

Page 122: F# at GameSys
Page 123: F# at GameSys

message-broker pattern

Page 124: F# at GameSys

Caught Gnome Trapping

Queue

Levelling

Quests

Achievements

Analytics

Partner Reporting

Page 125: F# at GameSys

Caught Gnome Trapping

Queue

Levelling

Quests

Achievements

Analytics

Partner Reporting

Ignore

Process

Process

Process

Process

Ignore

Page 126: F# at GameSys

Caught Gnome Trapping

Queue

Levelling

Quests

Achievements

Analytics

Partner Reporting

EXPItemGold

Page 127: F# at GameSys

Caught Gnome Trapping

Queue

Levelling

Quests

Achievements

Analytics

Partner Reporting

EXPItemGold

Page 128: F# at GameSys

Caught Gnome Trapping

Queue

Levelling

Quests

Achievements

Analytics

Partner Reporting

EXPItemGold

Process

Ignore

Ignore

Ignore

Process

Ignore

Page 129: F# at GameSys

Caught Gnome Trapping

Queue

Levelling

Quests

Achievements

Analytics

Partner Reporting

EXPItemGold

Level Up

Page 130: F# at GameSys

Caught Gnome Trapping

Queue

Levelling

Quests

Achievements

Analytics

Partner Reporting

EXPItemGold

Level Up

Page 131: F# at GameSys

need lots of facts

Page 132: F# at GameSys

type Fact =! | GotExp! ! of Exp! | GotGold! ! of Gold! | GotItem! ! of Item * Count! | CaughtMonster! of Monster * Bait * Location! | LevelUp! ! of OldLevel * NewLevel! …

Page 133: F# at GameSys

type Reward =! | GotExp! ! of Exp! | GotGold! ! of Gold! | GotItem! ! of Item * Count!!

type StateChange =! | LevelUp! ! of OldLevel * NewLevel! …

Page 134: F# at GameSys

type Fact =! | StateChange! of StateChange! | Reward! ! of Reward! | Trapping! ! of Trapping! | Fishing! ! of Fishing! …

Page 135: F# at GameSys

let process fact =! match fact with! | StateChange(stateChange)! -> …! | Reward(reward)! ! ! -> …! | Trapping(trapping)! ! -> …! …

Page 136: F# at GameSys

C# interop

Page 137: F# at GameSys

…!var fact = Fact.NewStateChange(!! StateChange.NewLevelUp(oldLvl, newLvl));!…

Page 138: F# at GameSys

type IFact = interface end!!

type Reward =! | GotExp! ! of Exp! | GotGold! ! of Gold! | GotItem! ! of Item * Count! interface IFact

Page 139: F# at GameSys

type IFact = interface end!!

type Reward =! | GotExp! ! of Exp! | GotGold! ! of Gold! | GotItem! ! of Item * Count! interface IFact

Page 140: F# at GameSys

let process (fact : IFact) =! match fact with! | :? StateChange ! as stateChange!-> …! | :? Reward ! ! as reward! ! -> …! | :? Trapping! ! as trapping! -> …! …! | _ -> raise <| NotSupportedFact fact

Page 141: F# at GameSys

let process (fact : IFact) =! match fact with! | :? StateChange ! as stateChange!-> …! | :? Reward ! ! as reward! ! -> …! | :? Trapping! ! as trapping! -> …! …! | _ -> raise <| NotSupportedFact fact

Page 142: F# at GameSys

simple

Page 143: F# at GameSys

flexible

Page 144: F# at GameSys

saver

Page 145: F# at GameSys
Page 146: F# at GameSys

location baitattraction rate

catch rate

Page 147: F# at GameSys

auto-tuning trapping stats

Page 148: F# at GameSys

Monsterstrength speed

intelligence

Trapstrength speed

technology

Page 149: F# at GameSys

Monsterstrength speed

intelligence

Trapstrength speed

technology

Catch Rate %

Page 150: F# at GameSys

trial-and-error

Page 151: F# at GameSys
Page 152: F# at GameSys
Page 153: F# at GameSys

genetic algorithms

Page 154: F# at GameSys
Page 155: F# at GameSys
Page 156: F# at GameSys
Page 157: F# at GameSys
Page 158: F# at GameSys
Page 159: F# at GameSys
Page 160: F# at GameSys
Page 161: F# at GameSys
Page 162: F# at GameSys
Page 163: F# at GameSys
Page 164: F# at GameSys
Page 165: F# at GameSys

F#-powered DSLs.!Awesome!

Page 166: F# at GameSys
Page 167: F# at GameSys
Page 168: F# at GameSys

wanna find correlations?

Page 169: F# at GameSys

wanna find correlations?

you can DIY it! !

;-)

Page 170: F# at GameSys

why was payment service slow last night?

Page 171: F# at GameSys
Page 172: F# at GameSys
Page 173: F# at GameSys

Pain Driven Development

Page 174: F# at GameSys

Amazon!! .CloudWatch!! .Selector

github.com/fsprojects/Amazon.CloudWatch.Selector

Page 175: F# at GameSys

Find metrics whose 5 min average exceeded

1 second during last 12 hours

Page 176: F# at GameSys

cloudWatch.Select( unitIs “milliseconds” + average (>) 1000.0 @ last 12 hours |> intervalOf 5 minutes)

Page 177: F# at GameSys

cloudWatch.Select(“ unitIs ‘milliseconds’ and average > 1000.0 duringLast 12 hours at intervalOf 5 minutes”)

Page 178: F# at GameSys

did any cache nodes’ CPU spike

yesterday?

Page 179: F# at GameSys

cloudWatch.Select( namespaceLike “elasticache” + nameLike “cpu” + max (>) 80.0 @ last 24 hours |> intervalOf 15 minutes)

Page 180: F# at GameSys

cloudWatch.Select( namespaceLike “elasticache” + nameLike “cpu” + max (>) 80.0 @ last 24 hours |> intervalOf 15 minutes)

regex support

Page 181: F# at GameSys
Page 182: F# at GameSys
Page 183: F# at GameSys
Page 184: F# at GameSys
Page 185: F# at GameSys
Page 186: F# at GameSys

Amazing

F# =

Page 187: F# at GameSys

usable from anywhere you can run F# code

e.g. F# REPL, executable, ..

Internal DSL

Page 188: F# at GameSys

useful for building tools e.g. CLI, …

External DSL

Page 189: F# at GameSys
Page 190: F# at GameSys
Page 191: F# at GameSys
Page 192: F# at GameSys

Recap

Page 193: F# at GameSys
Page 194: F# at GameSys
Page 195: F# at GameSys
Page 196: F# at GameSys

managed!key-value store

Page 197: F# at GameSys

redundancy!9-9s guarantee

Page 198: F# at GameSys

great performance

Page 199: F# at GameSys

name your throughput

Page 200: F# at GameSys
Page 201: F# at GameSys

hash key + range key

Page 202: F# at GameSys
Page 203: F# at GameSys

hash_key_1

hash_key_2

hash_key_3

range_key_1

range_key_2

range_key_1

range_key_2

range_key_1

range_key_2

Page 204: F# at GameSys

Hash Key Range Key

Page 205: F# at GameSys

Hash Key Range Key

Local Secondary Index

Page 206: F# at GameSys

Hash Key Range Key

Local Secondary Index

Global Secondary Index

Page 207: F# at GameSys

query:!! given a hash key!! filter on!! ! range key, or!! ! local secondary index

Page 208: F# at GameSys
Page 209: F# at GameSys

Hash Key Range Key

Local Secondary Index

Global Secondary Index

Who are the TOP 3 players in “Starship X” with a score of at least 1000?

Page 210: F# at GameSys
Page 211: F# at GameSys
Page 212: F# at GameSys

DynamoDB.SQL

github.com/fsprojects/DynamoDb.SQL

Page 213: F# at GameSys

GOAL

Disguise complexity

Page 214: F# at GameSys

GOAL

SELECT UserId, TopScore FROM GameScore WHERE GameTitle CONTAINS “Zelda” ORDER DESC!LIMIT !! 3 WITH (NoConsistentRead)

Page 215: F# at GameSys

Query

AST

Execution

Page 216: F# at GameSys

F# & FParsec*

*www.quanttec.com/fparsec

External DSL via

Page 217: F# at GameSys
Page 218: F# at GameSys

SELECT * FROM GameScore

Abstract Syntax Tree (AST)

FParsec

Page 219: F# at GameSys
Page 220: F# at GameSys

select GameTitle, UserId, TopScore from GameScores where GameTitle = “Starship X” and TopScore >= 1000 order desc limit 3 with (NoConsistentRead, Index(GameTitleIndex, true))

Page 221: F# at GameSys

S3 Provider

github.com/fsprojects/S3Provider

F# type provider for Amazon S3

Page 222: F# at GameSys
Page 223: F# at GameSys
Page 224: F# at GameSys

compile time validation

Page 225: F# at GameSys

no code generation

Page 226: F# at GameSys

a long time ago, in a language far far away from .Net - Erlang.

there was an idea to let you pattern match again binary

payloads and work with them at a bit level…

Page 227: F# at GameSys

<<SourcePort:16, DestinationPort:16, ! SeqNumber:32,! AckNumber:32, DataOffset:4, ! Reserved:3, Flags:9, WindSize:16,! CheckSum:16, UrgentPointer:16,! Payload/binary>> = SomeBinary

Page 228: F# at GameSys

<<SourcePort:16, DestinationPort:16, ! SeqNumber:32,! AckNumber:32, DataOffset:4, ! Reserved:3, Flags:9, WindSize:16,! CheckSum:16, UrgentPointer:16,! Payload/binary>> = SomeBinary

Page 229: F# at GameSys

<<SourcePort:16, DestinationPort:16, ! SeqNumber:32,! AckNumber:32, DataOffset:4, ! Reserved:3, Flags:9, WindSize:16,! CheckSum:16, UrgentPointer:16,! Payload/binary>> = SomeBinary

Page 230: F# at GameSys

<<SourcePort:16, DestinationPort:16, ! SeqNumber:32,! AckNumber:32, DataOffset:4, ! Reserved:3, Flags:9, WindSize:16,! CheckSum:16, UrgentPointer:16,! Payload/binary>> = SomeBinary

Page 231: F# at GameSys

<<SourcePort:16, DestinationPort:16, ! SeqNumber:32,! AckNumber:32, DataOffset:4, ! Reserved:3, Flags:9, WindSize:16,! CheckSum:16, UrgentPointer:16,! Payload/binary>> = SomeBinary

Page 232: F# at GameSys

<<SourcePort:16, DestinationPort:16, ! SeqNumber:32,! AckNumber:32, DataOffset:4, ! Reserved:3, Flags:9, WindSize:16,! CheckSum:16, UrgentPointer:16,! Payload/binary>> = SomeBinary

Page 233: F# at GameSys
Page 234: F# at GameSys

BitSyntax

github.com/theburningmonk/bitsyntax

Working with Stream at bit level

Page 235: F# at GameSys

do bitWriter stream {! do! BitWriter.WriteInt16(12345s) ! // source port! do! BitWriter.WriteInt16(12321s)! // destination port! do! BitWriter.WriteInt32(1) ! // sequence number! do! BitWriter.WriteInt32(1) ! // ack number! do! BitWriter.WriteInt32(2, numBits = 4) // data offset! do! BitWriter.WriteInt32(0, numBits = 3) // reserved! do! BitWriter.WriteBool(false) ! // NS! …! do! BitWriter.WriteInt16(42s)! ! // Checksum! do! BitWriter.WriteInt16(1208s)! // Urgent Pointer! do! BitWriter.WriteString("Hello World!") // payload!}

Page 236: F# at GameSys

do bitWriter stream {! do! BitWriter.WriteInt16(12345s) ! // source port! do! BitWriter.WriteInt16(12321s)! // destination port! do! BitWriter.WriteInt32(1) ! // sequence number! do! BitWriter.WriteInt32(1) ! // ack number! do! BitWriter.WriteInt32(2, numBits = 4) // data offset! do! BitWriter.WriteInt32(0, numBits = 3) // reserved! do! BitWriter.WriteBool(false) ! // NS! …! do! BitWriter.WriteInt16(42s)! ! // Checksum! do! BitWriter.WriteInt16(1208s)! // Urgent Pointer! do! BitWriter.WriteString("Hello World!") // payload!}

16 bits

Page 237: F# at GameSys

do bitWriter stream {! do! BitWriter.WriteInt16(12345s) ! // source port! do! BitWriter.WriteInt16(12321s)! // destination port! do! BitWriter.WriteInt32(1) ! // sequence number! do! BitWriter.WriteInt32(1) ! // ack number! do! BitWriter.WriteInt32(2, numBits = 4) // data offset! do! BitWriter.WriteInt32(0, numBits = 3) // reserved! do! BitWriter.WriteBool(false) ! // NS! …! do! BitWriter.WriteInt16(42s)! ! // Checksum! do! BitWriter.WriteInt16(1208s)! // Urgent Pointer! do! BitWriter.WriteString("Hello World!") // payload!}

32 bits

Page 238: F# at GameSys

do bitWriter stream {! do! BitWriter.WriteInt16(12345s) ! // source port! do! BitWriter.WriteInt16(12321s)! // destination port! do! BitWriter.WriteInt32(1) ! // sequence number! do! BitWriter.WriteInt32(1) ! // ack number! do! BitWriter.WriteInt32(2, numBits = 4) // data offset! do! BitWriter.WriteInt32(0, numBits = 3) // reserved! do! BitWriter.WriteBool(false) ! // NS! …! do! BitWriter.WriteInt16(42s)! ! // Checksum! do! BitWriter.WriteInt16(1208s)! // Urgent Pointer! do! BitWriter.WriteString("Hello World!") // payload!}

take ’first’ N bits

Page 239: F# at GameSys
Page 240: F# at GameSys

bitReader stream {! let! srcPort ! = BitReader.ReadInt32(numBits = 16)! let! destPort ! = BitReader.ReadInt32(numBits = 16)! let! seqNum ! = BitReader.ReadInt32(numBits = 32)! let! ackNum ! = BitReader.ReadInt32(numBits = 32)! let! dataOffset ! = BitReader.ReadInt32(numBits = 4)! let! _ ! = BitReader.ReadByte(numBits = 3)! …! let! payload ! = BitReader.Rest(UTF8.GetString)!! return srcPort, destPort, … payload!}

Page 241: F# at GameSys

bitReader stream {! let! srcPort ! = BitReader.ReadInt32(numBits = 16)! let! destPort ! = BitReader.ReadInt32(numBits = 16)! let! seqNum ! = BitReader.ReadInt32(numBits = 32)! let! ackNum ! = BitReader.ReadInt32(numBits = 32)! let! dataOffset ! = BitReader.ReadInt32(numBits = 4)! let! _ ! = BitReader.ReadByte(numBits = 3)! …! let! payload ! = BitReader.Rest(UTF8.GetString)!! return srcPort, destPort, … payload!}

packs into 32 bit int

Page 242: F# at GameSys

Byte1!Byte2!00000000!00000000

memory address

Byte1!Byte2!

Page 243: F# at GameSys

bitReader stream {! let! srcPort ! = BitReader.ReadInt32(numBits = 16)! let! destPort ! = BitReader.ReadInt32(numBits = 16)! let! seqNum ! = BitReader.ReadInt32(numBits = 32)! let! ackNum ! = BitReader.ReadInt32(numBits = 32)! let! dataOffset ! = BitReader.ReadInt32(numBits = 4)! let! _ ! = BitReader.ReadByte(numBits = 3)! …! let! payload ! = BitReader.Rest(UTF8.GetString)!! return srcPort, destPort, … payload!}

packs into a byte

Page 244: F# at GameSys

0 1 0 1 1 0 1 1

…!let! dataOffset = BitReader.ReadInt32(numBits = 4)!…

Page 245: F# at GameSys

0 1 0 1 1 0 1 1

…!let! dataOffset = BitReader.ReadInt32(numBits = 4)!…

0 1 1 0 1 1 0 0

<<< 2

Page 246: F# at GameSys

0 1 0 1 1 0 1 1

…!let! dataOffset = BitReader.ReadInt32(numBits = 4)!…

0 1 1 0 1 1 0 0

<<< 2

0 0 0 0 0 1 1 0

>>> 4

Page 247: F# at GameSys

0 0 0 0 0 1 1 0

…!let! dataOffset = BitReader.ReadInt32(numBits = 4)!…

6uy

Page 248: F# at GameSys

…!let! dataOffset = BitReader.ReadInt32(numBits = 4)!…

6uy

6

padding padding padding byte

0 0 0 0 0 1 1 0

Page 249: F# at GameSys

1 0 0 1 0 0 0 1

…!let! dataOffset = BitReader.ReadInt32(numBits = 4)!…

1 0 0 0 1 0 1 1

Page 250: F# at GameSys

1 0 0 1 0 0 0 1

…!let! dataOffset = BitReader.ReadInt32(numBits = 17)!…

1 0 0 0 1 0 1 1

1 1 0 1 1 0 1 0

0 1 0 0 1 0 0 0

Page 251: F# at GameSys
Page 252: F# at GameSys

Filbert

github.com/fsprojects/Filbert

BERT serializer and BERT-RPC client

Page 253: F# at GameSys
Page 254: F# at GameSys

@theburningmonktheburningmonk.comgithub.com/theburningmonk