1

I'm looking at keeping track of scores/points per user. From examples I've seen, it seems normal to just keep track of a total count of something in a field. However, I'm concerned about being able to backtrack or keep track of the scores/points given to the user in case of cheating. Here's what I've got in mind:

Meteor.User Collection:

Meteor.user.profile: {
    ...
    totalScore: 0
    ...
}

Scenario 1: Just add total score and keep track of it per user:

updateScore() {
       var currentUser = Meteor.user();

       currentUser.profile.update({ _id: this._id }{$inc: { totalScore: 1} });
}

Scenario 1: Put score into separate Collection first to log it, before adding to total score of user:

Scores Collection:

Scores: {
    playerId: ,
    score: ,
    ...
}


updateScore() {
       var currentUser = Meteor.user();
       Scores.insert({ playerId: this._id, score: 1, ...});
       currentUser.profile.update({ _id: this._id }{$inc: { totalScore: 1} });

       //if not the above, then thisor this

       var currentUserScore = Calculate total score count from Scores collection of current user here
       Meteor.user.profile.update({ _id: this._id }{$set: { totalScore: currentUserScore} });

}

So what I'd like to know is, does Scenario 2 make sense vs. Scenario 1? And if Scenario 2 makes sense, if I calculate for the total score via the variable currentUserScore then use that to update the user's totalScore profile field (this runs every time the score needs to be updated), will this be detrimental to the app's performance?

shibekin69
  • 31
  • 5
  • Some questions about the parameters of the problem: If a player was going to cheat, how would he/she do it? Are the scores always incremented by exactly 1? Is there any value in displaying a score history or would you want this strictly for audit purposes? How common would score increments be (is this like pinball or like stack overflow)? – David Weldon Sep 08 '14 at 06:00
  • Hi David, answering your questions 1. cheating or maybe gaming the system - not sure how they're going to do it but in case they do, I was thinking a history of transactions might be useful. 2. As for scores always being incremented by 1 - this was just an example :). 3. Probably want to put more use in the history (like for example, a backend or frontend viewer for whatever purpose) 4. maybe something like stack overflow. – shibekin69 Sep 08 '14 at 14:07
  • I just wasn't sure if this .aggregate() function I've been reading about will take so much processing power... I did come to the conclusion of having a separate table for such logs to be useful (for statistical info and such) - even if only to subscribe to the log to get the current logged in user's data. – shibekin69 Sep 08 '14 at 14:12

1 Answers1

1

Based on our discussion, Scenario 2 makes the most sense to me, especially given that the score history may have value outside of auditing the total. Keep in mind it's always easier to remove data than it is to create it, so even if the history doesn't prove useful there is no harm in removing the collection sometime later.

I would implement an addScore method like this:

Meteor.methods({
  addScore: function(score) {
    check(score, Number);

    Meteor.users.update(this.userId, {
      $inc: {'profile.totalScore': score}
    });

    Scores.insert({
      playerId: this.userId,
      score: score,
      createdAt: new Date()
    });
  }
});

Unless you can think of a compelling reason to do so, I suspect the db/computation overhead of aggregating the totalScore each time would not be worthwile. Doing so only fixes the case where a user cheated by updating her profile directly. You can solve that by adding the following:

Meteor.users.deny({
  update: function() {
    return true;
  }
});

I'd recommend adding the above regardless of the solution you go with as user profiles can be updated directly by the user even if insecure has been removed. See this section of the docs for more details.

Finally, if you want to audit the totalScore for each user you can aggregate the totals as part of a nightly process rather than each time a new score is added. You can do this on the server by fetching the Scores documents, or directly in mongodb with aggregation. Note the latter would require you to use a process outside of meteor (my understanding is that the aggregation packages for meteor don't currently work but you may want to research that yourself).

Community
  • 1
  • 1
David Weldon
  • 63,632
  • 11
  • 148
  • 146
  • Thanks for the code suggestion David. I ended up with more or less the same thing - hehe, but your suggestion confirms what I had in mind. I was thinking of only keeping 'redundant' data in 2 Collections max to avoid confusion - like the above - total score in one Collection, and individual scores in another. Yeah, it seems pre-calculating things these days is something to seriously consider, given we don't exactly have the luxury of resources. – shibekin69 Sep 09 '14 at 03:29
  • Oh one question about the above deny rule. Since I do want to give users access to their 'profile page', I can still deny updates to specific fields no? – shibekin69 Sep 09 '14 at 03:35
  • Denying the update will still *publish* the data. You can stick with the above and use a method to modify user profiles, or you can check the `fieldNames` parameter to allow/deny and allow client-side modifications. – David Weldon Sep 09 '14 at 07:21