Skip to content

Commit 842f68d

Browse files
committed
Better file utils
1 parent 990ba30 commit 842f68d

File tree

9 files changed

+144
-96
lines changed

9 files changed

+144
-96
lines changed

common/src/main/kotlin/com/lambda/module/modules/client/Network.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import com.lambda.network.NetworkManager.updateToken
3131
import com.lambda.network.api.v1.endpoints.login
3232
import com.lambda.util.StringUtils.hash
3333
import com.lambda.util.extension.isOffline
34+
import net.minecraft.SharedConstants
3435
import net.minecraft.client.network.AllowedAddressResolver
3536
import net.minecraft.client.network.ClientLoginNetworkHandler
3637
import net.minecraft.client.network.ServerAddress
@@ -51,6 +52,9 @@ object Network : Module(
5152
val authServer by setting("Auth Server", "auth.lambda-client.org")
5253
val apiUrl by setting("API Server", "https://api.lambda-client.org")
5354
val apiVersion by setting("API Version", ApiVersion.V1)
55+
val mappings by setting("Mappings", "https://mappings.lambda-client.org")
56+
57+
val gameVersion = SharedConstants.getGameVersion().name
5458

5559
private var hash: String? = null
5660

common/src/main/kotlin/com/lambda/network/LambdaHttp.kt

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,16 @@ val LambdaHttp = HttpClient {
3434
}
3535
}
3636

37-
suspend inline fun HttpClient.download(url: String, file: File, block: HttpRequestBuilder.() -> Unit = {}) =
38-
file.writeBytes(get(url, block).readRawBytes())
37+
suspend inline fun HttpClient.download(url: String, file: File, block: HttpRequestBuilder.() -> Unit = {}) {
38+
val response = get(url, block).readRawBytes()
39+
file.writeBytes(response)
40+
}
3941

40-
suspend inline fun HttpClient.download(url: String, output: OutputStream, block: HttpRequestBuilder.() -> Unit = {}) =
41-
output.write(get(url, block).readRawBytes())
42+
suspend inline fun HttpClient.download(url: String, output: OutputStream, block: HttpRequestBuilder.() -> Unit = {}) {
43+
val response = get(url, block).readRawBytes()
44+
output.write(response)
45+
}
4246

4347
suspend inline fun HttpClient.download(url: String, block: HttpRequestBuilder.() -> Unit) =
4448
get(url, block).readRawBytes()
49+

