diff --git a/app/Model/Creature/CharacterComputation.js b/app/Model/Creature/CharacterComputation.js index 03458a77..2b7c4961 100644 --- a/app/Model/Creature/CharacterComputation.js +++ b/app/Model/Creature/CharacterComputation.js @@ -90,7 +90,13 @@ const writeAttributes = function (char) { } return op; }); - Attributes.rawCollection().bulkWrite( bulkWriteOps, {ordered : false}); + if (Meteor.isServer){ + Attributes.rawCollection().bulkWrite( bulkWriteOps, {ordered : false}); + } else { + _.each(bulkWriteOps, op => { + Attributes.update(op.updateMany.filter, op.updateMany.update, {multi: true}); + }); + } } /* @@ -113,7 +119,13 @@ const writeSkills = function (char) { } return op; }); - Skills.rawCollection().bulkWrite( bulkWriteOps, {ordered : false}); + if (Meteor.isServer){ + Skills.rawCollection().bulkWrite( bulkWriteOps, {ordered : false}); + } else { + _.each(bulkWriteOps, op => { + Skills.update(op.updateMany.filter, op.updateMany.update, {multi: true}); + }); + } } /* @@ -131,7 +143,13 @@ const writeDamageMultipliers = function (char) { } return op; }); - DamageMultipliers.rawCollection().bulkWrite( bulkWriteOps, {ordered : false}); + if (Meteor.isServer){ + DamageMultipliers.rawCollection().bulkWrite( bulkWriteOps, {ordered : false}); + } else { + _.each(bulkWriteOps, op => { + DamageMultipliers.update(op.updateMany.filter, op.updateMany.update, {multi: true}); + }); + } } /* diff --git a/app/Model/Creature/Characters.js b/app/Model/Creature/Characters.js index b0257330..f83369b3 100644 --- a/app/Model/Creature/Characters.js +++ b/app/Model/Creature/Characters.js @@ -90,66 +90,20 @@ const insertCharacterMethod = new ValidatedMethod({ // Create the character document Characters.insert({name, owner: this.userId}); - //Add all the required attributes to it - addDefaultStats(charId); + this.unblock(); + //Add all the required attributes to it + if (Meteor.isServer){ + addDefaultStats(charId); + } }, }); -const addDefaultStats function(charId){ - const stats = DEFAULT_CHARACTER_STATS; - let order = 0; - const baseParent = { - collection: "Characters", - id: charId, - group: "default", - }; - let name, variableName, parent, attribute, skill, ability, dm; - for (type in stats.attributes){ - for (let i in stats.attributes[type]){ - attribute = stats.attributes[type][i]; - if (_.isString(attribute)){ - name = attribute; - variableName = attribute.toLowerCase(); - } else { - name = attribute.name; - variableName = attribute.variableName; - } - parent = _.clone(baseParent); - // TODO Inserting documets in a for-loop is slow - // conider using the raw collection to bulk insert - // Including a callback makes the operation async, 3x faster - Attributes.insert({ - charId, name, variableName, order, type, parent - }, function(error, _id){})); - order++; - } - } - order = 0; - for (type in stats.skills){ - for (let i in stats.skills[type]){ - skill = stats.skills[type][i]; - Skills.insert({ - charId, - type, - order, - name: skill.name, - variableName: skill.variableName, - ability: skill.ability, - parent: _.clone(baseParent), - }, function(error, _id){})); - order++; - } - } - for (let i in stats.damageMultipliers){ - dm = stats.damageMultipliers[i]; - DamageMultipliers.insert({ - charId, - name: dm.name, - variableName = dm.variableName, - parent: _.clone(baseParent), - }, function(error, _id){})); - } +const addDefaultStats = function(charId){ + const defaultDocs = getDefaultCharacterDocs(charId); + Attributes.rawCollection().insert(defaultDocs.attributes, {ordered: false}); + Skills.rawCollection().insert(defaultDocs.skills, {ordered: false}); + DamageMultipliers.rawCollection().insert(defaultDocs.damageMultipliers, {ordered: false}); }; //clean up all data related to that character before removing it diff --git a/app/lib/constants/defaultCharacterStats.js b/app/lib/constants/defaultCharacterStats.js index 3086b521..6cd878ad 100644 --- a/app/lib/constants/defaultCharacterStats.js +++ b/app/lib/constants/defaultCharacterStats.js @@ -94,3 +94,60 @@ DEFAULT_CHARACTER_STATS = { {"name": "Thunder Multiplier", "variableName":"thunderMultiplier"}, ] } + +getDefaultCharacterDocs = function(charId){ + let docs = {attributes: [], skills: [], damageMultipliers: []}; + const stats = DEFAULT_CHARACTER_STATS; + let order = 0; + const baseParent = { + collection: "Characters", + id: charId, + group: "default", + }; + let name, variableName, parent, attribute, skill, ability, dm, type; + for (type in stats.attributes){ + for (let i in stats.attributes[type]){ + attribute = stats.attributes[type][i]; + if (_.isString(attribute)){ + name = attribute; + variableName = attribute.toLowerCase(); + } else { + name = attribute.name; + variableName = attribute.variableName; + } + parent = _.clone(baseParent); + docs.attributes.push({ + _id: Random.id, + charId, name, variableName, order, type, parent + }); + order++; + } + } + order = 0; + for (type in stats.skills){ + for (let i in stats.skills[type]){ + skill = stats.skills[type][i]; + docs.skills.push({ + _id: Random.id, + charId, + type, + order, + name: skill.name, + variableName: skill.variableName, + ability: skill.ability, + parent: _.clone(baseParent), + }); + order++; + } + } + for (let i in stats.damageMultipliers){ + dm = stats.damageMultipliers[i]; + docs.damageMultipliers.push({ + _id: Random.id, + charId, + name: dm.name, + variableName = dm.variableName, + parent: _.clone(baseParent), + }); + } +} diff --git a/app/server/migrations/migrations.js b/app/server/migrations/migrations.js index 383c8ec7..9c855c10 100644 --- a/app/server/migrations/migrations.js +++ b/app/server/migrations/migrations.js @@ -54,3 +54,132 @@ Migrations.add({ return; }, }); + +Migrations.add({ + version: 3, + name: "Moves all character attributes off the character document", + up: function () { + const batchSize = 50; + const stats = [ + // Abilities + "strength", + "dexterity", + "constitution", + "intelligence", + "wisdom", + "charisma", + + // Stats + "hitPoints", + "tempHP", + "experience", + "proficiencyBonus", + "speed", + "weight", + "age", + "ageRate", + "armor", + "carryMultiplier", + + // Resources + "level1SpellSlots", + "level2SpellSlots", + "level3SpellSlots", + "level4SpellSlots", + "level5SpellSlots", + "level6SpellSlots", + "level7SpellSlots", + "level8SpellSlots", + "level9SpellSlots", + "ki", + "sorceryPoints", + "rages", + "superiorityDice", + "expertiseDice", + "rageDamage", + + // Hit Dice + "d6HitDice", + "d8HitDice", + "d10HitDice", + "d12HitDice", + + // Damage Multipliers + "acidMultiplier", + "bludgeoningMultiplier", + "coldMultiplier", + "fireMultiplier", + "forceMultiplier", + "lightningMultiplier", + "necroticMultiplier", + "piercingMultiplier", + "poisonMultiplier", + "psychicMultiplier", + "radiantMultiplier", + "slashingMultiplier", + "thunderMultiplier", + + // Saves + "strengthSave", + "dexteritySave", + "constitutionSave", + "intelligenceSave", + "wisdomSave", + "charismaSave", + + // Skills + "acrobatics", + "animalHandling", + "arcana", + "athletics", + "deception", + "history", + "insight", + "intimidation", + "investigation", + "medicine", + "nature", + "perception", + "performance", + "persuasion", + "religion", + "sleightOfHand", + "stealth", + "survival", + "initiative", + "dexterityArmor", + ]; + let modifier = {$unset: {}}; + _.each(stats, stat => { + modifier.$unset[stat] = 1; + }); + let charIds, defaultDocs; + let migrateBatch = function(){ + // Iterate over a batch of characters at a time + charIds = Characters.find( + {strength: {$exists: true}}, + {fields: {_id: 1}, limit: batchSize}, + ).map(char => char._id); + if (!charIds.length) { + return; + } + _.each(charIds, charId => { + // Add all the stats to their own collections + defaultDocs = getDefaultCharacterDocs(charId); + Attributes.rawCollection().insert(defaultDocs.attributes, {ordered: false}); + Skills.rawCollection().insert(defaultDocs.skills, {ordered: false}); + DamageMultipliers.rawCollection().insert(defaultDocs.damageMultipliers, {ordered: false}); + // Remove the stats on the character document + Characters.update(charId, modifier, function(error, result){ + if (error) console.log(error); + }); + }); + // Do the next batch + Meteor.defer(migrateBatch); + }; + migrateBatch(); + }, + down: function () { + return; + } +});