From 0abbc98b1cc2afd6a3de7d4fcba11f6e4f6e025d Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Thu, 4 Apr 2013 16:17:30 -0400 Subject: [PATCH 1/5] move all view helpers out of their respective files and into helpers.coffee. we need viewHelpers at top-level scope, but nothing else per se - this is preparation for memory leak fixing --- src/app/character.coffee | 17 -------- src/app/helpers.coffee | 84 +++++++++++++++++++++++++++++++++++++++- src/app/index.coffee | 3 -- src/app/items.coffee | 40 ------------------- src/app/profile.coffee | 3 +- src/app/tasks.coffee | 30 -------------- 6 files changed, 85 insertions(+), 92 deletions(-) diff --git a/src/app/character.coffee b/src/app/character.coffee index ad76409346..9bf5fd2195 100644 --- a/src/app/character.coffee +++ b/src/app/character.coffee @@ -8,23 +8,6 @@ _ = require 'underscore' lodash = require 'lodash' derby = require 'derby' -module.exports.username = username = (auth) -> - if auth?.facebook?.displayName? - auth.facebook.displayName - else if auth?.facebook? - fb = auth.facebook - if fb._raw then "#{fb.name.givenName} #{fb.name.familyName}" else fb.name - else if auth?.local? - auth.local.username - else - 'Anonymous' - -module.exports.view = (view) -> - view.fn "username", (auth) -> username(auth) - - view.fn "tnl", algos.tnl - - module.exports.app = (appExports, model) -> user = model.at '_user' diff --git a/src/app/helpers.coffee b/src/app/helpers.coffee index 900edcc610..9852545b48 100644 --- a/src/app/helpers.coffee +++ b/src/app/helpers.coffee @@ -1,5 +1,6 @@ moment = require 'moment' _ = require 'underscore' +algos = require './algos' # Absolute diff between two dates daysBetween = (yesterday, now, dayStart) -> @@ -23,6 +24,17 @@ removeWhitespace = (str) -> return '' unless str str.replace /\s/g, '' +username = (auth) -> + if auth?.facebook?.displayName? + auth.facebook.displayName + else if auth?.facebook? + fb = auth.facebook + if fb._raw then "#{fb.name.givenName} #{fb.name.familyName}" else fb.name + else if auth?.local? + auth.local.username + else + 'Anonymous' + viewHelpers = (view) -> view.fn "percent", (x, y) -> x=1 if x==0 @@ -59,4 +71,74 @@ viewHelpers = (view) -> view.fn "truarr", (num) -> num-1 -module.exports = { viewHelpers, removeWhitespace, randomVal, daysBetween, dayMapping } + ### + User + ### + view.fn "username", (auth) -> username(auth) + view.fn "tnl", algos.tnl + + ### + Items + ### + view.fn 'equipped', (user, type) -> + {gender, armorSet} = user?.preferences || {'m', 'v1'} + + if type=='armor' + armor = user?.items?.armor || 0 + if gender == 'f' + return if (parseInt(armor) == 0) then "f_armor_#{armor}_#{armorSet}" else "f_armor_#{armor}" + else + return "m_armor_#{armor}" + + else if type=='head' + head = user?.items?.head || 0 + if gender == 'f' + return if (parseInt(head) > 1) then "f_head_#{head}_#{armorSet}" else "f_head_#{head}" + else + return "m_head_#{head}" + + view.fn "gold", (num) -> + if num + return (num).toFixed(1).split('.')[0] + else + return "0" + + view.fn "silver", (num) -> + if num + (num).toFixed(2).split('.')[1] + else + return "00" + + ### + Tasks + ### + view.fn 'taskClasses', (task) -> + return unless task + {type, completed, value, repeat} = task + + classes = type + + # show as completed if completed (naturally) or not required for today + if type in ['todo', 'daily'] + if completed or (repeat and repeat[dayMapping[moment().day()]]==false) + classes += " completed" + else + classes += " uncompleted" + + if value < -20 + classes += ' color-worst' + else if value < -10 + classes += ' color-worse' + else if value < -1 + classes += ' color-bad' + else if value < 1 + classes += ' color-neutral' + else if value < 5 + classes += ' color-good' + else if value < 10 + classes += ' color-better' + else + classes += ' color-best' + return classes + +module.exports = { viewHelpers, removeWhitespace, randomVal, daysBetween, dayMapping, username } diff --git a/src/app/index.coffee b/src/app/index.coffee index 5317003429..431eb22669 100644 --- a/src/app/index.coffee +++ b/src/app/index.coffee @@ -24,9 +24,6 @@ profile = require './profile' pets = require './pets' helpers.viewHelpers view -character.view view -tasks.view view -items.view view _ = require('underscore') diff --git a/src/app/items.coffee b/src/app/items.coffee index 17a7c40e3b..d19d9e89f0 100644 --- a/src/app/items.coffee +++ b/src/app/items.coffee @@ -70,46 +70,6 @@ _.each ['weapon', 'armor', 'head', 'shield'], (key) -> _.each items.pets, (pet) -> pet.notes = 'Find a hatching potion to pour on this egg, and one day it will hatch into a loyal pet.' _.each items.hatchingPotions, (hatchingPotion) -> hatchingPotion.notes = "Pour this on an egg, and it will hatch as a #{hatchingPotion.text} pet." -### - view exports -### -module.exports.view = (view) -> - view.fn 'equipped', (user, type) -> - {gender, armorSet} = user?.preferences || {'m', 'v1'} - - if type=='armor' - armor = user?.items?.armor || 0 - if gender == 'f' - return if (parseInt(armor) == 0) then "f_armor_#{armor}_#{armorSet}" else "f_armor_#{armor}" - else - return "m_armor_#{armor}" - - else if type=='head' - head = user?.items?.head || 0 - if gender == 'f' - return if (parseInt(head) > 1) then "f_head_#{head}_#{armorSet}" else "f_head_#{head}" - else - return "m_head_#{head}" - - view.fn "gold", (num) -> - if num - return (num).toFixed(1).split('.')[0] - else - return "0" - - view.fn "silver", (num) -> - if num - (num).toFixed(2).split('.')[1] - else - return "00" - - view.fn "copper", (num) -> - if num - c = (num).toFixed(4).split('.')[1] - c.toString().substr(2,2) - else - return "00" - ### server exports ### diff --git a/src/app/profile.coffee b/src/app/profile.coffee index b578ac2529..defe3bc955 100644 --- a/src/app/profile.coffee +++ b/src/app/profile.coffee @@ -1,5 +1,6 @@ character = require './character' browser = require './browser' +helpers = require './helpers' module.exports.app = (appExports, model) -> user = model.at('_user') @@ -22,6 +23,6 @@ module.exports.app = (appExports, model) -> uid = $(el).attr('data-uid') model.ref '_profileActive', model.at("users.#{uid}") model.set '_profileActiveMain', user.get('id') is uid - model.set '_profileActiveUsername', character.username model.get('_profileActive.auth') + model.set '_profileActiveUsername', helpers.username model.get('_profileActive.auth') browser.setupTooltips(model) diff --git a/src/app/tasks.coffee b/src/app/tasks.coffee index ddfd3dc889..52a726d343 100644 --- a/src/app/tasks.coffee +++ b/src/app/tasks.coffee @@ -4,36 +4,6 @@ _ = require 'underscore' moment = require 'moment' character = require './character' -module.exports.view = (view) -> - view.fn 'taskClasses', (task) -> - return unless task - {type, completed, value, repeat} = task - - classes = type - - # show as completed if completed (naturally) or not required for today - if type in ['todo', 'daily'] - if completed or (repeat and repeat[helpers.dayMapping[moment().day()]]==false) - classes += " completed" - else - classes += " uncompleted" - - if value < -20 - classes += ' color-worst' - else if value < -10 - classes += ' color-worse' - else if value < -1 - classes += ' color-bad' - else if value < 1 - classes += ' color-neutral' - else if value < 5 - classes += ' color-good' - else if value < 10 - classes += ' color-better' - else - classes += ' color-best' - return classes - module.exports.app = (appExports, model) -> user = model.at('_user') From 1c2cc06a6e9ecbf69f6a8ee9cc112963a4d7bfa6 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Thu, 4 Apr 2013 15:36:52 -0400 Subject: [PATCH 2/5] BREAKING CHANGE! this removes list cleanup / re-establishment. In the past, id-lists in refLists were being completely emptied (see #85 & #86). I'm still not sure why (see https://github.com/codeparty/racer/issues/101 or https://github.com/codeparty/derby/issues/237 possibly?). This commit reverts working around that issue, but potentially fixes #769. Going to test this on heroku a bit before merging in --- src/app/browser.coffee | 6 ------ src/app/character.coffee | 32 -------------------------------- src/app/index.coffee | 14 ++++++++++++-- 3 files changed, 12 insertions(+), 40 deletions(-) diff --git a/src/app/browser.coffee b/src/app/browser.coffee index 873ce7eb83..1da6586f11 100644 --- a/src/app/browser.coffee +++ b/src/app/browser.coffee @@ -2,12 +2,6 @@ _ = require 'underscore' moment = require 'moment' #algos = require './algos' - -restoreRefs = module.exports.restoreRefs = (model) -> - #refLists - _.each ['habit', 'daily', 'todo', 'reward'], (type) -> - model.refList "_#{type}List", "_user.tasks", "_user.#{type}Ids" - ### Loads JavaScript files from public/js/* If a library is available in a CDN, we put it in (index.html) for better caching. If not, we use diff --git a/src/app/character.coffee b/src/app/character.coffee index 9bf5fd2195..1ac4c446d4 100644 --- a/src/app/character.coffee +++ b/src/app/character.coffee @@ -137,38 +137,6 @@ module.exports.newUserObject = -> when 'reward' then newUser.rewardIds.push guid return newUser -module.exports.updateUser = (model) -> - batch = new BatchUpdate(model) - user = batch.user - obj = batch.obj() - tasks = obj.tasks - - # Remove corrupted tasks - _.each tasks, (task, key) -> - unless task? - user.del("tasks.#{key}") - delete tasks[key] - - batch.startTransaction() - - batch.set('apiToken', derby.uuid()) unless obj.apiToken - - ## Task List Cleanup - # FIXME temporary hack to fix lists (Need to figure out why these are happening) - _.each ['habit','daily','todo','reward'], (type) -> - # 1. remove duplicates - # 2. restore missing zombie tasks back into list - taskIds = _.pluck( _.where(tasks, {type:type}), 'id') - union = _.union obj[type + 'Ids'], taskIds - - # 2. remove empty (grey) tasks - preened = _.filter union, (val) -> _.contains(taskIds, val) and val? - - # There were indeed issues found, set the new list - batch.set("#{type}Ids", preened) # if _.difference(preened, userObj[path]).length != 0 - - batch.commit() - module.exports.BatchUpdate = BatchUpdate = (model) -> user = model.at("_user") transactionInProgress = false diff --git a/src/app/index.coffee b/src/app/index.coffee index 431eb22669..2bf35dd892 100644 --- a/src/app/index.coffee +++ b/src/app/index.coffee @@ -41,10 +41,20 @@ get '/', (page, model, params, next) -> # return page.redirect 'https://' + req.headers.host + req.url party.partySubscribe page, model, params, next, -> - character.updateUser(model) + user = model.at('_user') + user.setNull('apiToken', derby.uuid()) + + # Remove corrupted tasks + tasks = user.get('tasks') + _.each tasks, (task, key) -> user.del("tasks.#{key}") unless task? + items.server(model) model.set '_view', _view - browser.restoreRefs model + + #refLists + _.each ['habit', 'daily', 'todo', 'reward'], (type) -> + model.refList "_#{type}List", "_user.tasks", "_user.#{type}Ids" + page.render() # ========== CONTROLLER FUNCTIONS ========== From 584759bd2586fb99110764401f9deefe333f8c2d Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Thu, 4 Apr 2013 16:44:54 -0400 Subject: [PATCH 3/5] shift some require() statements around, attempt to get things out of app.coffee top-level scope - working towards mem leak fix --- src/app/browser.coffee | 1 - src/app/index.coffee | 30 ++++++++++-------------------- src/app/party.coffee | 5 +++-- src/app/scoring.coffee | 1 - 4 files changed, 13 insertions(+), 24 deletions(-) diff --git a/src/app/browser.coffee b/src/app/browser.coffee index 1da6586f11..1d5513662b 100644 --- a/src/app/browser.coffee +++ b/src/app/browser.coffee @@ -1,6 +1,5 @@ _ = require 'underscore' moment = require 'moment' -#algos = require './algos' ### Loads JavaScript files from public/js/* diff --git a/src/app/index.coffee b/src/app/index.coffee index 2bf35dd892..780922f09d 100644 --- a/src/app/index.coffee +++ b/src/app/index.coffee @@ -12,17 +12,7 @@ i18n.localize app, availableLocales: ['en', 'he'] defaultLocale: 'en' -# Custom requires -character = require './character' -tasks = require './tasks' -scoring = require './scoring' helpers = require './helpers' -browser = require './browser' -party = require './party' -items = require './items' -profile = require './profile' -pets = require './pets' - helpers.viewHelpers view _ = require('underscore') @@ -40,7 +30,7 @@ get '/', (page, model, params, next) -> #if req.headers['x-forwarded-proto']!='https' and process.env.NODE_ENV=='production' # return page.redirect 'https://' + req.headers.host + req.url - party.partySubscribe page, model, params, next, -> + require('./party').partySubscribe page, model, params, next, -> user = model.at('_user') user.setNull('apiToken', derby.uuid()) @@ -48,7 +38,7 @@ get '/', (page, model, params, next) -> tasks = user.get('tasks') _.each tasks, (task, key) -> user.del("tasks.#{key}") unless task? - items.server(model) + require('./items').server(model) model.set '_view', _view #refLists @@ -66,15 +56,15 @@ ready (model) -> lastCron = user.get('lastCron') user.set('lastCron', +new Date) if (!lastCron? or lastCron == 'new') - scoring.cron(model) + require('./scoring').cron(model) - character.app(exports, model) - tasks.app(exports, model) - items.app(exports, model) - party.app(exports, model) - profile.app(exports, model) - pets.app(exports, model) + require('./character').app(exports, model) + require('./tasks').app(exports, model) + require('./items').app(exports, model) + require('./party').app(exports, model) + require('./profile').app(exports, model) + require('./pets').app(exports, model) require('../server/private').app(exports, model) require('./debug').app(exports, model) if model.get('_view.nodeEnv') != 'production' - browser.app(exports, model, app) + require('./browser').app(exports, model, app) diff --git a/src/app/party.coffee b/src/app/party.coffee index 47fb07fe6c..91902f689f 100644 --- a/src/app/party.coffee +++ b/src/app/party.coffee @@ -1,6 +1,4 @@ _ = require('underscore') -character = require './character' -browser = require './browser' partyUnsubscribe = (model, cb) -> if window? @@ -69,6 +67,9 @@ module.exports.partySubscribe = partySubscribe = (page, model, params, next, cb) return finished [partyQ, membersQ, selfQ], ['_party', '_partyMembers', '_user'] module.exports.app = (appExports, model) -> + character = require './character' + browser = require './browser' + user = model.at('_user') user.on 'set', 'flags.partyEnabled', (captures, args) -> diff --git a/src/app/scoring.coffee b/src/app/scoring.coffee index 6c6d481893..6fd41d6f6f 100644 --- a/src/app/scoring.coffee +++ b/src/app/scoring.coffee @@ -1,4 +1,3 @@ -async = require 'async' moment = require 'moment' _ = require 'underscore' { randomVal } = helpers = require './helpers' From a07dbaf9fd101aa1954acfba21c218d4aaeb2dd5 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Thu, 4 Apr 2013 17:38:31 -0400 Subject: [PATCH 4/5] nix the "remove if empty auth & no more session" cron task --- migrations/20130212_preen_cron.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/migrations/20130212_preen_cron.js b/migrations/20130212_preen_cron.js index 17bfe9d2b1..fc45cc0f13 100644 --- a/migrations/20130212_preen_cron.js +++ b/migrations/20130212_preen_cron.js @@ -44,9 +44,13 @@ db.users.find(un_registered).forEach(function(user) { }); -db.sessions.find().forEach(function(sess){ +/** + * Don't remove missing user auths anymore. This was previously necessary due to data corruption, + * revisit if needs be + */ +/*db.sessions.find().forEach(function(sess){ var uid = JSON.parse(sess.session).userId; if (!uid || db.users.count({_id:uid}) === 0) { db.sessions.remove({_id:sess._id}); } -}); \ No newline at end of file +});*/ \ No newline at end of file From 02a1a0d3b4883f7a8e8f249678918183c1b0ea7a Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Thu, 4 Apr 2013 18:01:06 -0400 Subject: [PATCH 5/5] cap minimum %chance of drop to 1% so users still rewarded for championed habits. Will remove once we have streaks / combos. Also factor in task difficulty --- src/app/algos.coffee | 2 +- src/app/scoring.coffee | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/app/algos.coffee b/src/app/algos.coffee index fff6cf5a7a..5a24d1b9a5 100644 --- a/src/app/algos.coffee +++ b/src/app/algos.coffee @@ -1,7 +1,7 @@ XP = 15 HP = 2 -priorityValue = (priority='!') -> +priorityValue = module.exports.priorityValue = (priority='!') -> switch priority when '!' then 1 when '!!' then 1.5 diff --git a/src/app/scoring.coffee b/src/app/scoring.coffee index 6fd41d6f6f..19044cca63 100644 --- a/src/app/scoring.coffee +++ b/src/app/scoring.coffee @@ -12,7 +12,7 @@ MODIFIER = algos.MODIFIER # each new level, armor, weapon add 2% modifier (this ### Drop System ### -randomDrop = (model, delta) -> +randomDrop = (model, delta, priority) -> user = model.at('_user') # limit drops to 2 / day @@ -25,7 +25,10 @@ randomDrop = (model, delta) -> # % chance of getting a pet or meat # debugging purpose - 50% chance during development, 3% chance on prod chanceMultiplier = if (model.flags.nodeEnv is 'development') then 50 else 1 - chanceMultiplier *= Math.abs(delta) # multiply chance by reddness + # TODO temporary min cap of 1 so people still get rewarded for good habits. Will change once we have streaks + deltaMultiplier = if Math.abs(delta) < 1 then 1 else Math.abs(delta) + chanceMultiplier = chanceMultiplier * deltaMultiplier * algos.priorityValue(priority) # multiply chance by reddness + if user.get('flags.dropsEnabled') and Math.random() < (.01 * chanceMultiplier) # current breakdown - 3% (adjustable) chance on drop # If they got a drop: 50% chance of egg, 50% Hatching Potion. If hatchingPotion, broken down further even further @@ -180,7 +183,7 @@ score = (model, taskId, direction, times, batch, cron) -> batch.commit() # Drop system - randomDrop(model, delta) if direction is 'up' + randomDrop(model, delta, priority) if direction is 'up' return delta