11 KiB
11 KiB
BMS BattleMap Generator - Technical Architecture
System Overview
┌─────────────────────────────────────────────────────────────┐
│ Frontend Application │
├─────────────────────────────────────────────────────────────┤
│ Editor Interface Preview Canvas Tools Panel │
│ (Monaco Editor) (Konva.js Canvas) (Export/Options) │
├─────────────────────────────────────────────────────────────┤
│ State Management │
│ (React Hooks/Context) │
├─────────────────────────────────────────────────────────────┤
│ YAML Parser │ Schema Validator │ Renderer Engine │
│ (js-yaml) │ (Zod) │ (Konva.js Layers) │
└─────────────────────────────────────────────────────────────┘
Component Architecture
1. BMS Schema Module (src/schema/)
schema/
├── types.ts # TypeScript interfaces
├── validators.ts # Zod validation schemas
├── parsers.ts # Coordinate/range parsing
├── transformers.ts # Data transformation
└── index.ts # Public API
2. Parser Module (src/parser/)
parser/
├── yaml-parser.ts # BMS YAML parsing
├── coordinate-parser.ts # Grid coordinate parsing
├── validation.ts # Schema validation logic
├── errors.ts # Error types and messages
└── index.ts
3. Renderer Module (src/renderer/)
renderer/
├── base-renderer.ts # Abstract base class
├── paperly-renderer.ts # Paperly style implementation
├── texture-renderer.ts # Future: texture pack rendering
├── layers/ # Individual layer renderers
│ ├── grid-layer.ts
│ ├── room-layer.ts
│ ├── door-layer.ts
│ ├── object-layer.ts
│ ├── hazard-layer.ts
│ ├── lighting-layer.ts
│ └── annotation-layer.ts
├── styles/ # Style definitions
│ ├── paperly-style.ts
│ └── texture-styles.ts # Future
└── utils/ # Rendering utilities
├── geometry.ts
├── colors.ts
└── textures.ts
4. UI Components (src/components/)
components/
├── Editor/
│ ├── YAMLEditor.tsx # Monaco editor wrapper
│ └── EditorPanel.tsx # Editor with error display
├── Preview/
│ ├── MapCanvas.tsx # Konva canvas wrapper
│ └── PreviewPanel.tsx # Preview with controls
├── Tools/
│ ├── ExportMenu.tsx # Export options
│ ├── StylePicker.tsx # Style selection
│ └── ValidationPanel.tsx # Validation status
├── Layout/
│ ├── Header.tsx
│ ├── Sidebar.tsx
│ └── StatusBar.tsx
└── Common/
├── ErrorDisplay.tsx
├── LoadingSpinner.tsx
└── Tooltip.tsx
5. State Management (src/hooks/, src/context/)
hooks/
├── useBMS.ts # BMS schema state
├── useRenderer.ts # Renderer instance
├── useExport.ts # Export functionality
└── useUndoRedo.ts # Undo/redo history
context/
├── AppContext.tsx # Global app state
└── ThemeContext.tsx # Theme/stylesheet
Data Flow
1. Parsing Pipeline
User Input (YAML)
↓
YAML Parser (js-yaml)
↓
Schema Validator (Zod)
↓
BMSSchema Object
↓
Renderer Engine
↓
Canvas Output
2. Rendering Pipeline
BMSSchema
↓
Setup Canvas (grid size, scale)
↓
Render Layers (in order):
1. Grid Layer
2. Room Layer (walls, floors)
3. Door Layer
4. Object Layer
5. Hazard Layer
6. Lighting Layer
7. Annotation Layer
↓
Canvas Output
3. Export Pipeline
Canvas State
↓
Export Format Selection
↓
Format-Specific Exporter:
- PNG: canvas.toDataURL()
- JSON: JSON.stringify(BMSSchema)
- BMS: serializeBMSToYAML()
↓
File Download
Key Technical Decisions
1. Canvas Library: Konva.js
Why Konva.js over alternatives:
- React Integration: react-konva provides React component wrappers
- Performance: Hardware acceleration, efficient rendering
- Features: Layers, events, transforms, filters
- Size: Smaller than PIXI.js, more focused than raw Canvas API
- Maintenance: Active development, good documentation
2. Validation: Zod
Why Zod over alternatives:
- TypeScript Integration: Automatic type inference
- Runtime Safety: Comprehensive validation at runtime
- Error Messages: Customizable error messages
- Size: Small bundle size
- Schema Composition: Easy to compose and extend schemas
3. YAML Parsing: js-yaml
Why js-yaml:
- Browser Support: Works in browser environments
- Safety: Secure parsing options
- Performance: Fast and efficient
- Features: Full YAML 1.2 support
4. State Management: React Hooks
Why React Hooks over Redux/MobX:
- Simplicity: Less boilerplate for MVP
- Performance: Optimized re-renders with useMemo/useCallback
- Scalability: Can migrate to Context or Zustand if needed
- Learning Curve: Familiar to React developers
Performance Considerations
1. Rendering Optimization
// Use Konva.js optimizations
const optimizedRender = () => {
// Batch updates
stage.batchDraw();
// Use caching for static elements
shape.cache();
// Implement viewport culling
if (isInViewport(element)) {
renderElement(element);
}
};
2. Memory Management
// Clean up resources
useEffect(() => {
const renderer = new PaperlyRenderer();
return () => {
renderer.destroy(); // Clean up Konva objects
};
}, []);
3. Large Map Handling
// Implement level of detail
const getDetailLevel = (zoom: number): DetailLevel => {
if (zoom < 0.5) return 'low';
if (zoom < 1.0) return 'medium';
return 'high';
};
// Progressive rendering
const renderProgressive = async (schema: BMSSchema) => {
// Render grid first
renderGrid();
// Render rooms
await renderRooms(schema.rooms);
// Render details progressively
requestIdleCallback(() => {
renderDoors(schema.doors);
renderObjects(schema.objects);
});
};
Security Considerations
1. YAML Parsing Security
import { load } from 'js-yaml';
// Safe YAML parsing
const parseBMSYAML = (yaml: string): BMSSchema => {
try {
// Use safeLoad to prevent code execution
const parsed = load(yaml, {
schema: yaml.DEFAULT_SAFE_SCHEMA,
onWarning: (warning) => console.warn(warning)
});
// Validate structure
return bmsSchema.parse(parsed);
} catch (error) {
throw new BMSValidationError('Invalid BMS YAML', error);
}
};
2. File Export Security
// Safe file naming
const sanitizeFilename = (name: string): string => {
return name.replace(/[^a-z0-9-_.]/gi, '_');
};
// Content security for downloads
const downloadFile = (content: string, filename: string) => {
const blob = new Blob([content], { type: 'application/octet-stream' });
const url = URL.createObjectURL(blob);
// Use safe download pattern
const a = document.createElement('a');
a.href = url;
a.download = sanitizeFilename(filename);
a.click();
URL.revokeObjectURL(url);
};
Testing Strategy
1. Unit Tests
// Test coordinate parsing
describe('parseCoordinate', () => {
it('should parse valid coordinates', () => {
expect(parseCoordinate('(5,10)')).toEqual([5, 10]);
});
it('should throw error for invalid format', () => {
expect(() => parseCoordinate('5,10')).toThrow();
});
});
2. Integration Tests
// Test full parsing pipeline
describe('BMS Parser Integration', () => {
it('should parse and validate complete BMS', () => {
const yaml = sampleBMSYAML;
const schema = parseBMSYAML(yaml);
expect(schema.mapName).toBe('Guard Room');
expect(schema.rooms).toHaveLength(1);
});
});
3. Visual Regression Tests
// Compare rendered output
describe('Renderer Visual Tests', () => {
it('should render rooms correctly', async () => {
const schema = createTestSchema();
const renderer = new PaperlyRenderer();
const image = await renderer.renderToImage(schema);
// Compare with baseline
expect(image).toMatchImageSnapshot();
});
});
Deployment Architecture
1. Development Environment
Local Machine → Vite Dev Server → Browser
2. Alpha Deployment
Git Repository → Build (Vite) → Static Files → maps.bouncypixel.com
3. Future Production Deployment
GitHub Actions → Build & Test → CDN (Vercel/Netlify) → Custom Domain
↘ Texture Packs → Cloud Storage (S3/Cloudflare)
Scalability Considerations
1. Frontend Scaling
- Code Splitting: Lazy load components
- Bundle Optimization: Tree shaking, compression
- CDN: Static assets via CDN
2. Rendering Scaling
- Virtualization: Only render visible area
- Web Workers: Offload heavy computation
- Caching: Cache rendered elements
3. Data Scaling
- IndexedDB: Local storage for large maps
- Compression: Compress BMS data
- Streaming: Progressive loading
Monitoring & Analytics
1. Performance Monitoring
// Track rendering performance
const measureRender = (schema: BMSSchema) => {
performance.mark('render-start');
renderer.render(schema);
performance.mark('render-end');
const measure = performance.measure('render', 'render-start', 'render-end');
console.log(`Render time: ${measure.duration}ms`);
};
2. Error Tracking
// Centralized error handling
class ErrorTracker {
static track(error: Error, context?: any) {
console.error('BMS Error:', error, context);
// Send to error tracking service (future)
}
}
3. Usage Analytics (Optional)
// Basic usage tracking
const trackEvent = (event: string, data?: any) => {
if (process.env.NODE_ENV === 'production') {
// Send to analytics service
}
};
Future Technical Considerations
1. Plugin System
interface BMSPlugin {
name: string;
version: string;
init: (app: BMSApp) => void;
render?: (context: RenderContext) => void;
export?: (schema: BMSSchema) => any;
}
2. Collaborative Features
- WebSocket: Real-time collaboration
- Conflict Resolution: Operational transforms
- Presence: User cursor/selection
3. Offline Support
- Service Workers: Offline functionality
- Local Storage: Save work locally
- Sync: Background synchronization
Last Updated: March 19, 2026