Skip to content

Commit

Permalink
Close #17 and Close #19
Browse files Browse the repository at this point in the history
  • Loading branch information
shedaniel committed Feb 4, 2024
1 parent b1d6b4d commit c6a72ac
Show file tree
Hide file tree
Showing 18 changed files with 415 additions and 182 deletions.
2 changes: 1 addition & 1 deletion backend/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ configurations {
}

dependencies {
implementation("me.shedaniel:linkie-core:1.0.120")
implementation("me.shedaniel:linkie-core:1.0.121")
implementation("io.ktor:ktor-server-cors:2.2.3")
implementation("io.ktor:ktor-server-content-negotiation:2.2.3")
implementation("io.ktor:ktor-server-status-pages:2.2.3")
Expand Down
98 changes: 83 additions & 15 deletions backend/src/main/kotlin/me/shedaniel/linkie/web/LinkieHandles.kt
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,13 @@ val json = Json {
}

suspend fun search(
namespaceStr: String, translateNsStr: String?,
namespaceStr: String, translateNsStr: String?, translateVersion: String?,
version: String?, query: String, allowClasses: Boolean,
allowMethods: Boolean, allowFields: Boolean, limit: Int,
): SearchResultEntries {
require(limit in 1..1000) { "Limit must be between 1 and 1000" }
if (translateNsStr != null) require(translateVersion == null) { "Cannot specify both translateNs and translateVersion" }
if (translateVersion != null) require(translateNsStr == null) { "Cannot specify both translateNs and translateVersion" }
val namespace =
Namespaces.namespaces[namespaceStr] ?: throw IllegalArgumentException("No namespace found for $namespaceStr")
val translateNamespace = translateNsStr?.let {
Expand All @@ -58,6 +60,7 @@ suspend fun search(
val provider = version?.let { namespace.getProvider(it) } ?: namespace.getProvider(defaultVersion)
var mappings by Delegates.notNull<MappingsContainer>()
var yarnMappings: MappingsContainer? = null
var translateMappings: MappingsContainer? = null
coroutineScope {
launch { withContext(Dispatchers.Default) { mappings = provider.get() } }
if (!namespace.hasMethodArgs(provider.version!!) && provider.version!!.tryToVersion()?.takeIf { it > "1.14.4".toVersion() } != null) {
Expand All @@ -66,14 +69,20 @@ suspend fun search(
launch { withContext(Dispatchers.Default) { yarnMappings = yarnProvider.get() } }
}
}
if (translateNamespace != null) {
launch { withContext(Dispatchers.Default) { translateMappings = translateNamespace.getProvider(provider.version!!).get() } }
} else if (translateVersion != null) {
launch { withContext(Dispatchers.Default) { translateMappings = namespace.getProvider(translateVersion).get() } }
}
Unit
}
val result = MappingsQueryUtils.query(mappings, query, *buildList {
if (allowClasses) add(MappingsEntryType.CLASS)
if (allowMethods) add(MappingsEntryType.METHOD)
if (allowFields) add(MappingsEntryType.FIELD)
}.toTypedArray(), limit = limit)
val argMappings: (Class, Method) -> List<MethodArg>? = if (yarnMappings != null) {
inner@{ clazz, method ->
val argsMetadata: MethodArgMetadata = if (yarnMappings != null) {
MethodArgMetadata(namespace.id.contains("mojang")) inner@{ clazz, method ->
val obfName = method.obfMergedName ?: return@inner null
val obfDesc = method.getObfMergedDesc(mappings)
val parentObfName = clazz.obfMergedName ?: return@inner null
Expand All @@ -84,20 +93,19 @@ suspend fun search(

targetMethod.args
}
} else { _, _ -> null }
if (translateNamespace == null) {
} else MethodArgMetadata(namespace)
if (translateMappings == null) {
return SearchResultEntries(
entries = result.results.asSequence().take(limit).mapNotNull {
toJsonFromEntry(namespace, mappings, it.value, it.score, argMappings)
toJsonFromEntry(mappings, it.value, it.score, argsMetadata)
}.toList(),
fuzzy = result.fuzzy,
)
} else {
val target = translateNamespace.getProvider(provider.version!!).get()
val elements = mutableListOf<JsonElement>()
translate(mappings, target, result.results.asSequence().take(limit)) { from, to, score ->
toJsonFromEntry(namespace, mappings, from, score, argMappings)?.also { obj ->
val translated = toJsonFromEntry(translateNamespace, target, to, score) { _, _ -> null }
translate(mappings, translateMappings!!, translateNamespace != null, result.results.asSequence().take(limit)) { from, to, score ->
toJsonFromEntry(mappings, from, score, argsMetadata)?.also { obj ->
val translated = toJsonFromEntry(translateMappings!!, to, score, null)
if (translated == null) elements.add(obj)
else {
elements.add(buildJsonObject {
Expand Down Expand Up @@ -219,7 +227,7 @@ fun getLoaderVersions(loader: String): MutableMap<String, MutableMap<String, Dep
return result
}

fun toJsonFromEntry(namespace: Namespace, mappings: MappingsContainer, entry: Any?, score: Double, argMappings: (Class, Method) -> List<MethodArg>?): JsonObject? {
fun toJsonFromEntry(mappings: MappingsContainer, entry: Any?, score: Double, argsMetadata: MethodArgMetadata?): JsonObject? {
return when (entry) {
is Class -> json.encodeToJsonElement(
SearchResultClassEntry.serializer(), SearchResultClassEntry(
Expand Down Expand Up @@ -254,9 +262,9 @@ fun toJsonFromEntry(namespace: Namespace, mappings: MappingsContainer, entry: An
memberType = if (entry.member is Field) "f" else "m"
)
) as JsonObject).let {
if (entry.member is Method) {
if (entry.member is Method && argsMetadata != null) {
var guessed = false
val args = (entry.member as Method).args ?: (argMappings(entry.owner, entry.member as Method)?.also {
val args = (entry.member as Method).args ?: (argsMetadata.guesser(entry.owner, entry.member as Method)?.also {
guessed = true
} ?: return@let it)
val map = it.toMutableMap()
Expand All @@ -266,7 +274,7 @@ fun toJsonFromEntry(namespace: Namespace, mappings: MappingsContainer, entry: An
}
}
map["q"] = JsonPrimitive(guessed)
map["r"] = JsonPrimitive(!guessed && namespace.id.contains("mojang"))
map["r"] = JsonPrimitive(!guessed && argsMetadata.isMojang)
JsonObject(map)
} else it
}
Expand All @@ -276,6 +284,15 @@ fun toJsonFromEntry(namespace: Namespace, mappings: MappingsContainer, entry: An
}

fun translate(
source: MappingsContainer,
target: MappingsContainer,
matchObf: Boolean,
results: Sequence<ResultHolder<*>>,
callback: (Any, Any, Double) -> Unit,
) = if (matchObf) translateViaObf(source, target, results, callback)
else translateViaIntermediary(source, target, results, callback)

fun translateViaObf(
source: MappingsContainer,
target: MappingsContainer,
results: Sequence<ResultHolder<*>>,
Expand All @@ -296,7 +313,7 @@ fun translate(
val obfName = field.obfMergedName ?: return@forEach
val parentObfName = parent.obfMergedName ?: return@forEach
val targetParent = target.getClassByObfName(parentObfName) ?: return@forEach
val targetField = targetParent.fields.firstOrNull { it.obfMergedName == obfName } ?: return@forEach
val targetField = targetParent.getFieldByObfName(obfName) ?: return@forEach

callback(value, MemberEntry(targetParent, targetField), score)
}
Expand All @@ -319,6 +336,50 @@ fun translate(
}
}

fun translateViaIntermediary(
source: MappingsContainer,
target: MappingsContainer,
results: Sequence<ResultHolder<*>>,
callback: (Any, Any, Double) -> Unit,
) {
results.forEach { (value, score) ->
when {
value is Class -> {
val intermediaryName = value.intermediaryName
val targetClass = target.getClass(intermediaryName) ?: return@forEach
callback(value, targetClass, score)
}

value is MemberEntry<*> && value.member is Field -> {
val parent = value.owner
val field = value.member as Field

val intermediaryName = field.intermediaryName
val parentIntermediaryName = parent.intermediaryName
val targetParent = target.getClass(parentIntermediaryName) ?: return@forEach
val targetField = targetParent.getField(intermediaryName) ?: return@forEach

callback(value, MemberEntry(targetParent, targetField), score)
}

value is MemberEntry<*> && value.member is Method -> {
val parent = value.owner
val method = value.member as Method

val intermediaryName = method.intermediaryName
val intermediaryDesc = method.intermediaryDesc
val parentIntermediaryName = parent.intermediaryName
val targetParent = target.getClass(parentIntermediaryName) ?: return@forEach
val targetMethod =
targetParent.methods.firstOrNull { it.intermediaryName == intermediaryName && it.intermediaryDesc == intermediaryDesc }
?: return@forEach

callback(value, MemberEntry(targetParent, targetMethod), score)
}
}
}
}

@Serializable
data class VersionInfo(
val version: String,
Expand Down Expand Up @@ -424,3 +485,10 @@ data class NamespaceEntry(
val supportsFieldDescription: Boolean,
val supportsSource: Boolean,
)

data class MethodArgMetadata(
val isMojang: Boolean,
val guesser: (Class, Method) -> List<MethodArg>?,
) {
constructor(namespace: Namespace) : this(namespace.id.contains("mojang"), { _, _ -> null })
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ import io.ktor.server.plugins.cors.routing.*
import io.ktor.server.plugins.statuspages.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import kotlinx.serialization.json.*
import kotlinx.serialization.json.addJsonObject
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.put
import kotlinx.serialization.json.putJsonArray
import me.shedaniel.linkie.Namespaces
import me.shedaniel.linkie.RemapperDaemon
import me.shedaniel.linkie.utils.tryToVersion
Expand Down Expand Up @@ -71,9 +74,10 @@ fun main() {
val allowClasses = call.parameters["allowClasses"]?.toBoolean() ?: true
val allowMethods = call.parameters["allowMethods"]?.toBoolean() ?: true
val allowFields = call.parameters["allowFields"]?.toBoolean() ?: true
val translateNsStr = call.parameters["translate"]?.lowercase()
val translateMode = call.parameters["translateMode"]?.lowercase()?.takeIf { it == "ns" || it == "ver" } ?: "ns"
val translateStr = call.parameters["translate"]?.lowercase()
try {
call.respond(search(namespaceStr, translateNsStr, version, query, allowClasses, allowMethods, allowFields, limit))
call.respond(search(namespaceStr, translateStr?.takeIf { translateMode == "ns" }, translateStr?.takeIf { translateMode == "ver" }, version, query, allowClasses, allowMethods, allowFields, limit))
} catch (error: NullPointerException) {
call.respond(buildJsonObject {
put("error", error.message ?: error.toString())
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/app/backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export function reqNamespaces<T = any>(): Promise<AxiosResponse<T>> {
}

export function reqSearch<T = any>(namespace: string, version: string, query: string, allowClasses: boolean, allowFields: boolean, allowMethods: boolean,
translate?: string, abortController?: AbortController, limit: number = 100): Promise<AxiosResponse<T>> {
translateMode?: string, translate?: string, abortController?: AbortController, limit: number = 100): Promise<AxiosResponse<T>> {
return HTTP.get(`/api/search`, {
signal: abortController?.signal,
params: {
Expand All @@ -32,6 +32,7 @@ export function reqSearch<T = any>(namespace: string, version: string, query: st
allowClasses,
allowFields,
allowMethods,
translateMode: translateMode || "ns",
translate,
},
})
Expand Down
39 changes: 32 additions & 7 deletions frontend/src/app/mappings-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ interface InfoData {
allowFields?: boolean,
allowMethods?: boolean,
translateAs?: string,
translateAsVersion?: string,
query?: string,
entries: MappingEntry[],
fuzzy: boolean,
Expand Down Expand Up @@ -111,18 +112,33 @@ export function ensureMappingsData(applyURL: boolean = false) {

if (store.mappingsData.namespaces.map(namespace => namespace.id).includes(urlParams.get("namespace") ?? "")) {
useMappingsStore().namespace = (urlParams.get("namespace") ?? "") as string
useMappingsStore().translateMode = undefined
useMappingsStore().translateAs = undefined
useMappingsStore().translateAsVersion = undefined
useMappingsStore().version = undefined
}

if (store.mappingsData.namespaces.map(namespace => namespace.id).includes(urlParams.get("translateAs") ?? "")
if (urlParams.has("translateMode")) {
useMappingsStore().translateMode = urlParams.get("translateMode") ?? ""

if (useMappingsStore().translateMode !== undefined && useMappingsStore().translateMode !in ["ns", "ver"]) {
useMappingsStore().translateMode = undefined
useMappingsStore().translateAs = undefined
useMappingsStore().translateAsVersion = undefined
}
}

if (useMappingsStore().translateMode === "ns" && store.mappingsData.namespaces.map(namespace => namespace.id).includes(urlParams.get("translateAs") ?? "")
&& useMappingsStore().namespace && useMappingsStore().namespace !== urlParams.get("translateAs")) {
useMappingsStore().translateAs = (urlParams.get("translateAs") ?? "") as string
}

if (applicableMappingsVersions().map(version => version.version).includes(urlParams.get("version") ?? "")) {
useMappingsStore().version = (urlParams.get("version") ?? "") as string
}
if (useMappingsStore().translateMode === "ver" && applicableMappingsVersions().map(version => version.version).includes(urlParams.get("translateAsVersion") ?? "")) {
useMappingsStore().translateAsVersion = (urlParams.get("translateAsVersion") ?? "") as string
}

if (urlParams.has("search")) useMappingsStore().searchText = urlParams.get("search") ?? ""
if (urlParams.has("allowClasses")) useMappingsStore().allowClasses = urlParams.get("allowClasses") === "true"
Expand All @@ -132,7 +148,7 @@ export function ensureMappingsData(applyURL: boolean = false) {
useMappingsDataStore().hasFirstLoaded = true
}

let {namespace, version, allowSnapshots, translateAs} = useMappingsStore()
let {namespace, version, allowSnapshots, translateAs, translateAsVersion} = useMappingsStore()
if (!namespace) {
namespace = store.mappingsData.namespaces[0]?.id
useMappingsStore().namespace = namespace
Expand All @@ -152,6 +168,9 @@ export function ensureMappingsData(applyURL: boolean = false) {
version = applicable_versions.find(_ => true)?.version
useMappingsStore().version = version
}
if (!applicable_versions.find(entry => entry.version === translateAsVersion)) {
useMappingsStore().translateAsVersion = undefined
}
}

if (useMappingsDataStore().hasFirstLoaded) {
Expand All @@ -161,16 +180,16 @@ export function ensureMappingsData(applyURL: boolean = false) {

export function updateMappingsInfo() {
let store = useMappingsDataStore()
let {namespace, version, searchText, allowClasses, allowFields, allowMethods, translateAs} = useMappingsStore()
let {namespace, version, searchText, allowClasses, allowFields, allowMethods, translateMode, translateAs, translateAsVersion} = useMappingsStore()
if (namespace && version && searchText && (allowClasses || allowMethods || allowFields)) {
if (store.infoData.namespace !== namespace || store.infoData.version !== version || store.infoData.query !== searchText
|| store.infoData.allowClasses !== allowClasses || store.infoData.allowFields !== allowFields || store.infoData.allowMethods !== allowMethods
|| store.infoData.translateAs !== translateAs) {
|| store.infoData.translateAs !== translateAs || store.infoData.translateAsVersion !== translateAsVersion) {
if (store.searchController) {
store.searchController.abort()
}
store.searchController = new AbortController()
reqSearch(namespace, version, searchText, allowClasses, allowFields, allowMethods, translateAs, store.searchController).then(value => {
reqSearch(namespace, version, searchText, allowClasses, allowFields, allowMethods, translateMode, translateMode === "ns" ? translateAs : translateMode === "ver" ? translateAsVersion : undefined, store.searchController).then(value => {
if (value.data.error) {
if (value.data.error !== "No results found!") {
addAlert({
Expand Down Expand Up @@ -209,14 +228,15 @@ export function updateMappingsInfo() {

export function setInfoDataToCurrent() {
let store = useMappingsDataStore()
let {namespace, version, searchText, allowClasses, allowFields, allowMethods, translateAs} = useMappingsStore()
let {namespace, version, searchText, allowClasses, allowFields, allowMethods, translateAs, translateAsVersion} = useMappingsStore()
store.infoData.namespace = namespace
store.infoData.version = version
store.infoData.query = searchText
store.infoData.allowClasses = allowClasses
store.infoData.allowMethods = allowMethods
store.infoData.allowFields = allowFields
store.infoData.translateAs = translateAs
store.infoData.translateAsVersion = translateAsVersion
}

export function mapEntryToMappingEntry(obj: any): MappingEntry {
Expand Down Expand Up @@ -253,7 +273,7 @@ export function mapEntryToMappingEntry(obj: any): MappingEntry {
}

export function updateMappingsWindowUrl() {
let {namespace, version, searchText, allowClasses, allowFields, allowMethods, translateAs} = useMappingsStore()
let {namespace, version, searchText, allowClasses, allowFields, allowMethods, translateMode, translateAs, translateAsVersion} = useMappingsStore()
let url = new URL(window.location.href)
url.searchParams.set("namespace", namespace ?? "")
url.searchParams.set("version", version ?? "")
Expand All @@ -268,8 +288,13 @@ export function updateMappingsWindowUrl() {
if (!allowMethods) url.searchParams.set("allowMethods", "false")
else url.searchParams.delete("allowMethods")

url.searchParams.set("translateMode", translateMode ?? "none")

if (translateAs) url.searchParams.set("translateAs", translateAs)
else url.searchParams.delete("translateAs")

if (translateAsVersion) url.searchParams.set("translateAsVersion", translateAsVersion)
else url.searchParams.delete("translateAsVersion")

window.history.replaceState({}, "", url.toString())
}
3 changes: 3 additions & 0 deletions frontend/src/app/mappings-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ interface State {
allowFields: boolean,
allowMethods: boolean,
searchText: string,
translateMode?: string,
translateAs?: string,
translateAsVersion?: string,
}

export interface VersionPossible {
Expand All @@ -26,6 +28,7 @@ function newState(): State {
allowFields: true,
allowMethods: true,
searchText: "",
translateMode: undefined,
translateAs: undefined,
}
}
Expand Down
Loading

0 comments on commit c6a72ac

Please sign in to comment.