common/src/main/kotlin/com/lambda/network/api/v1/endpoints/GetCape.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import java.util.UUID
3131
* Example:
3232
* - id: ab24f5d6-dcf1-45e4-897e-b50a7c5e7422
3333
*
34-
* response: [Cape] or error
34+
* @return results of cape
3535
*/
3636
suspend fun getCape(uuid: UUID) = runCatching {
3737
LambdaHttp.get("$apiUrl/api/${apiVersion.value}/cape?id=$uuid").body<Cape>()

common/src/main/kotlin/com/lambda/network/api/v1/endpoints/GetMappings.kt

Lines changed: 0 additions & 40 deletions
This file was deleted.

common/src/main/kotlin/com/lambda/network/api/v1/endpoints/LinkDiscord.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import io.ktor.http.*
3232
* Example:
3333
* - token: OTk1MTU1NzcyMzYxMTQ2NDM4
3434
*
35-
* response: [Authentication] or error
35+
* @return result of [Authentication]
3636
*/
3737
suspend fun linkDiscord(discordToken: String) = runCatching {
3838
LambdaHttp.post("${apiUrl}/api/${apiVersion.value}/link/discord") {

common/src/main/kotlin/com/lambda/network/api/v1/endpoints/Login.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import io.ktor.http.*
3232
* - username: Notch
3333
* - hash: 069a79f444e94726a5befca90e38aaf5
3434
*
35-
* response: [Authentication] or error
35+
* @return result of [Authentication]
3636
*/
3737
suspend fun login(username: String, hash: String) = runCatching {
3838
LambdaHttp.post("${apiUrl}/api/${apiVersion.value}/login") {

common/src/main/kotlin/com/lambda/network/api/v1/endpoints/SetCape.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import io.ktor.http.*
3030
* Example:
3131
* - id: galaxy
3232
*
33-
* response: [Unit] or error
33+
* @return nothing
3434
*/
3535
suspend fun setCape(id: String) = runCatching {
3636
val resp = LambdaHttp.put("$apiUrl/api/${apiVersion.value}/cape?id=$id") {

common/src/main/kotlin/com/lambda/util/DynamicReflectionSerializer.kt

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,12 @@ package com.lambda.util
2020
import com.lambda.Lambda
2121
import com.lambda.Lambda.LOG
2222
import com.lambda.core.Loadable
23-
import com.lambda.network.api.v1.endpoints.getMappings
24-
import com.lambda.util.FileUtils.getIfNotPresent
25-
import com.lambda.util.FileUtils.ifNotExists
23+
import com.lambda.module.modules.client.Network
24+
import com.lambda.util.FileUtils.downloadIfNotPresent
2625
import com.lambda.util.FolderRegister.cache
2726
import com.lambda.util.extension.resolveFile
2827
import com.mojang.serialization.Codec
29-
import net.minecraft.SharedConstants
28+
import kotlinx.coroutines.runBlocking
3029
import net.minecraft.block.BlockState
3130
import net.minecraft.client.resource.language.TranslationStorage
3231
import net.minecraft.item.ItemStack
@@ -71,19 +70,19 @@ object DynamicReflectionSerializer : Loadable {
7170

7271
private const val INDENT = 2
7372

74-
private val mappings =
75-
cache.resolveFile("${SharedConstants.getProtocolVersion()}.mappings")
76-
.ifNotExists {
77-
getMappings(
78-
success = it.getIfNotPresent(),
79-
failure = { LOG.error("Could not download the required files for the dynamic remapper") }
80-
).join()
81-
}.let { file ->
82-
if (!file.exists()) emptyMap<String, String>()
73+
private val mappings = runBlocking {
74+
"${Network.mappings}/${Network.gameVersion}"
75+
.downloadIfNotPresent(cache.resolveFile(Network.gameVersion))
76+
.map { file ->
8377
file.readLines()
84-
.map { it.split('\t') }
78+
.map { it.split(' ') }
8579
.associate { it[0].split('$').last() to it[1] }
8680
}
81+
.getOrElse {
82+
LOG.error("Unable to download deobfuscated qualifiers", it)
83+
emptyMap()
84+
}
85+
}
8786

8887
val String.remappedName get() = mappings.getOrDefault(this, this)
8988

common/src/main/kotlin/com/lambda/util/FileUtils.kt

Lines changed: 114 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -17,29 +17,22 @@
1717

1818
package com.lambda.util
1919

20-
import com.github.kittinunf.fuel.core.FuelError
21-
import com.github.kittinunf.fuel.httpDownload
22-
import com.github.kittinunf.fuel.httpGet
23-
import com.github.kittinunf.result.getOrNull
2420
import com.lambda.Lambda.mc
21+
import com.lambda.network.LambdaHttp
22+
import com.lambda.network.download
2523
import com.lambda.util.StringUtils.sanitizeForFilename
24+
import io.ktor.client.request.*
2625
import java.io.File
2726
import java.net.InetSocketAddress
27+
import kotlin.math.sign
28+
import kotlin.time.Duration
2829

2930
object FileUtils {
3031
/**
3132
* Returns a sequence of all the files in a tree that matches the [predicate]
3233
*/
3334
fun File.listRecursive(predicate: (File) -> Boolean): Sequence<File> = walk().filter(predicate)
3435

35-
/**
36-
* Ensures the current file exists by creating it if it does not.
37-
*
38-
* If the file already exists, it will not be recreated. The necessary
39-
* parent directories will be created if they do not exist.
40-
*/
41-
fun File.createIfNotExists() = also { parentFile.mkdirs(); createNewFile() }
42-
4336
/**
4437
* Retrieves or creates a directory based on the current network connection and world dimension.
4538
*
@@ -69,56 +62,143 @@ object FileUtils {
6962
}
7063

7164
/**
72-
* Executes the [block] if the receiver file exists
65+
* Executes the [block] if the file is older than the given [duration]
66+
*/
67+
fun File.isOlderThan(duration: Duration, block: (File) -> Unit) =
68+
ifExists { if (duration.inWholeMilliseconds < System.currentTimeMillis() - lastModified()) block(this) }
69+
70+
/**
71+
* Returns whether the receiver file is older than [duration]
72+
*/
73+
fun File.isOlderThan(duration: Duration) =
74+
duration.inWholeMilliseconds < System.currentTimeMillis() - lastModified()
75+
76+
/**
77+
* Executes the [block] if the receiver file exists and is not empty
7378
*/
7479
inline fun File.ifExists(block: (File) -> Unit): File {
75-
if (exists()) block(this)
80+
if (length() > 0) block(this)
81+
return this
82+
}
83+
84+
/**
85+
* Ensures the current file exists by creating it if it does not.
86+
*
87+
* If the file already exists, it will not be recreated. The necessary
88+
* parent directories will be created if they do not exist.
89+
*
90+
* @param block Lambda executed if the file doesn't exist or the file is empty
91+
*/
92+
inline fun File.createIfNotExists(block: (File) -> Unit): File {
93+
if (length() == 0L) block(this)
94+
95+
parentFile.mkdirs()
96+
createNewFile()
97+
7698
return this
7799
}
78100

79101
/**
80-
* Executes the [block] if the receiver file does not exist.
102+
* Executes the [block] if the receiver file does not exist or is empty.
81103
*/
82104
inline fun File.ifNotExists(block: (File) -> Unit): File {
83-
if (!exists()) block(this)
105+
if (length() == 0L) block(this)
84106
return this
85107
}
86108

109+
/**
110+
* Modifies the receiver file if the downloaded file compare check succeeds
111+
*
112+
* @receiver The destination file to write the bytes to
113+
*
114+
* @param url The url to download the file from
115+
* @param compare Compare method. -1 if remote is larger. 0 if both file have the same size. 1 if local is larger
116+
* @param block Configuration block for the request
117+
*
118+
* @return An exception or the file
119+
*/
120+
suspend fun File.downloadCompare(
121+
url: String,
122+
compare: Int,
123+
block: HttpRequestBuilder.() -> Unit = {},
124+
) = runCatching {
125+
createIfNotExists {
126+
val bytes = readBytes()
127+
val remote = LambdaHttp.download(url, block)
128+
val sign = (bytes.size - remote.size).sign
129+
130+
if (sign == compare) writeBytes(remote)
131+
}
132+
}
133+
87134
/**
88135
* Downloads the given file url if the file is not present
89136
*
90-
* This function does not guarantee that the given file will be created
137+
* @receiver The destination file to write the bytes to
138+
*
139+
* @param url The url to download the file from
140+
* @param block Configuration block for the request
141+
*
142+
* @return An exception or the file
91143
*/
92-
fun File.downloadIfNotPresent(
144+
suspend fun File.downloadIfNotPresent(
93145
url: String,
94-
success: (ByteArray) -> Unit = {},
95-
failure: (FuelError) -> Unit = {}
96-
) = ifNotExists { url.httpDownload().fileDestination { _, _ -> it }.response { _, _, result -> result.fold(success, failure) } }
146+
block: HttpRequestBuilder.() -> Unit = {},
147+
) = runCatching { createIfNotExists { LambdaHttp.download(url, this, block) } }
97148

98149
/**
99150
* Downloads the given file url if the file is not present
100151
*
101-
* This function does not guarantee that the given file will be created
152+
* @receiver The url to download the file from
153+
*
154+
* @param file The destination file to write the bytes to
155+
* @param block Configuration block for the request
156+
*
157+
* @return An exception or the file
102158
*/
103-
fun String.downloadIfNotPresent(
159+
suspend fun String.downloadIfNotPresent(
104160
file: File,
105-
success: (ByteArray) -> Unit = {},
106-
failure: (FuelError) -> Unit = {}
107-
) = file.ifNotExists { httpDownload().fileDestination { _, _ -> it }.response { _, _, result -> result.fold(success, failure) } }
161+
block: HttpRequestBuilder.() -> Unit = {},
162+
) = runCatching { file.createIfNotExists { LambdaHttp.download(this, file, block) } }
108163

109164
/**
110-
* Downloads the given file url if the file is not present
165+
* Lambda that downloads the given file url if the file is not present
166+
*
167+
* @receiver The destination file to write the bytes to
168+
* @param block Configuration block for the request
169+
*
170+
* @return A lambda that returns an exception or the file
171+
*/
172+
fun File.downloadIfNotPresent(block: HttpRequestBuilder.() -> Unit = {}): suspend (String) -> Result<Unit> =
173+
{ url -> runCatching { createIfNotExists { LambdaHttp.download(url, this, block) } } }
174+
175+
/**
176+
* Downloads the given file url if the file is present
111177
*
112-
* This function does not guarantee that the given file will be created
178+
* @receiver The destination file to write the bytes to
179+
*
180+
* @param url The url to download the file from
181+
* @param block Configuration block for the request
182+
*
183+
* @return An exception or the file
113184
*/
114-
fun File.downloadIfNotPresent(): (String) -> Unit =
115-
{ url -> ifNotExists { url.httpDownload().fileDestination { _, _ -> it }.response { _, _, _ -> } } }
185+
suspend fun File.downloadIfPresent(
186+
url: String,
187+
block: HttpRequestBuilder.() -> Unit = {},
188+
) = runCatching { ifExists { LambdaHttp.download(url, this, block) } }
116189

117190
/**
118-
* Gets the given url if the file is not present
191+
* Downloads the given file url if the file is present
119192
*
120-
* This function does not guarantee that the given file will be created
193+
* @receiver The url to download the file from
194+
*
195+
* @param file The destination file to write the bytes to
196+
* @param block Configuration block for the request
197+
*
198+
* @return An exception or the file
121199
*/
122-
fun File.getIfNotPresent(): (String) -> Unit =
123-
{ url -> ifNotExists { url.httpGet().responseString().third.getOrNull()?.let { writeText(it) } } }
200+
suspend fun String.downloadIfPresent(
201+
file: File,
202+
block: HttpRequestBuilder.() -> Unit = {},
203+
) = runCatching { file.ifExists { LambdaHttp.download(this, file, block) } }
124204
}

0 commit comments

Comments
 (0)