A Minecraft Fabric mod that connects the game with ATProtocol ⛏️
8
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 327 lines 13 kB view raw
1package com.jollywhoppers.atproto.examples 2 3import com.jollywhoppers.atproto.server.AppViewService 4import com.jollywhoppers.atproto.server.AppViewHttpServer 5import kotlinx.serialization.json.* 6import java.util.* 7 8/** 9 * Example demonstrating how to use the AppView service for displaying Minecraft data. 10 * 11 * An AppView is a custom service that indexes published AT Protocol records 12 * and provides rich display and query capabilities. This example shows: 13 * 14 * 1. Creating an AppView service instance 15 * 2. Indexing player data as records are published 16 * 3. Querying the indexed data for display 17 * 4. Starting an HTTP server to serve the data to clients 18 */ 19class AppViewExample( 20 private val appViewService: AppViewService 21) { 22 private val json = Json { 23 prettyPrint = true 24 ignoreUnknownKeys = true 25 } 26 27 /** 28 * Example 1: Index a player profile when published to AT Protocol 29 */ 30 fun exampleIndexPlayerProfile() { 31 // In a real AppView, this would be called by a subscription handler 32 // when a record is published to AT Protocol 33 34 val playerUuid = UUID.randomUUID().toString() 35 val profileRecord = json.parseToJsonElement(""" 36 { 37 "${'$'}type": "com.jollywhoppers.minecraft.player.profile", 38 "player": { 39 "uuid": "$playerUuid", 40 "username": "AlicePlayer" 41 }, 42 "displayName": "Alice", 43 "bio": "Minecraft enthusiast and builder", 44 "createdAt": "2026-04-20T10:30:00Z", 45 "updatedAt": null, 46 "publicStats": true, 47 "publicSessions": true 48 } 49 """) 50 51 val uri = "at://did:plc:alice123/com.jollywhoppers.minecraft.player.profile/self" 52 53 appViewService.indexPlayerProfile(uri, profileRecord) 54 .onSuccess { 55 println("✓ Indexed player profile for AlicePlayer") 56 } 57 .onFailure { e -> 58 println("✗ Failed to index profile: ${e.message}") 59 } 60 } 61 62 /** 63 * Example 2: Index player stats when synced to AT Protocol 64 */ 65 fun exampleIndexPlayerStats() { 66 val playerUuid = UUID.randomUUID().toString() 67 val statsRecord = json.parseToJsonElement(""" 68 { 69 "${'$'}type": "com.jollywhoppers.minecraft.player.stats", 70 "player": { 71 "uuid": "$playerUuid", 72 "username": "AlicePlayer" 73 }, 74 "server": { 75 "serverId": "server-123", 76 "serverName": "Main SMP" 77 }, 78 "statistics": [ 79 {"key": "minecraft.mined.oak_log", "value": 1250, "category": "blocks_mined"}, 80 {"key": "minecraft.killed.zombie", "value": 425, "category": "mobs_killed"}, 81 {"key": "minecraft.custom.play_one_minute", "value": 432000, "category": "playtime"} 82 ], 83 "playtimeMinutes": 7200, 84 "level": 34, 85 "gamemode": "survival", 86 "dimension": "minecraft:overworld", 87 "syncedAt": "2026-04-25T14:22:00Z" 88 } 89 """) 90 91 val uri = "at://did:plc:alice123/com.jollywhoppers.minecraft.player.stats/8l6rvp4j6d3e2c4b9" 92 93 appViewService.indexPlayerStats(uri, statsRecord) 94 .onSuccess { 95 println("✓ Indexed player stats for AlicePlayer") 96 } 97 .onFailure { e -> 98 println("✗ Failed to index stats: ${e.message}") 99 } 100 } 101 102 /** 103 * Example 3: Index achievements as they're earned 104 */ 105 fun exampleIndexAchievement() { 106 val playerUuid = UUID.randomUUID().toString() 107 val achievementRecord = json.parseToJsonElement(""" 108 { 109 "${'$'}type": "com.jollywhoppers.minecraft.achievement", 110 "player": { 111 "uuid": "$playerUuid", 112 "username": "AlicePlayer" 113 }, 114 "server": { 115 "serverId": "server-123", 116 "serverName": "Main SMP" 117 }, 118 "achievementId": "minecraft:adventure/kill_a_mob", 119 "achievementName": "Monster Hunter", 120 "achievementDescription": "Kill any type of monster", 121 "achievedAt": "2026-04-24T15:45:00Z", 122 "category": "adventure", 123 "isChallenge": false 124 } 125 """) 126 127 val uri = "at://did:plc:alice123/com.jollywhoppers.minecraft.achievement/8l6rvp4j6d3e2c5a7" 128 129 appViewService.indexAchievement(uri, achievementRecord) 130 .onSuccess { 131 println("✓ Indexed achievement for AlicePlayer") 132 } 133 .onFailure { e -> 134 println("✗ Failed to index achievement: ${e.message}") 135 } 136 } 137 138 /** 139 * Example 4: Query player profile with stats 140 */ 141 fun exampleQueryPlayerProfile(playerUuid: String) { 142 appViewService.getPlayerProfile(playerUuid) 143 .onSuccess { profileWithStats -> 144 if (profileWithStats != null) { 145 println("\n━━━ Player Profile ━━━") 146 println("Username: ${profileWithStats.profile.username}") 147 println("Display Name: ${profileWithStats.profile.displayName}") 148 println("Bio: ${profileWithStats.profile.bio}") 149 println("Stats Count: ${profileWithStats.statsCount}") 150 println("Achievements: ${profileWithStats.achievementCount}") 151 152 if (profileWithStats.latestStats != null) { 153 val stats = profileWithStats.latestStats 154 println("\nLatest Stats:") 155 println(" Level: ${stats.level}") 156 println(" Playtime: ${stats.playtimeMinutes} minutes") 157 println(" Gamemode: ${stats.gamemode}") 158 } 159 } else { 160 println("Player not found") 161 } 162 } 163 } 164 165 /** 166 * Example 5: Query leaderboards 167 */ 168 fun exampleQueryLeaderboard() { 169 println("\n━━━ Top Players by Blocks Mined ━━━") 170 appViewService.getLeaderboard("minecraft.mined.oak_log", limit = 10) 171 .onSuccess { leaders -> 172 leaders.forEachIndexed { index, entry -> 173 println("${index + 1}. ${entry.username} - ${entry.value} blocks") 174 } 175 } 176 } 177 178 /** 179 * Example 6: Search for players 180 */ 181 fun exampleSearchPlayers(query: String) { 182 println("\n━━━ Search Results for '$query' ━━━") 183 appViewService.searchPlayers(query) 184 .onSuccess { players -> 185 if (players.isEmpty()) { 186 println("No players found") 187 } else { 188 players.forEach { player -> 189 println("${player.username} (${player.displayName ?: "no display name"})") 190 } 191 } 192 } 193 } 194 195 /** 196 * Example 7: Get trending achievements 197 */ 198 fun exampleTrendingAchievements() { 199 println("\n━━━ Trending Achievements ━━━") 200 appViewService.getTrendingAchievements(limit = 5) 201 .onSuccess { trending -> 202 trending.forEachIndexed { index, achievement -> 203 println("${index + 1}. ${achievement.achievementName}") 204 println(" Earned by ${achievement.timesEarned} players") 205 println(" Category: ${achievement.category}") 206 } 207 } 208 } 209 210 /** 211 * Example 8: Get player stats summary 212 */ 213 fun examplePlayerStatsSummary(playerUuid: String) { 214 println("\n━━━ Player Stats Summary ━━━") 215 appViewService.getPlayerStatsSummary(playerUuid) 216 .onSuccess { summary -> 217 if (summary != null) { 218 println("Player: ${summary.username}") 219 println("Level: ${summary.level}") 220 println("Playtime: ${summary.playtimeMinutes} minutes") 221 println("Gamemode: ${summary.gamemode}") 222 println("\nTop Statistics:") 223 summary.topStatistics.forEachIndexed { index, stat -> 224 println(" ${index + 1}. ${stat.key}: ${stat.value}") 225 } 226 } 227 } 228 } 229 230 /** 231 * Example 9: Start the AppView HTTP server 232 */ 233 fun exampleStartAppViewServer() { 234 val server = AppViewHttpServer(appViewService, port = 8080) 235 236 println("\n━━━ Starting AppView Server ━━━") 237 server.start() 238 239 println("\nAvailable Endpoints:") 240 println(" GET /health") 241 println(" Check server health") 242 println() 243 println(" GET /player/{uuid}") 244 println(" Get player profile with stats summary") 245 println() 246 println(" GET /player/{uuid}/stats?limit=10&offset=0") 247 println(" Get player stats history (paginated)") 248 println() 249 println(" GET /player/{uuid}/achievements?limit=25&offset=0") 250 println(" Get player achievement history (paginated)") 251 println() 252 println(" GET /leaderboard/{statistic}?limit=20") 253 println(" Get leaderboard for a specific statistic") 254 println() 255 println(" GET /search?q={query}") 256 println(" Search for players by username or display name") 257 println() 258 println(" GET /trending/achievements?limit=10") 259 println(" Get trending achievements") 260 println() 261 println(" GET /stats/summary/{uuid}") 262 println(" Get quick summary of player stats") 263 } 264 265 /** 266 * Example 10: Complete workflow 267 */ 268 fun exampleCompleteWorkflow() { 269 println("\n┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓") 270 println("┃ AppView Integration Complete Workflow ┃") 271 println("┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛") 272 273 // 1. Set up sample data 274 println("\n[1] Indexing sample player data...") 275 exampleIndexPlayerProfile() 276 exampleIndexPlayerStats() 277 exampleIndexAchievement() 278 279 // 2. Query the data 280 println("\n[2] Querying indexed data...") 281 val sampleUuid = "550e8400-e29b-41d4-a716-446655440000" 282 exampleQueryPlayerProfile(sampleUuid) 283 exampleQueryLeaderboard() 284 exampleSearchPlayers("Alice") 285 exampleTrendingAchievements() 286 examplePlayerStatsSummary(sampleUuid) 287 288 // 3. Start server 289 println("\n[3] Starting HTTP server...") 290 exampleStartAppViewServer() 291 292 println("\n✓ AppView integration example complete!") 293 } 294} 295 296/** 297 * Quick start: Run this to see the AppView in action 298 */ 299fun main() { 300 // This is a demonstration - in practice, the AppView would: 301 // 1. Subscribe to AT Protocol repository events 302 // 2. Index records as they're published 303 // 3. Serve queries via HTTP endpoints 304 // 4. Maintain a database of indexed records 305 306 println(""" 307 ╔════════════════════════════════════════════╗ 308 ║ AT Protocol Minecraft AppView ║ 309 ║ Display and Query Synced Minecraft Data ║ 310 ╚════════════════════════════════════════════╝ 311 """.trimIndent()) 312 313 // In a real implementation, you would: 314 // 1. Create the AppViewService with a real database backend 315 // 2. Subscribe to Firehose events or use WebSocket subscriptions 316 // 3. Deploy the HTTP server to a public URL 317 // 4. Register your AppView with the AT Protocol application registry 318 319 println("\nTo implement a full AppView:") 320 println("1. Use a framework like Ktor or Spring Boot for HTTP server") 321 println("2. Subscribe to AT Protocol Firehose for real-time updates") 322 println("3. Use a database (PostgreSQL, MongoDB) for indexing") 323 println("4. Implement pagination, filtering, and search") 324 println("5. Add caching layers (Redis) for performance") 325 println("6. Deploy to a public URL accessible from AT Protocol clients") 326 println("7. Register your AppView in the AT Protocol registry") 327}