Building a Plug‑in‑Based Resource Manager for Web Games and Live Events

This article presents a plug‑in‑based, WebWorker‑enabled resource manager for web games and live‑stream activities, detailing its background, research of existing solutions, overall design, core modules, external APIs, and practical usage examples such as preloading, modular bundles, and image conversion.

NetEase Cloud Music Tech Team
NetEase Cloud Music Tech Team
NetEase Cloud Music Tech Team
Building a Plug‑in‑Based Resource Manager for Web Games and Live Events

Background

Applications consist of code and resources (configuration files, icons, images, fonts, etc.) that affect package size and runtime performance. In social live‑stream development two scenarios require strong resource management: game development, which uses many media assets, and activity operations that rely on preloading and caching to improve page performance and user experience.

Research

Typical game frameworks provide comprehensive resource‑management features such as loading, lookup, destruction, caching, multi‑file configuration, process monitoring, modularization, preloading, and custom handling. Existing solutions fall into two categories: resource‑preloading libraries and full‑featured game resource managers. Many are tightly coupled to specific engines or lack support for pre‑request scenarios.

Overall Design

The proposed manager is engine‑agnostic and built on a plug‑in architecture. Core functionality resides in a central module, while optional features (preloading, conversion, caching) are added as plug‑ins, keeping the main bundle size stable.

Dependency Capabilities

The manager relies on an Extension module for plug‑in registration and on WebWorker for multi‑threaded resource parsing and conversion, preventing main‑thread stalls during heavy asset processing.

import WorkController from 'music/WorkController';

const MAX_WORKER_NUM = navigator.hardwareConcurrency || 6;

const loadBufferImageCode = `
  async function loadBufferImage(url) {
    const result = await fetch(url);
    if (!result.ok) {
      throw new Error('failed to load');
    }
    const imageBuffer = await result.arrayBuffer();
    return imageBuffer;
  }
  onmessage = async (e) => {
    const { data: { uuid, id } } = e;
    try {
      const bufferImage = await loadBufferImage(e.data.data[0]);
      postMessage({ data: bufferImage, uuid, id }, [bufferImage]);
    } catch (error) {
      postMessage({ error, uuid, id });
    }
  };
`;

let worker = WorkController.workerPool.pop();
if (!worker && WorkController.WorkersNumber < MAX_WORKER_NUM) {
  const workerURL = URL.createObjectURL(new Blob([loadBufferImageCode], { type: 'application/javascript' }));
  WorkController.WorkersNumber++;
  worker = new Worker(workerURL);
  worker.addEventListener('message', (event) => {
    WorkController.complete(event.data);
    WorkController.next();
  });
}

Core Modules

For each resource type the manager performs detection, mapping, loading, parsing, and caching. Not all steps are mandatory; for example, pre‑request resources skip caching because the browser handles it.

External Interfaces

Two main interfaces are provided: Resource for global assets and Cache for caching. Resources are further divided into Resource and Bundle, enabling both global and modular asset operations.

Feature Usage

Preloading

Preloading uses idle browser time to fetch assets likely needed soon, reducing perceived load time and visual flicker.

const loadAssets = [
  { src: 'https://someurl.png', type: 'IMAGE' },
  { src: 'https://someurl.mp3', type: 'AUDIO' },
  { src: 'http://someurl.mp4', type: 'VIDEO' },
  { src: 'https://someurl.ttf', type: 'FONT', subType: 'ttf' },
  { src: 'https://someurl.json', type: 'JSON' }
];

const LoadingPage = () => {
  const [progress, setProgress] = React.useState(0);
  React.useEffect(() => {
    const load = async () => {
      await Resource.loadResource(loadAssets, (p) => {
        const num = Number(p.toString().match(/^\d+(?:\.\d{0,2})?/));
        setProgress(num * 100);
      });
    };
    load();
  }, []);
  return <p>Resource loading progress: {progress}%</p>;
};

Resource Modularization

Assets can be grouped into bundles (e.g., first‑scene, next‑scene) and loaded on demand to reduce initial download size.

// Add bundles
Resource.addBundle('first-scene', {
  mainBg: 'backgroundA.png',
  avatar: 'avatarA.png',
  font: 'fontA.ttf'
});
Resource.addBundle('next-scene', {
  mainBg: 'backgroundB.png',
  avatar: 'avatarB.png',
  font: 'fontB.ttf'
});

// Load bundles
const firstSceneResource = await Resource.loadBundle('first-scene');
const nextSceneResource = await Resource.loadBundle('next-scene');

Resource Conversion

Image conversion plug‑ins can transform assets into formats such as Bitmap, Blob, PixiTexture, etc.

import ResourceImagePlugin from 'resource-image-plugin';
Resource.addPlugin(ResourceImagePlugin);

const res = await Resource.loadResource({
  src: 'https://p5.music.126.net/.../image.png',
  formatType: 'Bitmap'
});

Conclusion

The manager has been deployed in multiple social‑live‑streaming services, providing out‑of‑the‑box asset handling for the Alice.js engine and preloading capabilities for activity pages. Future work includes support for 3D models and intelligent loading strategies.

References

https://github.com/cocos/cocos-engine

https://github.com/pixijs/pixijs

frontendgame developmentplugin architectureresource managementWeb Workerpreload
NetEase Cloud Music Tech Team
Written by

NetEase Cloud Music Tech Team

Official account of NetEase Cloud Music Tech Team

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.