feat: personal run replays (ghost system)
- GhostData: worldName field, format v4; GhostManager stores last N runs per player (ghosts/<uuid>/<ts>.ghost)
- GhostService: per-player recording (start on run start, every tick, finalize on fall), gated by ghost-mode.enabled + per-player toggle
- PlayerGhost: packet-based fake-player NPC with viewer skin (NMS via remapNms gradle task, reflection send, graceful degradation)
- GhostReplay: teleport viewer to run start, replay frames via PlayerGhost, restore on finish
- /lp replay [index] command + tab completion; settings menu ghost_recording toggle (persisted disk/SQL)
- config: ghost-mode.{enabled,max-runs}, remove dead show-top/transparency + GHOST_* statics
- tests: GhostData v4 round-trip, GhostManager eviction/ordering, yawToFixed (79 total)
This commit is contained in:
parent
4c255111f0
commit
8c8046a4f5
30 changed files with 1027 additions and 227 deletions
|
|
@ -1,3 +1,19 @@
|
|||
import java.io.File
|
||||
import java.net.URI
|
||||
import java.util.zip.ZipFile
|
||||
|
||||
// Remap tooling: rebuilds the Mojang-mapped 1.20.4 NMS classes at build time.
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven("https://maven.fabricmc.net/")
|
||||
}
|
||||
dependencies {
|
||||
classpath("net.fabricmc:mapping-io:0.6.1")
|
||||
classpath("net.fabricmc:tiny-remapper:0.9.0")
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id("java")
|
||||
id("io.github.goooler.shadow") version("8.1.8")
|
||||
|
|
@ -77,6 +93,63 @@ repositories {
|
|||
}
|
||||
}
|
||||
|
||||
// --- Mojang-mapped NMS (net.minecraft.*) for the packet NPC renderer ---------
|
||||
// repo.papermc.io purged the old io.papermc.paper:paper:1.20.4-R0.1-SNAPSHOT
|
||||
// dev-bundle, so the 1.20.4 vanilla server jar (bundler/obfuscated) is remapped
|
||||
// with Mojang's official mappings via tiny-remapper into the same Mojang-mapped
|
||||
// classes the dev-bundle shipped. Result is cached under the Gradle user home.
|
||||
val nmsVersion = "1.20.4"
|
||||
val nmsCacheDir = gradle.gradleUserHomeDir.resolve("caches/loparkour-nms/$nmsVersion")
|
||||
val nmsServerJar = nmsCacheDir.resolve("server.jar")
|
||||
val nmsNestedJar = nmsCacheDir.resolve("server-nested.jar")
|
||||
val nmsMappings = nmsCacheDir.resolve("server-mappings.txt")
|
||||
val nmsTiny = nmsCacheDir.resolve("server-mappings.tiny")
|
||||
val nmsMappedJar = nmsCacheDir.resolve("server-mojang-mapped.jar")
|
||||
val nmsAuthlibJar = nmsCacheDir.resolve("authlib-6.0.52.jar")
|
||||
|
||||
val remapNms by tasks.registering {
|
||||
outputs.files(nmsServerJar, nmsMappings, nmsMappedJar, nmsAuthlibJar)
|
||||
doLast {
|
||||
nmsCacheDir.mkdirs()
|
||||
fun download(url: String, target: File) {
|
||||
if (target.isFile) {
|
||||
return
|
||||
}
|
||||
URI(url).toURL().openStream().use { input ->
|
||||
target.outputStream().use { input.copyTo(it) }
|
||||
}
|
||||
}
|
||||
download("https://piston-data.mojang.com/v1/objects/8dd1a28015f51b1803213892b50b7b4fc76e594d/server.jar", nmsServerJar)
|
||||
download("https://piston-data.mojang.com/v1/objects/c1cafe916dd8b58ed1fe0564fc8f786885224e62/server.txt", nmsMappings)
|
||||
download("https://libraries.minecraft.net/com/mojang/authlib/6.0.52/authlib-6.0.52.jar", nmsAuthlibJar)
|
||||
if (!nmsNestedJar.isFile) {
|
||||
ZipFile(nmsServerJar).use { zip ->
|
||||
zip.getInputStream(zip.getEntry("META-INF/versions/$nmsVersion/server-$nmsVersion.jar")).use { input ->
|
||||
nmsNestedJar.outputStream().use { input.copyTo(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
val tree = net.fabricmc.mappingio.tree.MemoryMappingTree(false)
|
||||
net.fabricmc.mappingio.MappingReader.read(nmsMappings.toPath(), net.fabricmc.mappingio.format.MappingFormat.PROGUARD_FILE, tree)
|
||||
net.fabricmc.mappingio.MappingWriter.create(nmsTiny.toPath(), net.fabricmc.mappingio.format.MappingFormat.TINY_2_FILE).use { writer ->
|
||||
tree.accept(net.fabricmc.mappingio.adapter.MappingSourceNsSwitch(writer, "target"))
|
||||
}
|
||||
val remapper = net.fabricmc.tinyremapper.TinyRemapper.newRemapper()
|
||||
.withMappings(net.fabricmc.tinyremapper.TinyUtils.createTinyMappingProvider(nmsTiny.toPath(), "target", "source"))
|
||||
.ignoreConflicts(true)
|
||||
.rebuildSourceFilenames(true)
|
||||
.build()
|
||||
net.fabricmc.tinyremapper.OutputConsumerPath.Builder(nmsMappedJar.toPath()).build().use { output ->
|
||||
output.addNonClassFiles(nmsNestedJar.toPath())
|
||||
remapper.readInputs(nmsNestedJar.toPath())
|
||||
remapper.apply(output)
|
||||
}
|
||||
remapper.finish()
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<JavaCompile>().configureEach { dependsOn(remapNms) }
|
||||
|
||||
// Resolve the org.bukkit:bukkit capability conflict that newer paper-api artifacts
|
||||
// introduce (they both bundle the bukkit classes and declare the capability).
|
||||
configurations.configureEach {
|
||||
|
|
@ -100,6 +173,13 @@ dependencies {
|
|||
} else {
|
||||
compileOnly(apiCoordinate)
|
||||
}
|
||||
|
||||
// NMS (net.minecraft.*) for packet NPC rendering — compileOnly, Mojang-mapped.
|
||||
// Built once by the remapNms task above (vanilla 1.20.4 server + official
|
||||
// mappings) because the io.papermc.paper:paper:1.20.4 dev-bundle was purged.
|
||||
compileOnly(files(nmsMappedJar, nmsAuthlibJar))
|
||||
// fastutil types are referenced by the NMS packet signatures (IntList).
|
||||
compileOnly("it.unimi.dsi:fastutil:8.5.9")
|
||||
|
||||
// Adventure API (for Component)
|
||||
compileOnly("net.kyori:adventure-api:4.14.0")
|
||||
|
|
@ -133,6 +213,8 @@ dependencies {
|
|||
testImplementation("org.mockito:mockito-junit-jupiter:5.10.0")
|
||||
testImplementation("org.spigotmc:spigot-api:1.20.4-R0.1-SNAPSHOT")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
// Test runtime classpath for loading PlayerGhost (references NMS + authlib types).
|
||||
testImplementation(files(nmsMappedJar, nmsAuthlibJar))
|
||||
}
|
||||
|
||||
tasks.shadowJar {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue