The Marketplace system allows users to subscribe to community-created scripts, modules, and other content, with automatic updates and dependency management.
Overview
The MarketplaceManager (features/marketplace/MarketplaceManager.kt:42) handles:
Browsing marketplace items
Subscribing to items
Automatic updates
Version management
Local file storage
Marketplace Items
Item Types
Supported via MarketplaceItemType:
Scripts - Custom JavaScript/Kotlin scripts
Modules - Pre-built module packages
Other content types (extensible)
Each type has its own reload mechanism (MarketplaceManager.kt:122).
Subscribed Items
Subscribed items are tracked with:
data class SubscribedItem (
val id: Int ,
val type: MarketplaceItemType ,
// ... additional metadata
)
Stored in config:
val subscribedItems by list ( "subscribed" , mutableListOf < SubscribedItem >(), ValueType.SUBSCRIBED_ITEM)
File Storage
Marketplace content is stored in a dedicated directory:
val marketplaceRoot = File (ConfigSystem.rootFolder, "marketplace" ). apply {
mkdirs ()
}
Each item gets its own subdirectory in marketplaceRoot.
Subscribing to Items
Browse Marketplace
Browse available items from the marketplace API
Subscribe
MarketplaceManager. subscribe (item)
Checks if already subscribed before adding (MarketplaceManager.kt:102-111)
Download
Automatically downloads the newest revision of the item
Install
Item is installed to the marketplace directory
Save Config
Subscription is saved to config for persistence
Subscribe Implementation
suspend fun subscribe (item: MarketplaceItem ) {
if ( isSubscribed (item.id)) {
return
}
val item = SubscribedItem (item)
subscribedItems. add (item)
item. install (item. getNewestRevisionId () ?: return )
ConfigSystem. store ( this )
}
Unsubscribing
Remove items and clean up files:
suspend fun unsubscribe (itemId: Int )
Find Item
Locates the subscribed item by ID
Delete Files
Recursively deletes the item’s directory (MarketplaceManager.kt:116)
Remove from List
Removes from subscribed items list
Reload Type Manager
Reloads the appropriate manager (scripts, modules, etc.)
Save Config
Persists changes to config
check ( ! item.itemDir. exists () || item.itemDir. deleteRecursively ()) {
"Failed to delete item directory"
}
Update System
Checking for Updates
val updateRevisionId = item. checkUpdate ()
if (updateRevisionId != null ) {
// Update available
}
Manual Update
Update a specific item:
suspend fun update (
item: SubscribedItem ,
task: Task ? = null ,
command: Command ? = null
)
The function (MarketplaceManager.kt:64-100):
Checks for updates
Downloads new revision if available
Installs the update
Reports progress via task or command
Handles errors gracefully
Update All Items
Update all subscribed items:
suspend fun updateAll (task: Task ? = null , command: Command ? = null ) {
subscribedItems. toList (). forEach { item ->
update (item, task, command)
}
}
The .toList() creates a copy to avoid concurrent modification issues during updates.
Progress Tracking
Task Integration
Updates can be tracked with tasks:
val subTask = task?. getOrCreateFileTask (item.id. toString ())
item. install (updateRevisionId, subTask)
subTask?.isCompleted = true
This provides:
Progress indicators
File download tracking
Completion status
Command Feedback
Updates provide feedback through commands:
command?. run {
chat ( regular (command. result ( "updating" , variable (item.id. toString ()))))
}
// On success
command?. run {
chat ( regular (
command. result ( "success" ,
variable (item.id. toString ()),
variable (updateRevisionId. toString ())
)
))
}
Error Handling
Comprehensive error handling for reliability:
. onFailure { e ->
logger. error ( "Failed to update item ${ item.id } " , e)
if (command != null ) {
chat ( markAsError (
translation (
"liquidbounce.command.marketplace.error.updateFailed" ,
item.id,
e.message ?: "Unknown error"
)
))
}
}
Errors are:
Logged for debugging
Displayed to user via chat
Don’t prevent other items from updating
Querying Items
By Type
val scripts = MarketplaceManager. getSubscribedItemsOfType (MarketplaceItemType.SCRIPT)
By ID
val item = MarketplaceManager. getItem (itemId)
Check Subscription
if (MarketplaceManager. isSubscribed (itemId)) {
// Already subscribed
}
Configuration
Subscriptions are persisted in the config system:
object MarketplaceManager : Config ( "marketplace" ), EventListener
This ensures:
Subscriptions survive client restarts
Automatic update tracking
Version history
Revision Management
Each item tracks its installed revision:
// Get newest available revision
val revisionId = item. getNewestRevisionId ()
// Check if update available
val updateRevision = item. checkUpdate ()
// Install specific revision
item. install (revisionId, task)
This allows:
Version control
Rollback capability (if implemented)
Update detection
Best Practices
Call updateAll() periodically to keep marketplace content current. Consider:
On client startup
Daily automatic checks
Manual update command
Always provide user feedback on errors. The update system is designed to be resilient:
Failed updates don’t affect other items
Errors are logged for debugging
Users receive actionable error messages
Use tasks for large batches: val task = Task ( "Updating Marketplace" )
MarketplaceManager. updateAll (task)
After subscribe/unsubscribe, the appropriate type manager is automatically reloaded. Don’t manually reload unless necessary.
Usage Example
// Subscribe to a script
val scriptItem = // ... from marketplace API
MarketplaceManager. subscribe (scriptItem)
// Check for updates
MarketplaceManager.subscribedItems. forEach { item ->
val updateId = item. checkUpdate ()
if (updateId != null ) {
logger. info ( "Update available for ${ item.id } : revision $updateId " )
}
}
// Update all
MarketplaceManager. updateAll (
task = updateTask,
command = marketplaceCommand
)
// Unsubscribe
MarketplaceManager. unsubscribe (itemId)
// Get all subscribed scripts
val scripts = MarketplaceManager. getSubscribedItemsOfType (MarketplaceItemType.SCRIPT)