-
Notifications
You must be signed in to change notification settings - Fork 568
/
Copy pathr-tokenmanager.coffee
169 lines (155 loc) · 4.76 KB
/
r-tokenmanager.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
_ = require 'underscore'
Promise = require 'bluebird'
moment = require 'moment'
crypto = require 'crypto'
uuid = require 'node-uuid'
warlock = require '@counterplay/warlock'
Logger = require '../../app/common/logger.coffee'
config = require '../../config/config.js'
GameType = require '../../app/sdk/gameType.coffee'
env = config.get("env")
# Returns the Redis key prefix used
keyPrefix = () ->
return "#{env}:matchmaking:tokens"
###*
# Generate a 'matchmaking' token id
# id is returned the client for listening to errors on (via Firebase)
# @return {String} url safe token id
###
createTokenId = () ->
id = new Buffer(uuid.v4()).toString('base64')
id.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=/g, "")
return id
###*
# Class 'RedisTokenManager'
# Manages player 'matchmaking' tokens in Redis
# Each player has only a single token in existance at any given time
# Tokens are stored as hashes (objects) in Redis
###
class RedisTokenManager
###*
# Constructor
# @param {Object} redis, a promisified redis connection
###
constructor: (redis) ->
# TODO: add check to ensure Redis client is already promisified
@redis = redis
@locker = Promise.promisifyAll(warlock(redis))
return
###*
# Generate a 'matchmaking' token object which will be stored in redis
# We can set some defaults here or add extra stuff
# @param {Object} options
# @return {Object} matchmaking token object
###
create: (opts = {}) ->
token = {}
token.id = createTokenId()
token.createdAt = Date.now()
token.userId = opts.userId
token.name = opts.name
token.rank = opts.rank
token.deck = opts.deck
token.factionId = opts.factionId
token.gameType = opts.gameType or GameType.Ranked
token.inviteId = opts.inviteId or null
token.cardBackId = opts.cardBackId or null
token.deckValue = opts.deckValue or 0
token.lastOpponentId = opts.lastOpponentId or null
token.ticketId = opts.ticketId or null
if opts.battleMapIndexes
token.battleMapIndexes = opts.battleMapIndexes
token.riftRating = opts.riftRating or null
return token
###*
# Save the token to Redis
# We can set some defaults here or add extra stuff
# @param {Object} token
# @return {Promise} Redis 'OK'
###
add: (token) ->
playerId = token.userId
tokenKey = keyPrefix() + playerId
if token.deck? then token.deck = JSON.stringify(token.deck)
if token.battleMapIndexes? then token.battleMapIndexes = JSON.stringify(token.battleMapIndexes)
return @redis.hmsetAsync(tokenKey, token)
###*
# Remove the token or tokens from Redis in single op
# @param {String|Array} playerId or array of playerIds
# @return {Promise} number of elements removed
###
remove: (playerIds) ->
args = []
if _.isArray(playerIds)
_.each playerIds, (playerId) ->
tokenKey = keyPrefix() + playerId
args.push(tokenKey)
else
tokenKey = keyPrefix() + playerIds
args.push(tokenKey)
return @redis.delAsync(args)
###*
# Does the player have a token
# ie. they are waiting for a game (queue or friendly)
# @param {String} playerId
# @return {Promise} true or false
###
exists: (playerId) ->
tokenKey = keyPrefix() + playerId
return @redis.existsAsync(tokenKey)
###*
# Get the token object from Redis
# @param {String} playerId
# @return {Promise} token object
###
get: (playerId) ->
tokenKey = keyPrefix() + playerId
@redis.hgetallAsync(tokenKey) # return entire token object
.then (token) ->
# TODO: There might be other data that we want to convert to correct format here
if token?
if token.deck?
token.deck = JSON.parse(token.deck)
if token.battleMapIndexes?
token.battleMapIndexes = JSON.parse(token.battleMapIndexes)
return token
else
return null
###*
# Get the token's id from Redis
# @param {String} playerId
# @return {Promise} token id
###
getId: (playerId) ->
tokenKey = keyPrefix() + playerId
return @redis.hgetAsync(tokenKey,"id") # return token id only
###*
# Get the token's parameter (deck, rank, etc) from Redis
# @param {String} playerId
# @param {String} the parameter we want to retrieve
# @return {Promise} token id
###
getParameter: (playerId, param) ->
tokenKey = keyPrefix() + playerId
return @redis.hgetAsync(tokenKey,param) # return specified token param only
###*
# Lock the player's token to signal it is in use
# @param {String} playerId
# @param {Integer} ttl (in seconds) on the lock
# @return {Promise} unlock function if lock acquired
###
lock: (playerId, ttl = 5000) ->
return @locker.lockAsync(playerId, ttl)
###*
# Check if a player's token is locked
# @param {String} playerId
# @return {Promise} bool if lock is set
###
isLocked: (playerId) ->
return @locker.isLockedAsync(playerId)
###*
# Export a factory
###
module.exports = exports = (redis, opts) ->
TokenManager = new RedisTokenManager(redis, opts)
return TokenManager