Designing a Scalable Community Content System

Problem Statement

During development of the Ballistics puzzle game, I built a comprehensive level editing toolset for creating physics-based puzzle levels. This toolset enables efficient placement of game objects, configuration of gameplay parameters, and rapid iteration on level design. The natural progression was to democratize this capability—enabling players to create and share their own levels while maintaining quality standards and preventing abuse.

The core challenge involved designing a system that would allow user-generated content while addressing several critical constraints:

  • Ensuring published levels meet quality standards (solvability requirement)
  • Preventing statistical inconsistencies when levels are modified post-publication
  • Implementing efficient content discovery with multiple query patterns
  • Mitigating cost concerns through strategic resource gating
  • Preventing reward system abuse through infinite farming exploits

User Workflow and Feature Design

Content Creation and Management

Upon accessing the Community Levels interface from the main menu, users are presented with their draft levels and have access to community-published content. The workflow supports three primary operations:

  • Create New Level: Initializes a blank level in draft status
  • Duplicate Existing Level: Creates a copy of any level the user owns
  • Delete Level: Removes levels from the user's collection

Edit Mode Constraints

When editing a level, users have access to place up to 50 instances of each game object type, with one critical exception: coins are limited to 5 per level. This constraint exists because achieving three-star completion requires collecting all coins, and an excessive number would create unreasonable difficulty spikes that harm the player experience.

Configuration System

Level configuration occurs through a dedicated interface where creators specify:

  • Available Components: Quantity limits for each object type (tracks, portals, speed tracks, trampolines)
  • Component Tiers: Maximum upgrade tier available for each component type, aligning with the game's progression system
  • Active Skills: Up to three player-activated abilities with corresponding level specifications
  • Cannon Parameters: Power level and trajectory prediction accuracy
  • Visual Theme: Background selection for aesthetic customization

Quality Assurance Through "Play to Publish"

Validation Requirements

A fundamental design principle: every published level must be completable. To enforce this, I implemented a "Play to Publish" validation system. After configuring a level, creators must achieve three-star completion (all balls reaching targets, all coins collected) before the publish action becomes available.

The validation flow operates as follows:

  1. Creator clicks "Play to Publish," triggering an instantaneous mode transition from edit to play state
  2. All editing controls are removed; the level functions identically to standard gameplay
  3. Upon achieving three-star completion, the publish button activates
  4. Creator provides a level name and confirms publication
  5. System displays a warning regarding the immutability of published levels

Rationale for Immutability

Published levels are immutable by design. This decision addresses two critical data integrity concerns:

  • Statistical Validity: Levels accumulate metadata (likes, attempts, completion rates). Allowing post-publication edits would invalidate these metrics, particularly in scenarios where a level could be simplified after accumulating positive statistics or made more difficult after recording successful completions.
  • Player Progress Preservation: Individual player statistics (completion status, best time, star ratings) are tracked per level UUID. Modifications would invalidate these records, creating inconsistencies in player progression data.

To maintain flexibility, users can duplicate published levels, make modifications, delete the original, and publish the updated version—preserving iterative design capabilities while maintaining data integrity.

Data Architecture and Storage Strategy

DynamoDB Schema Design

I designed a DynamoDB schema to support multiple query patterns efficiently. The primary table structure uses:

  • Partition Key: user_id (Apple ID)
  • Sort Key: creation_time (integer timestamp)

Stored attributes include:

  • uuid: Level identifier (UUID4)
  • status: Either "draft" or "published"
  • level_name: User-provided string (converted to lowercase)
  • display_name: Creator's public username
  • likes: Integer count of player endorsements
  • attempts: Integer count of play attempts
  • rating: Calculated popularity score (see algorithm below)
  • level: MapAttribute containing complete level JSON data

Global Secondary Indexes

To support efficient content discovery, I implemented two GSIs:

GSI 1: status-score-index

  • Partition Key: status
  • Sort Key: rating

This index enables retrieval of published levels ordered by popularity score, supporting the default "Top Levels" view.

GSI 2: level-name-score-index

  • Partition Key: level_name
  • Sort Key: rating

