Skip to content

Commit ec8fc5c

Browse files
committed
Add modrinth analytics
1 parent c0fa092 commit ec8fc5c

10 files changed

Lines changed: 109 additions & 25 deletions

File tree

changelog.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
## Resourcify 1.8.2
1+
## Resourcify 1.8.3
22

33
Please make sure to report any bugs and/or visual anomalies
44
on [Resourcify's GitHub](https://github.com/DeDiamondPro/Resourcify/issues) or in
55
the [Discord](https://dediamondpro.dev/discord).
66

7-
- Port to 26.1 fabric
8-
- Fixed cancel button offset in screen when downloading a world.
7+
- Resourcify now contributes to Modrinth's analytics,
8+
see [their article](https://modrinth.com/news/article/analytics-overhaul/) for more info.
9+
- Ported to 26.1.2 NeoForge
910

1011
----------------------------------------------------------------------------------------------------
1112

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
mod_name = Resourcify
22
mod_id = resourcify
3-
mod_version = 1.8.2
3+
mod_version = 1.8.3
44

55
org.gradle.jvmargs=-Xmx4G
66
org.gradle.parallel=true

src/main/kotlin/dev/dediamondpro/resourcify/gui/projectpage/VersionWrapper.kt

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
1+
/*
2+
* This file is part of Resourcify
3+
* Copyright (C) 2026 DeDiamondPro
4+
*
5+
* This program is free software; you can redistribute it and/or
6+
* modify it under the terms of the GNU Lesser General Public
7+
* License Version 3 as published by the Free Software Foundation.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12+
* Lesser General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Lesser General Public License
15+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
*/
17+
118
package dev.dediamondpro.resourcify.gui.projectpage
219

320
import dev.dediamondpro.resourcify.Constants
@@ -7,6 +24,7 @@ import dev.dediamondpro.resourcify.gui.PaginatedScreen
724
import dev.dediamondpro.resourcify.gui.world.WorldDownloadingScreen
825
import dev.dediamondpro.resourcify.services.IVersion
926
import dev.dediamondpro.resourcify.services.ProjectType
27+
import dev.dediamondpro.resourcify.services.modrinth.ModrinthAnalytics
1028
import dev.dediamondpro.resourcify.util.DownloadManager
1129
import dev.dediamondpro.resourcify.util.Utils
1230
import dev.dediamondpro.resourcify.util.localize
@@ -97,7 +115,11 @@ class VersionWrapper(private val version: IVersion, private val type: ProjectTyp
97115
while (file.exists()) {
98116
file = File(downloadFolder, Utils.incrementFileName(file.name))
99117
}
100-
DownloadManager.download(file, version.getSha1(), url, type.shouldExtract) {
118+
DownloadManager.download(
119+
file, version.getSha1(),
120+
url, type.shouldExtract,
121+
ModrinthAnalytics.DownloadReason.STANDALONE
122+
) {
101123
text?.setText("${ChatColor.BOLD}${localize("resourcify.version.installed")}")
102124
installed = true
103125
}

src/main/kotlin/dev/dediamondpro/resourcify/gui/update/components/UpdateCard.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import dev.dediamondpro.resourcify.gui.PaginatedScreen
3232
import dev.dediamondpro.resourcify.gui.data.Colors
3333
import dev.dediamondpro.resourcify.gui.data.Icons
3434
import dev.dediamondpro.resourcify.mixins.PackScreenAccessor
35+
import dev.dediamondpro.resourcify.services.modrinth.ModrinthAnalytics
3536
import dev.dediamondpro.resourcify.util.*
3637
import gg.essential.elementa.UIComponent
3738
import gg.essential.elementa.components.UIBlock
@@ -239,8 +240,8 @@ class UpdateCard(
239240
downloadFile = File(file.parentFile, Utils.incrementFileName(downloadFile.name))
240241
}
241242
DownloadManager.download(
242-
downloadFile,
243-
newVersion.getSha1(), updateUrl
243+
downloadFile, newVersion.getSha1(), updateUrl,
244+
downloadReason = ModrinthAnalytics.DownloadReason.UPDATE
244245
) {
245246
try {
246247
// Try to update the pack if it is currently selected, not critical if it fails

src/main/kotlin/dev/dediamondpro/resourcify/gui/world/DownloadWorldTab.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,10 @@ import net.minecraft.network.chat.Component
2828
import java.util.function.Consumer
2929

3030
//?if >=26.2 {
31-
import net.minecraft.client.gui.layouts.Layout
31+
/*import net.minecraft.client.gui.layouts.Layout
3232
import net.minecraft.client.gui.layouts.LinearLayout
3333
34-
//?}
34+
*///?}
3535

3636
class DownloadWorldTab : Tab {
3737
override fun getTabTitle(): Component {
@@ -51,12 +51,12 @@ class DownloadWorldTab : Tab {
5151
}
5252

5353
//?if >=26.2 {
54-
val layout: LinearLayout = LinearLayout.vertical()
54+
/*val layout: LinearLayout = LinearLayout.vertical()
5555
5656
override fun getLayout(): Layout {
5757
return layout
5858
}
59-
//?}
59+
*///?}
6060

6161
fun onClick() {
6262
UScreen.displayScreen(BrowseScreen(ProjectType.WORLD, Platform.getFileInGameDir("saves")))

src/main/kotlin/dev/dediamondpro/resourcify/handlers/VulkanHandler.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,17 @@ import gg.essential.universal.UScreen
2121
import net.minecraft.client.gui.screens.Screen
2222

2323
//?if >=26.2 {
24-
import com.mojang.blaze3d.vulkan.VulkanBackend
24+
/*import com.mojang.blaze3d.vulkan.VulkanBackend
2525
import net.minecraft.client.Minecraft
2626
import net.minecraft.client.gui.screens.AlertScreen
2727
import net.minecraft.network.chat.Component
2828
29-
//?}
29+
*///?}
3030

3131
object VulkanHandler {
3232
inline fun runOrBlock(callable: () -> Unit) {
3333
//?if >=26.2 {
34-
val mc = Minecraft.getInstance()
34+
/*val mc = Minecraft.getInstance()
3535
if (mc.window.backend() is VulkanBackend) {
3636
val currentScreen = mc.gui.screen()
3737
mc.setScreenAndShow(
@@ -43,13 +43,13 @@ object VulkanHandler {
4343
)
4444
return
4545
}
46-
//?}
46+
*///?}
4747
callable.invoke()
4848
}
4949

5050
inline fun createOrBlock(callable: () -> Screen): Screen {
5151
//?if >=26.2 {
52-
val mc = Minecraft.getInstance()
52+
/*val mc = Minecraft.getInstance()
5353
if (mc.window.backend() is VulkanBackend) {
5454
val currentScreen = mc.gui.screen()
5555
return AlertScreen(
@@ -58,7 +58,7 @@ object VulkanHandler {
5858
Component.translatable("resourcify.no-vulkan.description")
5959
)
6060
}
61-
//?}
61+
*///?}
6262
return callable.invoke()
6363
}
6464
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* This file is part of Resourcify
3+
* Copyright (C) 2026 DeDiamondPro
4+
*
5+
* This program is free software; you can redistribute it and/or
6+
* modify it under the terms of the GNU Lesser General Public
7+
* License Version 3 as published by the Free Software Foundation.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12+
* Lesser General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Lesser General Public License
15+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
*/
17+
18+
package dev.dediamondpro.resourcify.services.modrinth
19+
20+
import com.google.gson.annotations.SerializedName
21+
22+
/**
23+
* Data to contribute to Modrinth's analytics,
24+
* see https://modrinth.com/news/article/analytics-overhaul/ (bottom of article)
25+
*/
26+
data class ModrinthAnalytics(
27+
@SerializedName("download_reason") val downloadReason: DownloadReason? = null,
28+
@SerializedName("game_version") val gameVersion: String? = null,
29+
@SerializedName("loader") val loader: String? = null,
30+
) {
31+
enum class DownloadReason {
32+
@SerializedName("standalone") STANDALONE,
33+
@SerializedName("dependency") DEPENDENCY,
34+
@SerializedName("modpack") MODPACK,
35+
@SerializedName("update") UPDATE,
36+
}
37+
}

src/main/kotlin/dev/dediamondpro/resourcify/util/DownloadManager.kt

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
* This file is part of Resourcify
3-
* Copyright (C) 2023-2024 DeDiamondPro
3+
* Copyright (C) 2023-2026 DeDiamondPro
44
*
55
* This program is free software; you can redistribute it and/or
66
* modify it under the terms of the GNU Lesser General Public
@@ -19,6 +19,7 @@ package dev.dediamondpro.resourcify.util
1919

2020
import dev.dediamondpro.resourcify.Constants
2121
import dev.dediamondpro.resourcify.platform.Platform
22+
import dev.dediamondpro.resourcify.services.modrinth.ModrinthAnalytics
2223
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry
2324
import org.apache.commons.compress.archivers.zip.ZipFile
2425
import java.io.File
@@ -39,9 +40,11 @@ object DownloadManager {
3940

4041
fun download(
4142
file: File, sha512: String? = null, uri: URI,
42-
extract: Boolean = false, callback: (() -> Unit)? = null,
43+
extract: Boolean = false,
44+
downloadReason: ModrinthAnalytics.DownloadReason? = null,
45+
callback: (() -> Unit)? = null,
4346
) {
44-
queuedDownloads[uri] = QueuedDownload(file, sha512, extract, callback)
47+
queuedDownloads[uri] = QueuedDownload(file, sha512, extract, callback, downloadReason)
4548
downloadNext()
4649
}
4750

@@ -74,7 +77,17 @@ object DownloadManager {
7477
tempFile = File(tempFolder, queuedDownload.file.name + "-$i.tmp")
7578
}
7679
downloadsInProgress[url] = DownloadData(runAsync {
77-
val con = url.toURL().setupConnection()
80+
// Modrinth analytics
81+
val headers = mutableMapOf<String, String>()
82+
if (url.host == "cdn.modrinth.com") {
83+
headers["modrinth-download-meta"] = ModrinthAnalytics(
84+
downloadReason = queuedDownload.downloadReason,
85+
gameVersion = Platform.getMcVersion(),
86+
loader = Platform.getLoader(),
87+
).toJson()
88+
}
89+
90+
val con = url.toURL().setupConnection(headers)
7891
downloadsInProgress[url]?.length = con.contentLength
7992
con.getEncodedInputStream().use {
8093
Files.copy(it!!, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING)
@@ -172,6 +185,12 @@ object DownloadManager {
172185
}
173186
}
174187

175-
private data class QueuedDownload(val file: File, val sha1: String?, val extract: Boolean, val callback: (() -> Unit)?)
188+
private data class QueuedDownload(
189+
val file: File,
190+
val sha1: String?,
191+
val extract: Boolean,
192+
val callback: (() -> Unit)?,
193+
val downloadReason: ModrinthAnalytics.DownloadReason?
194+
)
176195

177196
private data class DownloadData(val future: CompletableFuture<Void>, val file: File, var length: Int? = null)

src/main/kotlin/dev/dediamondpro/resourcify/util/NetworkUtil.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,15 @@ object NetworkUtil {
3636
private val cache = ConcurrentHashMap<URI, CacheObject>()
3737
private val currentlyFetching = ConcurrentHashMap<URI, CompletableFuture<ByteArray?>>()
3838

39-
fun setupConnection(url: URL): HttpsURLConnection {
39+
fun setupConnection(url: URL, headers: Map<String, String> = emptyMap()): HttpsURLConnection {
4040
val con = url.openConnection() as HttpsURLConnection
4141
con.setRequestProperty(
4242
"User-Agent",
4343
"${Constants.NAME}/${Constants.VERSION} (${Platform.getMcVersion()}-${Platform.getLoader()})"
4444
)
45+
for ((key, value) in headers) {
46+
con.setRequestProperty(key, URLEncoder.encode(value, "UTF-8"))
47+
}
4548
con.setRequestProperty("Accept-Encoding", "gzip, deflate")
4649
con.connectTimeout = 5000
4750
con.readTimeout = 5000
@@ -129,7 +132,8 @@ object NetworkUtil {
129132
}
130133
}
131134

132-
fun URL.setupConnection(): HttpsURLConnection = NetworkUtil.setupConnection(this)
135+
fun URL.setupConnection(headers: Map<String, String> = emptyMap()): HttpsURLConnection =
136+
NetworkUtil.setupConnection(this, headers)
133137

134138
fun URLConnection.getEncodedInputStream(): InputStream? {
135139
return try {

stonecutter.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ plugins {
1919
id("dev.kikugie.stonecutter")
2020
alias(libs.plugins.arch.loom) apply false
2121
}
22-
stonecutter active "26.2-fabric" /* [SC] DO NOT EDIT */
22+
stonecutter active "26.1.2-fabric" /* [SC] DO NOT EDIT */
2323

2424
stonecutter tasks {
2525
val ordering = versionComparator.thenComparingInt {

0 commit comments

Comments
 (0)