diff --git a/src/app/index.coffee b/src/app/index.coffee index e8c8987328..cf2dbf5daa 100644 --- a/src/app/index.coffee +++ b/src/app/index.coffee @@ -46,9 +46,14 @@ get '/', (page, model, next) -> #user = result.at(0) model.ref '_user', user batch = new schema.BatchUpdate(model) + batch.startTransaction() userObj = batch.userObj - return page.redirect '/500.html' unless userObj? #this should never happen, but it is. Looking into it + unless userObj? + #this should never happen, but it is. Looking into it + console.error 'User object was null!' + return page.redirect '/500.html' + # Setup Item Store items = userObj.items @@ -254,14 +259,14 @@ ready (model) -> revive = (batch) -> # Reset stats - batch.queue 'stats.hp', 50 - batch.queue 'stats.lvl', 1 - batch.queue 'stats.money', 0 - batch.queue 'stats.exp', 0 + batch.set 'stats.hp', 50 + batch.set 'stats.lvl', 1 + batch.set 'stats.money', 0 + batch.set 'stats.exp', 0 # Reset items - batch.queue 'items.armor', 0 - batch.queue 'items.weapon', 0 + batch.set 'items.armor', 0 + batch.set 'items.weapon', 0 # Reset item store model.set '_view.items.armor', content.items.armor[1] @@ -269,16 +274,18 @@ ready (model) -> exports.revive = (e, el) -> batch = new schema.BatchUpdate(model) + batch.startTransaction() revive(batch) batch.commit() resetDom(model) exports.reset = (e, el) -> batch = new schema.BatchUpdate(model) + batch.startTransaction() taskTypes = ['habit', 'daily', 'todo', 'reward'] - batch.queue 'tasks', {} - _.each taskTypes, (type) -> batch.queue "#{type}Ids", [] - batch.queue 'balance', 2 if user.get('balance') < 2 #only if they haven't manually bought tokens + batch.set 'tasks', {} + _.each taskTypes, (type) -> batch.set "#{type}Ids", [] + batch.set 'balance', 2 if user.get('balance') < 2 #only if they haven't manually bought tokens revive(batch, true) batch.commit() resetDom(model) diff --git a/src/app/schema.coffee b/src/app/schema.coffee index 5e62598265..cf6c0d2505 100644 --- a/src/app/schema.coffee +++ b/src/app/schema.coffee @@ -35,14 +35,14 @@ module.exports.newUserObject = -> module.exports.updateUser = (batch) -> userObj = batch.userObj - batch.queue('notifications.kickstarter', 'show') unless userObj.notifications?.kickstarter? - batch.queue('friends', []) unless !_.isEmpty(userObj.friends) + batch.set('notifications.kickstarter', 'show') unless userObj.notifications?.kickstarter? + batch.set('friends', []) unless !_.isEmpty(userObj.friends) # Preferences, including API key # Some side-stepping to avoid unecessary set (one day, model.update... one day..) prefs = _.clone(userObj.preferences) - _.defaults prefs, { gender: 'm', armorSet: 'v1', api_token: derby.uuid() } - batch.queue('preferences', prefs) unless _.isEqual(prefs, userObj.preferences) + prefs = _.defaults prefs, { gender: 'm', armorSet: 'v1', api_token: derby.uuid() } + batch.set('preferences', prefs) unless _.isEqual(prefs, userObj.preferences) ## Task List Cleanup # FIXME temporary hack to fix lists (Need to figure out why these are happening) @@ -60,48 +60,54 @@ module.exports.updateUser = (batch) -> preened = _.filter(union, (val) -> _.contains(taskIds, val)) # There were indeed issues found, set the new list - batch.queue(path, preened) # if _.difference(preened, userObj[path]).length != 0 + batch.set(path, preened) # if _.difference(preened, userObj[path]).length != 0 module.exports.BatchUpdate = BatchUpdate = (model) -> - user = model.at('_user') - - # this is really stupid, but i can't find how to get around user.get() making only available what has been gotten specifically before -# userObj = {} -# _.each Object.keys(userSchema), (key) -> userObj[key] = lodash.cloneDeep user.get(key) -# userObj = lodash.cloneDeep obj # whaaa??? - - #FIXME - this doesn't work, modifying userObj modifies the value of user.get() at that path - # though that shouldn't be happening + user = model.at("_user") userObj = user.get() - - orig_commit = model._commit - model._commit = (txn) -> - txn.dontPersist = true - orig_commit.apply(model, arguments) - + origCommit = model._commit + transactionInProgress = false updates = {} - { - queue: (path, val) -> updates[path] = val + { userObj: userObj + startTransaction: -> + # start a batch transaction - nothing between now and @commit() will be set immediately + transactionInProgress = true + + # Really strange, user.get() seems to only return attributes which have previously been accessed. So in + # many cases, userObj.tasks.{taskId}.value is undefined - so we manually .get() each attribute here. + # Additionally, for some reason after getting the user object, changing properies manually (userObj.stats.hp = 50) + # seems to actually run user.set('stats.hp',50) which we don't want to do - so we deepClone here + #_.each Object.keys(userSchema), (key) -> userObj[key] = lodash.cloneDeep user.get(key) + #userObj = user.get() + model._commit = (txn) -> + txn.dontPersist = true + origCommit.apply(model, arguments) + ### - Handles updating the user model. If this is an en-mass operation (eg, server cron), pass the user object as {update}. - otherwise, null means commit the changes immediately + Handles updating the user model. If this is an en-mass operation (eg, server cron), changes are queued + but not actually set to the model. It also modifies userObj in case you need to access properties manually later. + If transaction not in progress, it just runs standard model.set() ### - updateAndQueue: (path, val) -> - @queue path, val - # Special function for setting object properties by string dot-notation. See http://stackoverflow.com/a/6394168/362790 - arr = path.split('.') - arr.reduce (curr, next, index) -> - if (arr.length - 1) == index - curr[next] = val - curr[next] - , userObj + set: (path, val) -> + if transactionInProgress + updates[path] = val + # Special function for setting object properties by string dot-notation. See http://stackoverflow.com/a/6394168/362790 + arr = path.split('.') + arr.reduce (curr, next, index) -> + if (arr.length - 1) == index then curr[next] = val + return curr[next] + , userObj + else + user.set(path, val) commit: -> _.each updates, (val, path) -> user.set(path, val) - model._commit = orig_commit - user.set "update__", updates # some hackery in our own branched racer-db-mongo, see findAndModify + model._commit = origCommit + # some hackery in our own branched racer-db-mongo, see findAndModify of lefnire/racer-db-mongo#habitrpg index.js + user.set "update__", updates + transactionInProgress = false } diff --git a/src/app/scoring.coffee b/src/app/scoring.coffee index 07e9aee966..388b43cf4c 100644 --- a/src/app/scoring.coffee +++ b/src/app/scoring.coffee @@ -67,29 +67,29 @@ updateStats = (newStats, batch) -> if newStats.hp? # Game Over if newStats.hp <= 0 - batch.updateAndQueue 'stats.lvl', 0 # signifies dead - batch.updateAndQueue 'stats.hp', 0 + batch.set 'stats.lvl', 0 # signifies dead + batch.set 'stats.hp', 0 return else - batch.updateAndQueue 'stats.hp', newStats.hp + batch.set 'stats.hp', newStats.hp if newStats.exp? # level up & carry-over exp tnl = user.get '_tnl' if newStats.exp >= tnl newStats.exp -= tnl - batch.updateAndQueue 'stats.lvl', userObj.stats.lvl + 1 - batch.updateAndQueue 'stats.hp', 50 + batch.set 'stats.lvl', userObj.stats.lvl + 1 + batch.set 'stats.hp', 50 newStats.lvl = userObj.stats.lvl if !userObj.items?.itemsEnabled and newStats.lvl >= 2 - batch.queue 'items.itemsEnabled', true #bit of trouble using userSet here + batch.set 'items.itemsEnabled', true #bit of trouble using userSet here if !userObj.flags?.partyEnabled and newStats.lvl >= 3 - batch.queue 'flags.partyEnabled', true - batch.updateAndQueue 'stats.exp', newStats.exp + batch.set 'flags.partyEnabled', true + batch.set 'stats.exp', newStats.exp if newStats.money? money = 0.0 if (!money? or money<0) - batch.updateAndQueue 'stats.money', newStats.money + batch.set 'stats.money', newStats.money # {taskId} task you want to score # {direction} 'up' or 'down' @@ -99,9 +99,10 @@ score = (taskId, direction, times, batch, cron) -> times ?= 1 commit = false - unless batch + unless batch? commit = true batch = new schema.BatchUpdate(model) + batch.startTransaction() userObj = batch.userObj {money, hp, exp, lvl} = userObj.stats @@ -140,7 +141,7 @@ score = (taskId, direction, times, batch, cron) -> if (delta > 0) then addPoints() else subtractPoints() taskObj.history ?= [] taskObj.history.push historyEntry - batch.updateAndQueue "#{taskPath}.history", taskObj.history if taskObj.value != value + batch.set "#{taskPath}.history", taskObj.history if taskObj.value != value when 'daily' calculateDelta() @@ -165,7 +166,7 @@ score = (taskId, direction, times, batch, cron) -> hp += money # hp - money difference money = 0 - batch.updateAndQueue "#{taskPath}.value", value + batch.set "#{taskPath}.value", value updateStats {hp: hp, exp: exp, money: money}, batch batch.commit() if commit return delta @@ -212,7 +213,7 @@ cron = (resetDom_cb) -> else absVal = if (completed) then Math.abs(value) else value todoTally += absVal - batch.queue('tasks.' + taskObj.id, taskObj) + batch.set('tasks.' + taskObj.id, taskObj) # Finished tallying userObj.history ?= {}; userObj.history.todos ?= []; userObj.history.exp ?= [] @@ -227,8 +228,8 @@ cron = (resetDom_cb) -> # Set the new user specs, and animate HP loss [hpAfter, userObj.stats.hp] = [userObj.stats.hp, hpBefore] - batch.queue('stats', userObj.stats) - batch.queue('history', userObj.history) + batch.set('stats', userObj.stats) + batch.set('history', userObj.history) batch.commit() resetDom_cb(model) setTimeout (-> user.set 'stats.hp', hpAfter), 1000 # animate hp loss