DEV Community

Piler
Piler

Posted on

[Design Pattern] Observer pattern for achievement System

I would use Node.js as example.

eventBus.js

const EventEmitter = require('events');
const eventBus = new EventEmitter();
module.exports = eventBus;
Enter fullscreen mode Exit fullscreen mode

account.js

const eventBus = require("../eventBus");
function register(uid) {
    eventBus.emit("onRegister", { uid, time: Date.now() });
}
Enter fullscreen mode Exit fullscreen mode

gacha.js

const eventBus = require("../eventBus");
function gacha(uid) {
    eventBus.emit("onGacha", { uid, grade});

}
Enter fullscreen mode Exit fullscreen mode

achievement.js

const eventBus = require("../eventBus");
eventBus.on("onRegister", (args) => {
  createUserAchievements(args.uid);
});

eventBus.on("onGacha", (args) => {
  updateProgress(args.uid, `gacha`);
  updateProgress(args.uid, `gacha:${args.grade}`);

});

async function updateProgress(uid, type, count = 1) {
  console.log(`[userId:${uid}] updateProgress: ${type}`);

  try {
    // Fetch user achievements
    const userAchievementsData = await UserAchievement.findOne({ userId: uid });
    if (!userAchievementsData || !Array.isArray(userAchievementsData.achievements)) {
      console.error(`[userId:${uid}] No achievements data found`);
      return;
    }

    // Iterate through user achievements
    let hasUpdates = false;
    for (const userAchievement of userAchievementsData.achievements) {
      // Skip achievements not in progress
      if (userAchievement.status !== "in_progress") continue;

      // Fetch the related achievement
      const achievement = await Achievement.findById(userAchievement.achievementId);
      if (!achievement) {
        console.error(`[userId:${uid}] Achievement not found: ${userAchievement.achievementId}`);
        continue;
      }

      // Update progress if type matches
      if (type === achievement.requirements.type) {
        userAchievement.progress += count;
        hasUpdates = true;

        // Check for completion
        if (userAchievement.progress >= achievement.requirements.count) {
          userAchievement.status = "completed";
          console.log(`[userId:${uid}] Achievement "${achievement.en}" completed`);
        }
      }
    }

    // Save updates if any changes were made
    if (hasUpdates) {
      await userAchievementsData.save();
      console.log(`[userId:${uid}] Progress updated successfully`);
    } else {
      console.log(`[userId:${uid}] No progress updates required`);
    }
  } catch (error) {
    console.error(`[userId:${uid}] Error updating progress:`, error);
  }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)