This index supports name-based search using DynamoDB's begins_with operator on the partition key, enabling efficient prefix matching without requiring a full table scan. All level names are stored in lowercase to ensure case-insensitive search behavior.

Supported Query Patterns

The schema supports four primary access patterns:

  1. User's Own Levels: Query on primary key (user_id) returns all levels owned by the creator
  2. Top Published Levels: Query GSI 1 with status = "published", sorted by rating in descending order
  3. Search by Name: Query GSI 2 with begins_with on level_name, sorted by rating
  4. Levels by Creator: Query primary table with specific user_id and filter on status = "published" (acceptable performance given the 50-level-per-user maximum)

Scoring Algorithm Design

Complexity Trade-offs

The ideal scoring system would normalize metrics across all levels, comparing each level's statistics against global maxima. However, maintaining global maximum values (highest like count, most attempts, most completions) would require either:

  • Scanning the entire table on each update (unacceptable cost and latency)
  • Maintaining a separate aggregation table with complex synchronization logic

Logarithmic Scoring Approach

I opted for a logarithmic scoring algorithm that provides relative ranking without requiring global state tracking. The formula applies logarithmic scaling to three engagement metrics:

rating = (log₂(likes) × α) + (log₃(attempts) × β) + (log₅(wins) × γ)

Where:

  • likes: Number of player endorsements
  • attempts: Total play attempts
  • wins: Successful completions (one or more stars)
  • α, β, γ: Tunable weight coefficients for balancing metric influence

The logarithmic approach provides several advantages:

  • Diminishing returns prevent single metrics from dominating rankings
  • Different logarithm bases enable fine-tuning of metric relative importance
  • Calculation requires only local data, eliminating cross-record dependencies
  • The algorithm naturally handles edge cases (log of zero/small numbers)

Cost Optimization and Abuse Prevention

Energy-Based Rate Limiting

To prevent excessive iteration and reduce infrastructure costs, each "Play to Publish" attempt requires five energy units. This economic gate ensures creators invest meaningful effort in level design before validation attempts, reducing frivolous submissions and DynamoDB write operations.

Auto-Save Implementation

To balance user experience with cost management, I implemented an auto-save system with the following characteristics:

  • Save Interval: Two-minute periodic saves
  • Change Detection: Client-side JSON comparison against last saved state
  • Conditional Updates: DynamoDB writes occur only when content has changed

This approach provides crash protection (maximum two minutes of lost work) while minimizing unnecessary write operations to the database.

Reward System Design

Analysis revealed a significant abuse vector: if coin collection in community levels credited to player profiles, users could create trivial levels with five coins in close proximity, complete them repeatedly, delete and recreate the level, and farm coins indefinitely.

To mitigate this risk, community level completion rewards exclusively provide gems (premium currency not tied to progression gates). Coins remain obtainable only through standard game levels, preserving the progression economy's integrity.

Capacity Management

Users have a default creation limit of five community levels. This limit is expandable to 50 levels through in-app purchase, providing a monetization pathway while preventing database bloat from abandoned content. The low default limit also reduces the performance impact of filter operations when querying published levels by creator.

Lessons Learned

This implementation highlighted several key architectural insights:

  • Immutability Simplifies State Management: Preventing level edits post-publication eliminated complex data migration scenarios and statistical recalculation logic
  • Economic Gates Influence Behavior: Energy costs naturally encourage thoughtful iteration rather than trial-and-error approaches
  • Client-Side Filtering Can Be Acceptable: The 50-level-per-user cap makes client-side filtering viable for specific query patterns
  • Logarithmic Scoring Scales Well: The scoring algorithm provides meaningful rankings without requiring global state synchronization
  • Abuse Prevention Requires Economic Thinking: Understanding player incentives revealed the coin farming exploit before production deployment

Future Considerations

Areas for potential enhancement include:

  • Implementing a reporting system for inappropriate content
  • Adding tag-based categorization for improved content discovery
  • Developing difficulty estimation algorithms based on completion statistics
  • Creating featured level curation workflows for highlighting exceptional community content

Comments

Popular posts from this blog

Solving Aspect Ratio Problems For Good!!

Customizing the playing experience with special ball effects