Following is an outline of the design process used for the web backed for Ananias Roguelike
Step 1 – Rough Draft
Make a rough draft including the features intended to be supported in the game and the potential integration points to be required.
Step 2 – Architecture
Decide on the architecture and the technology stack.
Step 3 – API Blueprint
Clean up and design an API blueprint.
Anon Services
These require no auth token
POST /player
Creates a new player and starts a session
Request
{
"name": "slash",
"email": "slash@slashware.net",
"password": "password"
}
Response
{
"pin": 54544
"t": "5496876546873"
}
GET /authToken/:name
Obtains a token (starting a session)
Returns a 401 if there's no player with than name or the password doesn't match the player with the given name
Request
{
"password": "password"
}
Response
{
"t": "5496876546873"
}
DELETE /authToken/:t
Deletes a token, expiring a session
Returns a 404 if there's no such token
POST /authToken/isAlive/:t
Prolongs the life time of a session
Returns a 404 if there's no such token
GET /player/:pin
Obtains info about a player
Returns a 404 if there's no player with than PIN
Response
{
"name": "slash",
"pin": 54544
"games": 330,
"victories": 1,
"groups": 0,
"friends": 4
}
Auth Services
These require an auth token passed as "t" via parameter
All services returns a 401 if the token is not valid or expired
GET /friends/scores
Obtains the scores of a player friends combined with own, sorted by depth and kills
Response
{
"scores": [
{
"name": "deekatax",
"class": "Paladin",
"sex": "Male",
"depth": 14,
"kills": 85
"won": false
}
]
}
GET /messages
Consumes the messages queue for the player.
Response
{
"messages": [
{ "type": "newLevel",
"from": "deekatax",
"time": "2015-01-02T13:45:22Z",
"text": "Deekatax's Arcane reached Level 3"
}
]
}
POST /event/:type
Generates messages for all of a player friends
Request
{
"text": "Deekatax's Arcane reached Level 3"
}
POST /score
Saves a score for the player
Returns 400 Bad request if the hash doesn't validate the score.
Request
{
"class": "Paladin",
"sex": "Male",
"depth": 14,
"kills": 85
"won": false,
"hash": "51468768743asdasdasda8s7da6sd787678"
}
GET /scores
Obtains the personal scores for the player
{
"scores": [
{
"name": "slash",
"class": "Paladin",
"sex": "Male",
"depth": 14,
"kills": 85
"won": false
}
]
}
POST /friendship/:friendPIN
Creates a friendship link between the current user and the user identified with the PIN
Returns a 404 error if user doesn't exist
Step 4 – Functional Analysis
Functional analysis for all API operations, describing in general terms how each one would work in terms of data and “business logic” operations.
POST /player * Verify no player with same name exists, else return 400 * Verify password complies with required features, else return 400 * Encrypt password * Generate PIN * Insert a new player * Forward to GET /authToken/:name GET /authToken/:name * Get player by name. If not found return 401 * Encrypt password * Compare password with player password. If doesn't match return 401 * Generate auth token * Validate no session with such token exists, else regenerate * Calculate expiry time * Insert new session * Return token DELETE /authToken/:t * Get session by token. If none return 404 * Delete session by token POST /authToken/isAlive/:t * Get session by token. If none return 404 * Calculate new expiry time * Update session by token with new expiry time GET /player/:pin * Get player facts by pin (without friendNames). If none return 404 * Return player facts (General validation for all Auth Services) * Validate authToken, if invalid return 401 * Extend session expiry time * Get the username related to the token GET /friends/scores * Get friendNames list for player from playerFacts * Get the top 10 scores for each friend on the list (+ the auth player) and add to a master list) * Get the scores for the player sorted by depth and kills, first 10 * Sort list by depth and kills * Trim list to first 10 results * Return list GET /messages * Get the unconsumed messages for the player, and mark them as consumed * Return messages POST /event/:type * Get the friendNames list from playerFacts * Create a message for each friend POST /score * Regenerate hash using provided data, if it doesn't match provided hash return 400 * Insert a score document with given data * Update playerFacts for authPlayer * Increase games * If won, increase victories GET /scores * Obtain data from scores by authPlayer sorted by depth and kills, trim result by 10 POST /friendship/:friendPIN * Get player with given PIN, return 404 if not found * Get friendship from authPlayer to givenPlayer, if found return. * Get friendship from givenPlayer to authPlayer, if found return. * Create friendship from authPlayer to givenPlayer * Update playerFacts for authPlayer, adding givenPlayer to friendNames and increasing the friendsCount * Update playerFacts for givenPlayer, adding authPlayer to friendNames and increasing the friendsCount
Step 5 – Document Model
Define the document model (master documents, transactional and facts) based on the blueprint and the functional model.
Masters ======= players playerName String, email String, password String, pin Number friendships fromPlayer String, toPlayer String, friendshipTime String scores playerName String, characterClass String, characterSex String, depth Number, kills Number, won Boolean, scoreTime String Transactional ============= sessions playerName String, token String, expiryTime String messages messageTime String messageType String, fromPlayerName String, toPlayerName String, messageText String, consumed Boolean Facts ===== playerFacts playerName String, pin Number, games Number, victories Number, friendsCount Number, friendNames Array< String >
Step 6 – Component Prototypes
Design the prototypes for the Data Access and “Business Logic” components based mainly on the functional analysis.
Business Logic Prototype {
verifyPassword: function(plainPassword){},
encryptPassword: function(plainPassword){},
generatePIN: function(){},
generateAuthToken: function(){},
getNewExpiryTime: function(){},
isTokenValid: function(expiryTime){},
generateScoreHash: function(score){}
}
Data Access Prototype {
getPlayer: function(name){},
getPlayerByPIN: function(pin){},
insertPlayer: function(player){},
getSession: function(token){},
insertSession: function(session){},
deleteSession: function(token){},
updateSessionExpiry: function(token, expiry){},
getPlayerFactsWithoutFriendNames: function(pin){},
getFriendNames: function(playerName){},
getTop10Scores: function(playerName){},
getAndConsumeUnconsumedMessages: function(playerName){},
insertMessage: function(message){},
insertScore: function(score){},
updatePlayerFactsByScore: function(score){},
getFriendship: function(fromPlayer, toPlayer){},
insertFriendship: function(fromPlayer, toPlayer){},
updatePlayerFriendFacts: function (player, newFriend){}
}


1 Comment