A Minecraft Fabric mod that connects the game with ATProtocol ⛏️
1package com.jollywhoppers.atproto.examples
2
3import com.jollywhoppers.atproto.AtProtoSessionManager
4import kotlinx.serialization.Serializable
5import kotlinx.serialization.encodeToString
6import kotlinx.serialization.json.Json
7import kotlinx.serialization.json.JsonElement
8import kotlinx.serialization.json.encodeToJsonElement
9import java.util.*
10
11/**
12 * Example showing how to create records in a player's AT Protocol repository.
13 * This demonstrates the foundation for syncing Minecraft data to AT Protocol.
14 */
15class RecordCreationExample(
16 private val sessionManager: AtProtoSessionManager
17) {
18 private val json = Json { prettyPrint = false }
19
20 /**
21 * Example: Create a player stats record
22 */
23 suspend fun createPlayerStatsRecord(
24 playerUuid: UUID,
25 statistics: List<Statistic>,
26 playtimeMinutes: Int,
27 level: Int
28 ): Result<String> = runCatching {
29 // Get the player's session (will auto-refresh if needed)
30 val session = sessionManager.getSession(playerUuid).getOrThrow()
31
32 // Build the record according to our lexicon
33 val record = PlayerStatsRecord(
34 `$type` = "com.jollywhoppers.minecraft.player.stats",
35 player = PlayerReference(
36 uuid = playerUuid.toString(),
37 username = "ExamplePlayer" // Would come from Minecraft player object
38 ),
39 server = ServerReference(
40 serverId = "example-server-id",
41 serverName = "Example Server"
42 ),
43 statistics = statistics,
44 playtimeMinutes = playtimeMinutes,
45 level = level,
46 gamemode = "survival",
47 syncedAt = java.time.Instant.now().toString()
48 )
49
50 // Create the record via XRPC
51 val requestBody = CreateRecordRequest(
52 repo = session.did,
53 collection = "com.jollywhoppers.minecraft.player.stats",
54 record = json.encodeToJsonElement(record)
55 )
56
57 val response = sessionManager.makeAuthenticatedRequest(
58 uuid = playerUuid,
59 method = "POST",
60 endpoint = "com.atproto.repo.createRecord",
61 body = json.encodeToString(requestBody)
62 ).getOrThrow()
63
64 response
65 }
66
67 /**
68 * Example: Create a player profile record
69 */
70 suspend fun createPlayerProfileRecord(
71 playerUuid: UUID,
72 displayName: String?,
73 bio: String?,
74 publicStats: Boolean = true
75 ): Result<String> = runCatching {
76 val session = sessionManager.getSession(playerUuid).getOrThrow()
77
78 val record = PlayerProfileRecord(
79 `$type` = "com.jollywhoppers.minecraft.player.profile",
80 player = PlayerReference(
81 uuid = playerUuid.toString(),
82 username = "ExamplePlayer"
83 ),
84 displayName = displayName,
85 bio = bio,
86 createdAt = java.time.Instant.now().toString(),
87 publicStats = publicStats,
88 publicSessions = true
89 )
90
91 // For profile, we use rkey "self" since there's only one per account
92 val requestBody = CreateRecordRequestWithRkey(
93 repo = session.did,
94 collection = "com.jollywhoppers.minecraft.player.profile",
95 rkey = "self",
96 record = json.encodeToJsonElement(record)
97 )
98
99 sessionManager.makeAuthenticatedRequest(
100 uuid = playerUuid,
101 method = "POST",
102 endpoint = "com.atproto.repo.putRecord",
103 body = json.encodeToString(requestBody)
104 ).getOrThrow()
105 }
106
107 /**
108 * Example: Create an achievement record
109 */
110 suspend fun createAchievementRecord(
111 playerUuid: UUID,
112 achievementId: String,
113 achievementName: String,
114 achievementDescription: String,
115 category: String,
116 isChallenge: Boolean = false
117 ): Result<String> = runCatching {
118 val session = sessionManager.getSession(playerUuid).getOrThrow()
119
120 val record = AchievementRecord(
121 `$type` = "com.jollywhoppers.minecraft.achievement",
122 player = PlayerReference(
123 uuid = playerUuid.toString(),
124 username = "ExamplePlayer"
125 ),
126 server = ServerReference(
127 serverId = "example-server-id",
128 serverName = "Example Server"
129 ),
130 achievementId = achievementId,
131 achievementName = achievementName,
132 achievementDescription = achievementDescription,
133 achievedAt = java.time.Instant.now().toString(),
134 category = category,
135 isChallenge = isChallenge
136 )
137
138 val requestBody = CreateRecordRequest(
139 repo = session.did,
140 collection = "com.jollywhoppers.minecraft.achievement",
141 record = json.encodeToJsonElement(record)
142 )
143
144 sessionManager.makeAuthenticatedRequest(
145 uuid = playerUuid,
146 method = "POST",
147 endpoint = "com.atproto.repo.createRecord",
148 body = json.encodeToString(requestBody)
149 ).getOrThrow()
150 }
151
152 // Data classes matching our lexicon schemas
153
154 @Serializable
155 data class PlayerReference(
156 val uuid: String,
157 val username: String
158 )
159
160 @Serializable
161 data class ServerReference(
162 val serverId: String,
163 val serverName: String
164 )
165
166 @Serializable
167 data class Statistic(
168 val key: String,
169 val value: Int,
170 val category: String? = null
171 )
172
173 @Serializable
174 data class PlayerStatsRecord(
175 val `$type`: String,
176 val player: PlayerReference,
177 val server: ServerReference,
178 val statistics: List<Statistic>,
179 val playtimeMinutes: Int,
180 val level: Int,
181 val gamemode: String,
182 val dimension: String? = null,
183 val syncedAt: String
184 )
185
186 @Serializable
187 data class PlayerProfileRecord(
188 val `$type`: String,
189 val player: PlayerReference,
190 val displayName: String?,
191 val bio: String?,
192 val createdAt: String,
193 val updatedAt: String? = null,
194 val publicStats: Boolean,
195 val publicSessions: Boolean
196 )
197
198 @Serializable
199 data class AchievementRecord(
200 val `$type`: String,
201 val player: PlayerReference,
202 val server: ServerReference,
203 val achievementId: String,
204 val achievementName: String,
205 val achievementDescription: String,
206 val achievedAt: String,
207 val category: String,
208 val isChallenge: Boolean
209 )
210
211 @Serializable
212 data class CreateRecordRequest(
213 val repo: String,
214 val collection: String,
215 val record: JsonElement
216 )
217
218 @Serializable
219 data class CreateRecordRequestWithRkey(
220 val repo: String,
221 val collection: String,
222 val rkey: String,
223 val record: JsonElement
224 )
225}
226
227/**
228 * Usage example:
229 *
230 * ```kotlin
231 * val example = RecordCreationExample(sessionManager)
232 *
233 * // Create a stats snapshot
234 * val stats = listOf(
235 * Statistic("minecraft:killed.minecraft.zombie", 42, "killed"),
236 * Statistic("minecraft:mined.minecraft.diamond_ore", 15, "mined")
237 * )
238 *
239 * example.createPlayerStatsRecord(
240 * playerUuid = player.uuid,
241 * statistics = stats,
242 * playtimeMinutes = 180,
243 * level = 25
244 * ).onSuccess { response ->
245 * logger.info("Stats synced successfully!")
246 * }.onFailure { error ->
247 * logger.error("Failed to sync stats", error)
248 * }
249 * ```
250 */