mirror of
https://github.com/sudoxnym/habitica.git
synced 2026-08-05 03:52:14 +00:00
update batchTxn to be a callback instead of object, put batch
operations back in for scoring & cron. hoping this will cut back on the 4000ms issue, quite an emergency that
This commit is contained in:
parent
89160919f4
commit
1745ae6205
5 changed files with 87 additions and 146 deletions
|
|
@ -1,6 +1,8 @@
|
|||
browser = require './browser'
|
||||
items = require './items'
|
||||
algos = require 'habitrpg-shared/script/algos'
|
||||
misc = require './misc'
|
||||
helpers = require 'habitrpg-shared/script/helpers'
|
||||
|
||||
moment = require 'moment'
|
||||
_ = require 'lodash'
|
||||
|
|
@ -31,26 +33,17 @@ module.exports.app = (appExports, model) ->
|
|||
items.updateStore(model)
|
||||
|
||||
appExports.reset = (e, el) ->
|
||||
batch = new BatchUpdate(model)
|
||||
batch.startTransaction()
|
||||
taskTypes = ['habit', 'daily', 'todo', 'reward']
|
||||
batch.set 'tasks', {}
|
||||
taskTypes.forEach (type) -> batch.set "#{type}Ids", []
|
||||
#batch.set 'balance', 1 if user.get('balance') < 1 #only if they haven't manually bought gems
|
||||
|
||||
# Reset stats
|
||||
batch.set 'stats.hp', 50
|
||||
batch.set 'stats.lvl', 1
|
||||
batch.set 'stats.gp', 0
|
||||
batch.set 'stats.exp', 0
|
||||
# Reset items
|
||||
batch.set 'items.armor', 0
|
||||
batch.set 'items.weapon', 0
|
||||
batch.set 'items.head', 0
|
||||
batch.set 'items.shield', 0
|
||||
|
||||
misc.batchTxn model, (uObj, paths) ->
|
||||
taskTypes = ['habit', 'daily', 'todo', 'reward']
|
||||
uObj.tasks = {}; paths['tasks'] = true
|
||||
taskTypes.forEach (type) -> uObj["#{type}Ids"] = []; paths["#{type}Ids"] = true
|
||||
# Reset stats
|
||||
[uObj.stats.hp, uObj.stats.lvl, uObj.stats.gp, uObj.stats.exp] = [50, 1, 0, 0]
|
||||
# Reset items
|
||||
[uObj.items.armor, uObj.items.weapon, uObj.items.head, uObj.items.shield] = [0, 0, 0, 0]
|
||||
['stats.hp', 'stats.lvl', 'stats.gp', 'stats.exp', 'items.armor', 'items.weapon', 'items.head', 'items.shield'].forEach (path) ->
|
||||
paths[path] = true
|
||||
items.updateStore(model)
|
||||
batch.commit()
|
||||
browser.resetDom(model)
|
||||
|
||||
appExports.closeNewStuff = (e, el) ->
|
||||
|
|
@ -69,70 +62,15 @@ module.exports.app = (appExports, model) ->
|
|||
user.set 'preferences.armorSet', $(el).attr('data-value')
|
||||
|
||||
appExports.restoreSave = (e, el) ->
|
||||
batch = new BatchUpdate(model)
|
||||
batch.startTransaction()
|
||||
$('#restore-form input').each ->
|
||||
batch.set $(this).attr('data-for'), parseInt($(this).val() || 1)
|
||||
batch.commit()
|
||||
misc.batchTxn model, (uObj, paths) ->
|
||||
$('#restore-form input').each ->
|
||||
[path, val] = [$(this).attr('data-for'), parseInt($(this).val() || 1)]
|
||||
helpers.dotSet(path, val, uObj); paths[path] = true
|
||||
debugger
|
||||
|
||||
appExports.toggleHeader = (e, el) ->
|
||||
user.set 'preferences.hideHeader', !user.get('preferences.hideHeader')
|
||||
|
||||
appExports.deleteAccount = (e, el) ->
|
||||
model.del "users.#{user.get('id')}", ->
|
||||
window.location.href = "/logout"
|
||||
|
||||
module.exports.BatchUpdate = BatchUpdate = (model) ->
|
||||
user = model.at("_user")
|
||||
transactionInProgress = false
|
||||
obj = null
|
||||
updates = {}
|
||||
|
||||
{
|
||||
user: user
|
||||
|
||||
obj: ->
|
||||
obj ?= model.get 'users.'+user.get('id')
|
||||
return obj
|
||||
|
||||
startTransaction: ->
|
||||
# start a batch transaction - nothing between now and @commit() will be set immediately
|
||||
transactionInProgress = true
|
||||
model._dontPersist = true
|
||||
@obj()
|
||||
|
||||
###
|
||||
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()
|
||||
###
|
||||
set: (path, val) ->
|
||||
updates[path] = val if transactionInProgress
|
||||
user.set(path, val)
|
||||
|
||||
###
|
||||
Hack to get around dom bindings being lost if parent objects are replaced whole-sale
|
||||
eg, user.set('stats', {hp:50, exp:10...}) will break dom bindings, but user.set('stats.hp',50) is ok
|
||||
###
|
||||
setStats: (stats) ->
|
||||
stats ?= obj.stats
|
||||
that = @
|
||||
_.each Object.keys(stats), (key) -> that.set "stats.#{key}", stats[key]; true
|
||||
|
||||
# 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]
|
||||
# , obj
|
||||
|
||||
commit: ->
|
||||
model._dontPersist = false
|
||||
# some hackery in our own branched racer-db-mongo, see findAndModify of lefnire/racer-db-mongo#habitrpg index.js
|
||||
# pass true if we have levelled to supress xp notification
|
||||
user.set "update__", updates
|
||||
transactionInProgress = false
|
||||
updates = {}
|
||||
}
|
||||
location.href = "/logout"
|
||||
|
|
@ -40,30 +40,25 @@ cleanupCorruptTasks = (model) ->
|
|||
delete tasks[key]
|
||||
true
|
||||
|
||||
batch = null
|
||||
misc.batchTxn model, (uObj, paths) ->
|
||||
## Task List Cleanup
|
||||
['habit','daily','todo','reward'].forEach (type) ->
|
||||
|
||||
## Task List Cleanup
|
||||
['habit','daily','todo','reward'].forEach (type) ->
|
||||
# 1. remove duplicates
|
||||
# 2. restore missing zombie tasks back into list
|
||||
idList = uObj["#{type}Ids"]
|
||||
taskIds = _.pluck( _.where(tasks, {type:type}), 'id')
|
||||
union = _.union idList, taskIds
|
||||
|
||||
# 1. remove duplicates
|
||||
# 2. restore missing zombie tasks back into list
|
||||
idList = user.get("#{type}Ids")
|
||||
taskIds = _.pluck( _.where(tasks, {type:type}), 'id')
|
||||
union = _.union idList, taskIds
|
||||
# 2. remove empty (grey) tasks
|
||||
preened = _.filter union, (id) -> id and _.contains(taskIds, id)
|
||||
|
||||
# 2. remove empty (grey) tasks
|
||||
preened = _.filter union, (id) -> id and _.contains(taskIds, id)
|
||||
# There were indeed issues found, set the new list
|
||||
if !_.isEqual(idList, preened)
|
||||
uObj["#{type}Ids"] = preened; paths["#{type}Ids"] = true
|
||||
console.error uObj.id + "'s #{type}s were corrupt."
|
||||
true
|
||||
|
||||
# There were indeed issues found, set the new list
|
||||
if !_.isEqual(idList, preened)
|
||||
unless batch?
|
||||
batch = new require('./character').BatchUpdate(model)
|
||||
batch.startTransaction()
|
||||
batch.set("#{type}Ids", preened)
|
||||
console.error user.get('id') + "'s #{type}s were corrupt."
|
||||
true
|
||||
|
||||
batch.commit() if batch?
|
||||
|
||||
###
|
||||
Subscribe to the user, the users's party (meta info like party name, member ids, etc), and the party's members. 3 subscriptions.
|
||||
|
|
@ -122,6 +117,7 @@ get '/', (page, model, params, next) ->
|
|||
ready (model) ->
|
||||
user = model.at('_user')
|
||||
browser = require './browser'
|
||||
character = require './character'
|
||||
|
||||
require('./character').app(exports, model)
|
||||
require('./tasks').app(exports, model)
|
||||
|
|
@ -138,18 +134,14 @@ ready (model) ->
|
|||
###
|
||||
Cron
|
||||
###
|
||||
uObj = misc.hydrate(user.get())
|
||||
# habitrpg-shared/algos requires uObj.habits, uObj.dailys etc instead of uObj.tasks
|
||||
_.each ['habit','daily','todo','reward'], (type) ->
|
||||
uObj["#{type}s"] = _.where(uObj.tasks, {type:type}); true
|
||||
paths = {}
|
||||
algos.cron(uObj, {paths:paths})
|
||||
if !_.isEmpty(paths)
|
||||
if paths['lastCron'] and _.size(paths) is 1
|
||||
user.set "lastCron", uObj.lastCron
|
||||
else
|
||||
lostHp = delete paths['stats.hp'] # we'll set this manually so we can get a cool animation
|
||||
_.each paths, (v,k) -> user.pass({cron:true}).set(k,helpers.dotGet(k, uObj)); true
|
||||
if lostHp
|
||||
misc.batchTxn model, (uObj, paths) ->
|
||||
# habitrpg-shared/algos requires uObj.habits, uObj.dailys etc instead of uObj.tasks
|
||||
_.each ['habit','daily','todo','reward'], (type) -> uObj["#{type}s"] = _.where(uObj.tasks, {type}); true
|
||||
algos.cron uObj, {paths}
|
||||
return if _.isEmpty(paths) or (paths['lastCron'] and _.size(paths) is 1)
|
||||
if lostHp = delete paths['stats.hp'] # we'll set this manually so we can get a cool animation
|
||||
setTimeout ->
|
||||
browser.resetDom(model)
|
||||
setTimeout (-> user.set('stats.hp', uObj.stats.hp)), 750
|
||||
user.set 'stats.hp', uObj.stats.hp
|
||||
, 750
|
||||
,{cron:true}
|
||||
|
|
@ -2,6 +2,21 @@ _ = require 'lodash'
|
|||
algos = require 'habitrpg-shared/script/algos'
|
||||
items = require('habitrpg-shared/script/items').items
|
||||
helpers = require('habitrpg-shared/script/helpers')
|
||||
character = require('./character')
|
||||
|
||||
module.exports.batchTxn = batchTxn = (model, cb, options) ->
|
||||
user = model.at("_user")
|
||||
uObj = hydrate(user.get()) # see https://github.com/codeparty/racer/issues/116
|
||||
paths = {}
|
||||
model._dontPersist = true
|
||||
cb uObj, paths
|
||||
_.each paths, (v,k) -> user.pass({cron:options?.cron}).set(k,helpers.dotGet(k, uObj));true
|
||||
model._dontPersist = false
|
||||
# some hackery in our own branched racer-db-mongo, see findAndModify of lefnire/racer-db-mongo#habitrpg index.js
|
||||
# pass true if we have levelled to supress xp notification
|
||||
unless _.isEmpty paths
|
||||
setOps = _.reduce paths, ((m,v,k)-> m[k] = helpers.dotGet(k,uObj);m), {}
|
||||
user.set "update__", setOps
|
||||
|
||||
###
|
||||
algos.score wrapper for habitrpg-helpers to work in Derby. We need to do model.set() instead of simply setting the
|
||||
|
|
@ -11,28 +26,24 @@ helpers = require('habitrpg-shared/script/helpers')
|
|||
###
|
||||
module.exports.score = (model, taskId, direction, allowUndo=false) ->
|
||||
#return setTimeout( (-> score(taskId, direction)), 500) if model._txnQueue.length > 0
|
||||
user = model.at("_user")
|
||||
batchTxn model, (uObj, paths) ->
|
||||
tObj = uObj.tasks[taskId]
|
||||
|
||||
uObj = hydrate(user.get()) # see https://github.com/codeparty/racer/issues/116
|
||||
tObj = uObj.tasks[taskId]
|
||||
# Stuff for undo
|
||||
if allowUndo
|
||||
tObjBefore = _.cloneDeep tObj
|
||||
tObjBefore.completed = !tObjBefore.completed if tObjBefore.type in ['daily', 'todo']
|
||||
previousUndo = model.get('_undo')
|
||||
clearTimeout(previousUndo.timeoutId) if previousUndo?.timeoutId
|
||||
timeoutId = setTimeout (-> model.del('_undo')), 20000
|
||||
model.set '_undo', {stats:_.cloneDeep(uObj.stats), task:tObjBefore, timeoutId: timeoutId}
|
||||
|
||||
# Stuff for undo
|
||||
if allowUndo
|
||||
tObjBefore = _.cloneDeep tObj
|
||||
tObjBefore.completed = !tObjBefore.completed if tObjBefore.type in ['daily', 'todo']
|
||||
previousUndo = model.get('_undo')
|
||||
clearTimeout(previousUndo.timeoutId) if previousUndo?.timeoutId
|
||||
timeoutId = setTimeout (-> model.del('_undo')), 20000
|
||||
model.set '_undo', {stats:_.cloneDeep(uObj.stats), task:tObjBefore, timeoutId: timeoutId}
|
||||
|
||||
paths = {}
|
||||
delta = algos.score(uObj, tObj, direction, {paths})
|
||||
_.each paths, (v,k) -> user.set(k,helpers.dotGet(k, uObj)); true
|
||||
model.set('_streakBonus', uObj._tmp.streakBonus) if uObj._tmp?.streakBonus
|
||||
if uObj._tmp?.drop and $?
|
||||
model.set '_drop', uObj._tmp.drop
|
||||
$('#item-dropped-modal').modal 'show'
|
||||
delta
|
||||
delta = algos.score(uObj, tObj, direction, {paths})
|
||||
model.set('_streakBonus', uObj._tmp.streakBonus) if uObj._tmp?.streakBonus
|
||||
if uObj._tmp?.drop and $?
|
||||
model.set '_drop', uObj._tmp.drop
|
||||
$('#item-dropped-modal').modal 'show'
|
||||
delta
|
||||
|
||||
###
|
||||
Make sure model.get() returns all properties, see https://github.com/codeparty/racer/issues/116
|
||||
|
|
|
|||
|
|
@ -128,19 +128,16 @@ module.exports.app = (appExports, model) ->
|
|||
appExports.undo = () ->
|
||||
undo = model.get '_undo'
|
||||
clearTimeout(undo.timeoutId) if undo?.timeoutId
|
||||
batch = character.BatchUpdate(model)
|
||||
batch.startTransaction()
|
||||
model.del '_undo'
|
||||
_.each undo.stats, (val, key) -> batch.set "stats.#{key}", val; true
|
||||
_.each undo.stats, (val, key) -> user.set "stats.#{key}", val; true
|
||||
taskPath = "tasks.#{undo.task.id}"
|
||||
_.each undo.task, (val, key) ->
|
||||
return true if key in ['id', 'type'] # strange bugs in this world: https://workflowy.com/shared/a53582ea-43d6-bcce-c719-e134f9bf71fd/
|
||||
if key is 'completed'
|
||||
user.pass({cron:true}).set("#{taskPath}.completed",val)
|
||||
else
|
||||
batch.set "#{taskPath}.#{key}", val
|
||||
user.set "#{taskPath}.#{key}", val
|
||||
true
|
||||
batch.commit()
|
||||
|
||||
appExports.tasksToggleAdvanced = (e, el) ->
|
||||
$(el).next('.advanced-option').toggleClass('visuallyhidden')
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
_ = require 'lodash'
|
||||
character = require "../app/character"
|
||||
misc = require "../app/misc"
|
||||
|
||||
module.exports.middleware = (req, res, next) ->
|
||||
model = req.getModel()
|
||||
|
|
@ -33,12 +33,15 @@ module.exports.app = (appExports, model) ->
|
|||
###
|
||||
Buy Reroll Button
|
||||
###
|
||||
appExports.buyReroll = (e, el, next) ->
|
||||
batch = new character.BatchUpdate(model)
|
||||
obj = model.get('_user')
|
||||
batch.set 'balance', obj.balance-1
|
||||
_.each obj.tasks, (task) -> batch.set("tasks.#{task.id}.value", 0) unless task.type is 'reward';true
|
||||
batch.commit()
|
||||
appExports.buyReroll = ->
|
||||
misc.batchTxn model, (uObj, paths) ->
|
||||
uObj.balance -= 1; paths['balance'] =1
|
||||
_.each uObj.tasks, (task) ->
|
||||
unless task.type is 'reward'
|
||||
uObj.tasks[task.id].value = 0
|
||||
paths["tasks.#{task.id}.value"] = 1
|
||||
true
|
||||
$('#reroll-modal').modal('hide')
|
||||
|
||||
module.exports.routes = (expressApp) ->
|
||||
###
|
||||
|
|
|
|||
Loading…
Reference in a new issue