Achievements are now in 'achievements.hjson'
+ Config.general.achievementFile * Implement (re)caching (aka hot-reload) * Update values a bit
This commit is contained in:
parent
c332b0f3ec
commit
2bd51c0725
|
@ -0,0 +1,53 @@
|
||||||
|
{
|
||||||
|
enabled : true,
|
||||||
|
|
||||||
|
art : {
|
||||||
|
localHeader : 'achievement_local_header',
|
||||||
|
localFooter : 'achievement_local_footer',
|
||||||
|
globalHeader : 'achievement_global_header',
|
||||||
|
globalFooter : 'achievement_global_footer',
|
||||||
|
},
|
||||||
|
|
||||||
|
// :TODO: achievements should be a path/filename -> achievements.hjson & allow override/theming
|
||||||
|
|
||||||
|
achievements : {
|
||||||
|
user_login_count : {
|
||||||
|
type : 'userStat',
|
||||||
|
statName : 'login_count',
|
||||||
|
retroactive : true,
|
||||||
|
|
||||||
|
match : {
|
||||||
|
2 : {
|
||||||
|
title : 'Return Caller',
|
||||||
|
globalText : '{userName} has returned to {boardName}!',
|
||||||
|
text : 'You\'ve returned to {boardName}!',
|
||||||
|
points : 5,
|
||||||
|
},
|
||||||
|
10 : {
|
||||||
|
title : '{achievedValue} Logins',
|
||||||
|
globalText : '{userName} has logged into {boardName} {achievedValue} times!',
|
||||||
|
text : 'You\'ve logged into {boardName} {achievedValue} times!',
|
||||||
|
points : 5,
|
||||||
|
},
|
||||||
|
25 : {
|
||||||
|
title : '{achievedValue} Logins',
|
||||||
|
globalText : '{userName} has logged into {boardName} {achievedValue} times!',
|
||||||
|
text : 'You\'ve logged into {boardName} {achievedValue} times!',
|
||||||
|
points : 10,
|
||||||
|
},
|
||||||
|
100 : {
|
||||||
|
title : '{boardName} Regular',
|
||||||
|
globalText : '{userName} has logged into {boardName} {achievedValue} times!',
|
||||||
|
text : 'You\'ve logged into {boardName} {achievedValue} times!',
|
||||||
|
points : 10,
|
||||||
|
},
|
||||||
|
500 : {
|
||||||
|
title : '{boardName} Addict',
|
||||||
|
globalText : '{userName} the BBS {boardName} addict has logged in {achievedValue} times!',
|
||||||
|
text : 'You\'re a {boardName} addict! You\'ve logged in {achievedValue} times!',
|
||||||
|
points : 25,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,10 @@
|
||||||
// ENiGMA½
|
// ENiGMA½
|
||||||
const Events = require('./events.js');
|
const Events = require('./events.js');
|
||||||
const Config = require('./config.js').get;
|
const Config = require('./config.js').get;
|
||||||
|
const {
|
||||||
|
getConfigPath,
|
||||||
|
getFullConfig,
|
||||||
|
} = require('./config_util.js');
|
||||||
const UserDb = require('./database.js').dbs.user;
|
const UserDb = require('./database.js').dbs.user;
|
||||||
const {
|
const {
|
||||||
getISOTimestampString
|
getISOTimestampString
|
||||||
|
@ -22,11 +26,13 @@ const { pipeToAnsi } = require('./color_codes.js');
|
||||||
const stringFormat = require('./string_format.js');
|
const stringFormat = require('./string_format.js');
|
||||||
const StatLog = require('./stat_log.js');
|
const StatLog = require('./stat_log.js');
|
||||||
const Log = require('./logger.js').log;
|
const Log = require('./logger.js').log;
|
||||||
|
const ConfigCache = require('./config_cache.js');
|
||||||
|
|
||||||
// deps
|
// deps
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const async = require('async');
|
const async = require('async');
|
||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
|
const paths = require('path');
|
||||||
|
|
||||||
class Achievement {
|
class Achievement {
|
||||||
constructor(data) {
|
constructor(data) {
|
||||||
|
@ -107,11 +113,56 @@ class Achievements {
|
||||||
}
|
}
|
||||||
|
|
||||||
init(cb) {
|
init(cb) {
|
||||||
|
let achievementConfigPath = _.get(Config(), 'general.achievementFile');
|
||||||
|
if(!achievementConfigPath) {
|
||||||
|
// :TODO: Log me
|
||||||
|
return cb(null);
|
||||||
|
}
|
||||||
|
achievementConfigPath = getConfigPath(achievementConfigPath); // qualify
|
||||||
|
|
||||||
|
// :TODO: Log enabled
|
||||||
|
|
||||||
|
const configLoaded = (achievementConfig) => {
|
||||||
|
if(true !== achievementConfig.enabled) {
|
||||||
|
this.stopMonitoringUserStatUpdateEvents();
|
||||||
|
delete this.achievementConfig;
|
||||||
|
} else {
|
||||||
|
this.achievementConfig = achievementConfig;
|
||||||
|
this.monitorUserStatUpdateEvents();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const changed = ( { fileName, fileRoot } ) => {
|
||||||
|
const reCachedPath = paths.join(fileRoot, fileName);
|
||||||
|
if(reCachedPath === achievementConfigPath) {
|
||||||
|
getFullConfig(achievementConfigPath, (err, achievementConfig) => {
|
||||||
|
if(err) {
|
||||||
|
return Log.error( { error : err.message }, 'Failed to reload achievement config from cache');
|
||||||
|
}
|
||||||
|
configLoaded(achievementConfig);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ConfigCache.getConfigWithOptions(
|
||||||
|
{
|
||||||
|
filePath : achievementConfigPath,
|
||||||
|
forceReCache : true,
|
||||||
|
callback : changed,
|
||||||
|
},
|
||||||
|
(err, achievementConfig) => {
|
||||||
|
if(err) {
|
||||||
|
return cb(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
configLoaded(achievementConfig);
|
||||||
|
return cb(null);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// :TODO: if enabled/etc., load achievements.hjson -> if theme achievements.hjson{}, merge @ display time?
|
// :TODO: if enabled/etc., load achievements.hjson -> if theme achievements.hjson{}, merge @ display time?
|
||||||
// merge for local vs global (per theme) clients
|
// merge for local vs global (per theme) clients
|
||||||
// ...only merge/override text
|
// ...only merge/override text
|
||||||
this.monitorUserStatUpdateEvents();
|
|
||||||
return cb(null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
loadAchievementHitCount(user, achievementTag, field, cb) {
|
loadAchievementHitCount(user, achievementTag, field, cb) {
|
||||||
|
@ -139,7 +190,7 @@ class Achievements {
|
||||||
return cb(err);
|
return cb(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
Events.emit(
|
this.events.emit(
|
||||||
Events.getSystemEvents().UserAchievementEarned,
|
Events.getSystemEvents().UserAchievementEarned,
|
||||||
{
|
{
|
||||||
user : info.client.user,
|
user : info.client.user,
|
||||||
|
@ -172,7 +223,11 @@ class Achievements {
|
||||||
}
|
}
|
||||||
|
|
||||||
monitorUserStatUpdateEvents() {
|
monitorUserStatUpdateEvents() {
|
||||||
this.events.on(Events.getSystemEvents().UserStatUpdate, userStatEvent => {
|
if(this.userStatEventListener) {
|
||||||
|
return; // already listening
|
||||||
|
}
|
||||||
|
|
||||||
|
this.userStatEventListener = this.events.on(Events.getSystemEvents().UserStatUpdate, userStatEvent => {
|
||||||
if([ UserProps.AchievementTotalCount, UserProps.AchievementTotalPoints ].includes(userStatEvent.statName)) {
|
if([ UserProps.AchievementTotalCount, UserProps.AchievementTotalPoints ].includes(userStatEvent.statName)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -182,10 +237,9 @@ class Achievements {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const config = Config();
|
|
||||||
// :TODO: Make this code generic - find + return factory created object
|
// :TODO: Make this code generic - find + return factory created object
|
||||||
const achievementTag = _.findKey(
|
const achievementTag = _.findKey(
|
||||||
_.get(config, 'userAchievements.achievements', {}),
|
_.get(this.achievementConfig, 'achievements', {}),
|
||||||
achievement => {
|
achievement => {
|
||||||
if(false === achievement.enabled) {
|
if(false === achievement.enabled) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -199,7 +253,7 @@ class Achievements {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const achievement = Achievement.factory(config.userAchievements.achievements[achievementTag]);
|
const achievement = Achievement.factory(this.achievementConfig.achievements[achievementTag]);
|
||||||
if(!achievement) {
|
if(!achievement) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -230,10 +284,11 @@ class Achievements {
|
||||||
achievement,
|
achievement,
|
||||||
details,
|
details,
|
||||||
client,
|
client,
|
||||||
matchField,
|
matchField, // match - may be in odd format
|
||||||
matchValue,
|
matchValue, // actual value
|
||||||
user : userStatEvent.user,
|
achievedValue : matchField, // achievement value met
|
||||||
timestamp : moment(),
|
user : userStatEvent.user,
|
||||||
|
timestamp : moment(),
|
||||||
};
|
};
|
||||||
|
|
||||||
return callback(null, info);
|
return callback(null, info);
|
||||||
|
@ -256,6 +311,13 @@ class Achievements {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stopMonitoringUserStatUpdateEvents() {
|
||||||
|
if(this.userStatEventListener) {
|
||||||
|
this.events.removeListener(Events.getSystemEvents().UserStatUpdate, this.userStatEventListener);
|
||||||
|
delete this.userStatEventListener;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
createAchievementInterruptItems(info, cb) {
|
createAchievementInterruptItems(info, cb) {
|
||||||
const dateTimeFormat =
|
const dateTimeFormat =
|
||||||
info.details.dateTimeFormat ||
|
info.details.dateTimeFormat ||
|
||||||
|
@ -291,7 +353,7 @@ class Achievements {
|
||||||
const spec =
|
const spec =
|
||||||
_.get(info.details, `art.${name}`) ||
|
_.get(info.details, `art.${name}`) ||
|
||||||
_.get(info.achievement, `art.${name}`) ||
|
_.get(info.achievement, `art.${name}`) ||
|
||||||
_.get(config, `userAchievements.art.${name}`);
|
_.get(this.achievementConfig, `art.${name}`);
|
||||||
if(!spec) {
|
if(!spec) {
|
||||||
return callback(null);
|
return callback(null);
|
||||||
}
|
}
|
||||||
|
@ -351,12 +413,6 @@ class Achievements {
|
||||||
let achievements;
|
let achievements;
|
||||||
|
|
||||||
exports.moduleInitialize = (initInfo, cb) => {
|
exports.moduleInitialize = (initInfo, cb) => {
|
||||||
|
|
||||||
if(false === _.get(Config(), 'userAchievements.enabled')) {
|
|
||||||
// :TODO: Log disabled
|
|
||||||
return cb(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
achievements = new Achievements(initInfo.events);
|
achievements = new Achievements(initInfo.events);
|
||||||
return achievements.init(cb);
|
return achievements.init(cb);
|
||||||
};
|
};
|
||||||
|
|
|
@ -175,6 +175,7 @@ function getDefaultConfig() {
|
||||||
|
|
||||||
menuFile : 'menu.hjson', // 'oputil.js config new' will set this appropriately in config.hjson; may be full path
|
menuFile : 'menu.hjson', // 'oputil.js config new' will set this appropriately in config.hjson; may be full path
|
||||||
promptFile : 'prompt.hjson', // 'oputil.js config new' will set this appropriately in config.hjson; may be full path
|
promptFile : 'prompt.hjson', // 'oputil.js config new' will set this appropriately in config.hjson; may be full path
|
||||||
|
achievementFile : 'achievements.hjson',
|
||||||
},
|
},
|
||||||
|
|
||||||
users : {
|
users : {
|
||||||
|
@ -1004,46 +1005,5 @@ function getDefaultConfig() {
|
||||||
loginHistoryMax: -1, // set to -1 for forever
|
loginHistoryMax: -1, // set to -1 for forever
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
userAchievements : {
|
|
||||||
enabled : true,
|
|
||||||
|
|
||||||
art : {
|
|
||||||
localHeader : 'achievement_local_header',
|
|
||||||
localFooter : 'achievement_local_footer',
|
|
||||||
globalHeader : 'achievement_global_header',
|
|
||||||
globalFooter : 'achievement_global_footer',
|
|
||||||
},
|
|
||||||
|
|
||||||
// :TODO: achievements should be a path/filename -> achievements.hjson & allow override/theming
|
|
||||||
|
|
||||||
achievements : {
|
|
||||||
user_login_count : {
|
|
||||||
type : 'userStat',
|
|
||||||
statName : 'login_count',
|
|
||||||
retroactive : true,
|
|
||||||
match : {
|
|
||||||
10 : {
|
|
||||||
title : 'Return Caller',
|
|
||||||
globalText : '{userName} has logged in {matchValue} times!',
|
|
||||||
text : 'You\'ve logged in {matchValue} times!',
|
|
||||||
points : 5,
|
|
||||||
},
|
|
||||||
25 : {
|
|
||||||
title : 'Seems To Like It!',
|
|
||||||
globalText : '{userName} has logged in {matchValue} times!',
|
|
||||||
text : 'You\'ve logged in {matchValue} times!',
|
|
||||||
points : 10,
|
|
||||||
},
|
|
||||||
100 : {
|
|
||||||
title : '{boardName} Addict',
|
|
||||||
globalText : '{userName} the BBS {boardName} addict has logged in {matchValue} times!',
|
|
||||||
text : 'You\'re a {boardName} addict! You\'ve logged in {matchValue} times!',
|
|
||||||
points : 10,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ const paths = require('path');
|
||||||
const async = require('async');
|
const async = require('async');
|
||||||
|
|
||||||
exports.init = init;
|
exports.init = init;
|
||||||
|
exports.getConfigPath = getConfigPath;
|
||||||
exports.getFullConfig = getFullConfig;
|
exports.getFullConfig = getFullConfig;
|
||||||
|
|
||||||
function getConfigPath(filePath) {
|
function getConfigPath(filePath) {
|
||||||
|
|
Loading…
Reference in New Issue