Merge branch 'johni/1.13-pre' into johni/blend

This commit is contained in:
Jonas Herzig
2019-03-13 16:07:32 +01:00
177 changed files with 7973 additions and 3552 deletions

View File

@@ -8,8 +8,9 @@ pipeline:
commands: commands:
- curl --fail --create-dirs -o $HOME/.gradle/init.d/mirror.gradle https://maven.johni0702.de/config/gradle - curl --fail --create-dirs -o $HOME/.gradle/init.d/mirror.gradle https://maven.johni0702.de/config/gradle
- git config --global user.email "replaymod@drone" && git config --global user.name "Drone" - git config --global user.email "replaymod@drone" && git config --global user.name "Drone"
- REPRODUCIBLE_BUILD=1 ./gradlew :1.7.10:setupCIWorkspace :jGui:1.7.10:setupCIWorkspace - ./gradlew --old-gradle :1.7.10:setupCIWorkspace :jGui:1.7.10:setupCIWorkspace
- SIGNED_JAR=$(! git describe --exact 2>&1 > /dev/null; echo $?) REPRODUCIBLE_BUILD=1 ./gradlew $(bash -c "echo :{1.7.10,1.8,1.8.9,1.9.4,1.10.2,1.11,1.11.2,1.12,1.12.1,1.12.2}:reobfJar") --parallel - ./gradlew --old-gradle :1.7.10:reobfJar
- ./gradlew $(bash -c "echo :{1.8,1.8.9,1.9.4,1.10.2,1.11,1.11.2,1.12,1.12.1,1.12.2,1.13.2}:reobfJar") --parallel
- mkdir -p build/libs && mv versions/*/build/libs/* build/libs/ - mkdir -p build/libs && mv versions/*/build/libs/* build/libs/
archive: archive:
image: plugins/s3 image: plugins/s3

View File

@@ -1 +1 @@
eyJhbGciOiJIUzI1NiJ9.cGlwZWxpbmU6CiAgY2xvbmU6CiAgICBpbWFnZTogcGx1Z2lucy9naXQKICAgIHRhZ3M6IHRydWUKICAgIHJlY3Vyc2l2ZTogdHJ1ZQogIGJ1aWxkOgogICAgaW1hZ2U6IG1hdmVuOjMuMy45LWpkay04CiAgICBjb21tYW5kczoKICAgICAgLSBjdXJsIC0tZmFpbCAtLWNyZWF0ZS1kaXJzIC1vICRIT01FLy5ncmFkbGUvaW5pdC5kL21pcnJvci5ncmFkbGUgaHR0cHM6Ly9tYXZlbi5qb2huaTA3MDIuZGUvY29uZmlnL2dyYWRsZQogICAgICAtIGdpdCBjb25maWcgLS1nbG9iYWwgdXNlci5lbWFpbCAicmVwbGF5bW9kQGRyb25lIiAmJiBnaXQgY29uZmlnIC0tZ2xvYmFsIHVzZXIubmFtZSAiRHJvbmUiCiAgICAgIC0gUkVQUk9EVUNJQkxFX0JVSUxEPTEgLi9ncmFkbGV3IDoxLjcuMTA6c2V0dXBDSVdvcmtzcGFjZSA6akd1aToxLjcuMTA6c2V0dXBDSVdvcmtzcGFjZQogICAgICAtIFNJR05FRF9KQVI9JCghIGdpdCBkZXNjcmliZSAtLWV4YWN0IDI-JjEgPiAvZGV2L251bGw7IGVjaG8gJD8pIFJFUFJPRFVDSUJMRV9CVUlMRD0xIC4vZ3JhZGxldyAkKGJhc2ggLWMgImVjaG8gOnsxLjcuMTAsMS44LDEuOC45LDEuOS40LDEuMTAuMiwxLjExLDEuMTEuMiwxLjEyLDEuMTIuMSwxLjEyLjJ9OnJlb2JmSmFyIikgLS1wYXJhbGxlbAogICAgICAtIG1rZGlyIC1wIGJ1aWxkL2xpYnMgJiYgbXYgdmVyc2lvbnMvKi9idWlsZC9saWJzLyogYnVpbGQvbGlicy8KICBhcmNoaXZlOgogICAgaW1hZ2U6IHBsdWdpbnMvczMKICAgIGFjbDogcHVibGljLXJlYWQKICAgIHJlZ2lvbjogInVzLWVhc3QtMSIKICAgIGJ1Y2tldDogInJlcGxheW1vZCIKICAgIHBhdGhfc3R5bGU6IHRydWUKICAgIHNvdXJjZTogYnVpbGQvbGlicy8qCiAgICBzdHJpcF9wcmVmaXg6IGJ1aWxkL2xpYnMvCiAgICB0YXJnZXQ6IC8K.2bNe9Pd4FUTIP9VTrFR4YuHJkgCmjIbfBCUu-Wrvap8 eyJhbGciOiJIUzI1NiJ9.cGlwZWxpbmU6CiAgY2xvbmU6CiAgICBpbWFnZTogcGx1Z2lucy9naXQKICAgIHRhZ3M6IHRydWUKICAgIHJlY3Vyc2l2ZTogdHJ1ZQogIGJ1aWxkOgogICAgaW1hZ2U6IG1hdmVuOjMuMy45LWpkay04CiAgICBjb21tYW5kczoKICAgICAgLSBjdXJsIC0tZmFpbCAtLWNyZWF0ZS1kaXJzIC1vICRIT01FLy5ncmFkbGUvaW5pdC5kL21pcnJvci5ncmFkbGUgaHR0cHM6Ly9tYXZlbi5qb2huaTA3MDIuZGUvY29uZmlnL2dyYWRsZQogICAgICAtIGdpdCBjb25maWcgLS1nbG9iYWwgdXNlci5lbWFpbCAicmVwbGF5bW9kQGRyb25lIiAmJiBnaXQgY29uZmlnIC0tZ2xvYmFsIHVzZXIubmFtZSAiRHJvbmUiCiAgICAgIC0gLi9ncmFkbGV3IC0tb2xkLWdyYWRsZSA6MS43LjEwOnNldHVwQ0lXb3Jrc3BhY2UgOmpHdWk6MS43LjEwOnNldHVwQ0lXb3Jrc3BhY2UKICAgICAgLSAuL2dyYWRsZXcgLS1vbGQtZ3JhZGxlIDoxLjcuMTA6cmVvYmZKYXIKICAgICAgLSAuL2dyYWRsZXcgJChiYXNoIC1jICJlY2hvIDp7MS44LDEuOC45LDEuOS40LDEuMTAuMiwxLjExLDEuMTEuMiwxLjEyLDEuMTIuMSwxLjEyLjIsMS4xMy4yfTpyZW9iZkphciIpIC0tcGFyYWxsZWwKICAgICAgLSBta2RpciAtcCBidWlsZC9saWJzICYmIG12IHZlcnNpb25zLyovYnVpbGQvbGlicy8qIGJ1aWxkL2xpYnMvCiAgYXJjaGl2ZToKICAgIGltYWdlOiBwbHVnaW5zL3MzCiAgICBhY2w6IHB1YmxpYy1yZWFkCiAgICByZWdpb246ICJ1cy1lYXN0LTEiCiAgICBidWNrZXQ6ICJyZXBsYXltb2QiCiAgICBwYXRoX3N0eWxlOiB0cnVlCiAgICBzb3VyY2U6IGJ1aWxkL2xpYnMvKgogICAgc3RyaXBfcHJlZml4OiBidWlsZC9saWJzLwogICAgdGFyZ2V0OiAvCg.xvkvCrkXlWrSXghw1bLwsewIfd0vJZyz-Wk15airj_s

BIN
.gitignore vendored

Binary file not shown.

View File

@@ -8,6 +8,10 @@ After the initial clone, running `./gradlew setupDecompWorkspace` is necessary.
This will also be necessary if the `replaymod_at.cfg` file has been changed (getting errors about code that is trying This will also be necessary if the `replaymod_at.cfg` file has been changed (getting errors about code that is trying
to access private fields is a good indication that this has happened). to access private fields is a good indication that this has happened).
**Note**: ForgeGradle 1.2 (used for 1.7.10) is not compatible with Gradle 4.
As such, you need to use an older version (Gradle 3.x) to build the ReplayMod for 1.7.10.
An easy way to do that is to pass `--old-gradle` as the first argument to the Gradle wrapper shell script: `./gradlew --old-gradle build`
### No IDE ### No IDE
You can build the mod by running `./gradlew build`. You can then find the final jar files in `versions/$MCVERSION/build/libs/`. You can build the mod by running `./gradlew build`. You can then find the final jar files in `versions/$MCVERSION/build/libs/`.
You can also build single versions by running `./gradlew :1.8:build` (builds the MC 1.8 version). You can also build single versions by running `./gradlew :1.8:build` (builds the MC 1.8 version).

View File

@@ -1,3 +1,6 @@
apply plugin: 'eclipse'
apply plugin: 'idea'
import groovy.json.JsonOutput import groovy.json.JsonOutput
def latestVersion = file('version.txt').readLines().first() def latestVersion = file('version.txt').readLines().first()
@@ -13,7 +16,7 @@ if (releaseCommit == currentCommit) {
version = "$latestVersion-$diff-g${currentCommit.substring(0, 7)}" version = "$latestVersion-$diff-g${currentCommit.substring(0, 7)}"
} }
if (gitDescribe().endsWith('*')) { if (gitDescribe().endsWith('*')) {
version = "$version*" version = "$version-dirty"
} }
group= "com.replaymod" group= "com.replaymod"
@@ -23,18 +26,6 @@ subprojects {
repositories { repositories {
maven { url 'https://jitpack.io' } maven { url 'https://jitpack.io' }
} }
configurations.classpath.resolutionStrategy.eachDependency { dep ->
if (System.getenv('REPRODUCIBLE_BUILD') == '1') return
def req = dep.target
// Substitute FG 2.0 with custom fork that works with Gradle 3
if (req.group == 'net.minecraftforge.gradle' && req.name == 'ForgeGradle' && req.version == '2.0-SNAPSHOT') {
dep.useTarget 'com.github.ReplayMod:ForgeGradle:dd521d0'
}
// Substitute FG 2.1 with custom fork that works with Gradle 3
if (req.group == 'net.minecraftforge.gradle' && req.name == 'ForgeGradle' && req.version == '2.1-SNAPSHOT') {
dep.useTarget 'com.github.ReplayMod:ForgeGradle:9c2f79c'
}
}
} }
// Thanks for wasting my time gradle... // Thanks for wasting my time gradle...

5
gradle.properties Normal file
View File

@@ -0,0 +1,5 @@
# Sets default memory used for gradle commands. Can be overridden by user or command line properties.
# This is required to provide enough memory for the Minecraft decompilation process.
org.gradle.jvmargs=-Xmx3G
org.gradle.daemon=false
org.gradle.parallel=

View File

View File

@@ -30,24 +30,24 @@ cddfc1a07301f2fa07a5e1b6897e455d397d6d5c547d45082c267a1ee2c75f70 /com/github/Mod
7191124b8491d36b9fc1eb1a4e0f8ef710a4b98e27dde92854c24d45457fd9d0 /com/github/ModCoderPack/MCInjector/05aa80e/MCInjector-05aa80e.pom 7191124b8491d36b9fc1eb1a4e0f8ef710a4b98e27dde92854c24d45457fd9d0 /com/github/ModCoderPack/MCInjector/05aa80e/MCInjector-05aa80e.pom
ecf57aab63509ec0ce3937c2215bc3a2e3cb6357cd11095978db18bd3251731a /com/github/ModCoderPack/MCInjector/7258466/MCInjector-7258466.jar ecf57aab63509ec0ce3937c2215bc3a2e3cb6357cd11095978db18bd3251731a /com/github/ModCoderPack/MCInjector/7258466/MCInjector-7258466.jar
dc7ed1f4f0ddd8f5ce3b449db944907a5eb2855dbbbc59b08dad1247ebafba33 /com/github/ModCoderPack/MCInjector/7258466/MCInjector-7258466.pom dc7ed1f4f0ddd8f5ce3b449db944907a5eb2855dbbbc59b08dad1247ebafba33 /com/github/ModCoderPack/MCInjector/7258466/MCInjector-7258466.pom
e1a215146621ab100c5d88c6c65e7b76c91f2a06ab8714ac438738ddec5ea904 /com/github/ReplayMod/ReplayStudio/1.10.2/3680c2e/1.10.2-3680c2e.jar 72dd4d393e1103a8719629c2231b7fc4ac307f339c24d0da94c6cc1d050838ec /com/github/ReplayMod/ReplayStudio/1.10.2/2d8d41d/1.10.2-2d8d41d.jar
da0b27f2719bf829f91eb4d6ea7d6e54c71b301a19578bafe6ec05e72ed04a6a /com/github/ReplayMod/ReplayStudio/1.10.2/3680c2e/1.10.2-3680c2e.pom 6849dab3156262b109d07bba0ac56ed996f6581b2f7f4065c88afe0a9d3a620c /com/github/ReplayMod/ReplayStudio/1.10.2/2d8d41d/1.10.2-2d8d41d.pom
67e8b400d005235bd2825717864c4a9341567302877c857c3681adc74802b2ba /com/github/ReplayMod/ReplayStudio/1.11.2/3680c2e/1.11.2-3680c2e.jar 868ce8eb3c2cf8628edb952828cbfec26fa9c443a33a6125a912c40548e0a835 /com/github/ReplayMod/ReplayStudio/1.11.2/2d8d41d/1.11.2-2d8d41d.jar
ff786e8da1d52bfdca8abe58bc83cf9200e027a85212da633e795811b5c8b1ee /com/github/ReplayMod/ReplayStudio/1.11.2/3680c2e/1.11.2-3680c2e.pom 599546e6a69a60df616f657f3bda58d0d4e58103a17de351c65941e0b53bc658 /com/github/ReplayMod/ReplayStudio/1.11.2/2d8d41d/1.11.2-2d8d41d.pom
68da074f5cccd590c9584efc885f6ea842a15f1ce968aad04cbac6af2c8ecf49 /com/github/ReplayMod/ReplayStudio/1.11/3680c2e/1.11-3680c2e.jar 4ccffcdea734d26004364737b90cc394049a11f3ab0d47c66cdd2f0d5728e316 /com/github/ReplayMod/ReplayStudio/1.11/2d8d41d/1.11-2d8d41d.jar
79a6ed9587d6b23f5221b0fdff12f01de4859dc985f3cdbebf081edd4e478a54 /com/github/ReplayMod/ReplayStudio/1.11/3680c2e/1.11-3680c2e.pom e8e9bf0ee84eb6f89ed6ec28d5142fc0cdb68eea74a763a6f8ee5f4ee8af00d2 /com/github/ReplayMod/ReplayStudio/1.11/2d8d41d/1.11-2d8d41d.pom
60549740b778011d5e4a8b786805c6de8c518ce8f627cf70369b2c71f0d399c4 /com/github/ReplayMod/ReplayStudio/1.12.1/3680c2e/1.12.1-3680c2e.jar 57fef9ef4cbcf766f73122556d05aab6e80fecc3c9b12d0efdfb090500d422cf /com/github/ReplayMod/ReplayStudio/1.12.2/2d8d41d/1.12.2-2d8d41d.jar
da248126087579d384175fa36eb654c8755027727aff6c285cd8e4e056ddf424 /com/github/ReplayMod/ReplayStudio/1.12.1/3680c2e/1.12.1-3680c2e.pom 55601b8a60329c2b5271dd8a44f7dd82bbf4288d698e840c111edeb9a0b52517 /com/github/ReplayMod/ReplayStudio/1.12.2/2d8d41d/1.12.2-2d8d41d.pom
30ecf796e3697cf7d86c21f245695e659b39f55f2aecf40864ec6ac2c0eeae87 /com/github/ReplayMod/ReplayStudio/1.12.2/3680c2e/1.12.2-3680c2e.jar f175ad66645b7fdf6e472ccac0e01893ea22cfb7812636013d0982b6280b4663 /com/github/ReplayMod/ReplayStudio/1.12.1/2d8d41d/1.12.1-2d8d41d.jar
73c4c53e5f18cffd921db81019510c47afb306eb2aea86377f44fc91e8b45130 /com/github/ReplayMod/ReplayStudio/1.12.2/3680c2e/1.12.2-3680c2e.pom d3ea142c6ef67c84d1b0f658a8a17612465fc2d89fb8c10fe15def51392a9fc2 /com/github/ReplayMod/ReplayStudio/1.12.1/2d8d41d/1.12.1-2d8d41d.pom
2f0e27b40a0c3ac298573c6a2fad24829e968731676305b5f1c471a827ac7458 /com/github/ReplayMod/ReplayStudio/1.12/3680c2e/1.12-3680c2e.jar bcf66a214b72615ba0d60a7d4a8be0ae3480baa5a65aa37732616d0d7154fe68 /com/github/ReplayMod/ReplayStudio/1.12/2d8d41d/1.12-2d8d41d.jar
282e2fa0ef80206ab592f90f8585d5ecf5adfb110dcd1cb5204971257e3fd44e /com/github/ReplayMod/ReplayStudio/1.12/3680c2e/1.12-3680c2e.pom 6681d0640cb569a2b2c8c082371e80f96e67b3ba292a4ba56cf188a71a6d915b /com/github/ReplayMod/ReplayStudio/1.12/2d8d41d/1.12-2d8d41d.pom
1aa22b15d5e89258fc58c2a310c915937cf93b42bf27b9fefc04d20b6b07b19c /com/github/ReplayMod/ReplayStudio/1.7.10/3680c2e/1.7.10-3680c2e.jar 7f718341faa2b1203b078e8a416b74ae195dc98f98eebb0e08eb462cf98d6cd7 /com/github/ReplayMod/ReplayStudio/1.7.10/2d8d41d/1.7.10-2d8d41d.jar
fbd4bfe141d845248bb095d59f3aaf273bf20ad849cec89d21594754a69f3f53 /com/github/ReplayMod/ReplayStudio/1.7.10/3680c2e/1.7.10-3680c2e.pom d482d4a1d6b3b169822512ecb8a296ed43cbdba2f3ae85cc391c9e890526b7e7 /com/github/ReplayMod/ReplayStudio/1.7.10/2d8d41d/1.7.10-2d8d41d.pom
96b37e76cf8b4aac4f0419d84ba6427da156cd7c4f41f9b9380d149893dd50a7 /com/github/ReplayMod/ReplayStudio/1.8/3680c2e/1.8-3680c2e.jar dc19cacb7b2e8dbce84200f78f0799ec8509343777b6d7511614e4b3a48e6f26 /com/github/ReplayMod/ReplayStudio/1.8/2d8d41d/1.8-2d8d41d.pom
7a68b798f4029b98b391076992d39633b3060d06d43edc25422edbbed09914f5 /com/github/ReplayMod/ReplayStudio/1.8/3680c2e/1.8-3680c2e.pom 6497cc087743beaaf397cda6d2ca76eb5afa37dae9889ea587ef8deae6483edd /com/github/ReplayMod/ReplayStudio/1.8/2d8d41d/1.8-2d8d41d.jar
5e5bea81fd85631a5f80973fab581e991d793d543e2bbbdbf1bebc15b4a279cd /com/github/ReplayMod/ReplayStudio/1.9.4/3680c2e/1.9.4-3680c2e.jar 9134cdee78eaac0c2c4a4728cef40fb8b9c824cbb587efdde5d768f23f295dfc /com/github/ReplayMod/ReplayStudio/1.9.4/2d8d41d/1.9.4-2d8d41d.jar
cb0a152f7c720de75325b2cb857879596edf8c4b0de6f5b39168a1be1aa6709f /com/github/ReplayMod/ReplayStudio/1.9.4/3680c2e/1.9.4-3680c2e.pom 67faf1d44985182c4056ddbde300db813ca424b3f07818172eedcfc0f0a2c496 /com/github/ReplayMod/ReplayStudio/1.9.4/2d8d41d/1.9.4-2d8d41d.pom
bfdcda4cbf23f28392384818a1417c2d6df687e15ce77530ef96381393102f8a /com/github/SpongePowered/MixinGradle/dcfaf61/MixinGradle-dcfaf61.jar bfdcda4cbf23f28392384818a1417c2d6df687e15ce77530ef96381393102f8a /com/github/SpongePowered/MixinGradle/dcfaf61/MixinGradle-dcfaf61.jar
3552e0c04817d91d1b6671a051d0e1d952c8b6a8eec57b54e829de9fa3ddf002 /com/github/SpongePowered/MixinGradle/dcfaf61/MixinGradle-dcfaf61.pom 3552e0c04817d91d1b6671a051d0e1d952c8b6a8eec57b54e829de9fa3ddf002 /com/github/SpongePowered/MixinGradle/dcfaf61/MixinGradle-dcfaf61.pom
c6ebd74cd6108b8c372c95b842725ee5bd6d3303ba26cb00f18bc92f80b47956 /com/github/tony19/named-regexp/0.2.3/named-regexp-0.2.3.jar c6ebd74cd6108b8c372c95b842725ee5bd6d3303ba26cb00f18bc92f80b47956 /com/github/tony19/named-regexp/0.2.3/named-regexp-0.2.3.jar

View File

@@ -34,7 +34,7 @@ popd
java -Dproxywitness.httpUris=http://export.mcpbot.bspk.rs/versions.json \ java -Dproxywitness.httpUris=http://export.mcpbot.bspk.rs/versions.json \
-Dproxywitness.useCache=false \ -Dproxywitness.useCache=false \
-jar deps/proxy-witness/build/libs/proxy-witness.jar \ -jar deps/proxy-witness/build/libs/proxy-witness.jar \
"$PROXY_PORT" checksums.txt > proxy.log 2>&1 & "$PROXY_PORT" checksums.empty.txt > proxy.log 2>&1 &
proxy_pid=$! proxy_pid=$!
trap "kill $proxy_pid" EXIT trap "kill $proxy_pid" EXIT

BIN
gradle/wrapper/gradle-wrapper-old.jar vendored Executable file

Binary file not shown.

View File

@@ -0,0 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.5.1-all.zip
distributionSha256Sum=69d99ade952de3727b65a88f3188ee72231ff4e28c2d00aacfc96a42c6d6f303

View File

@@ -2,5 +2,5 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.5.1-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-all.zip
distributionSha256Sum=69d99ade952de3727b65a88f3188ee72231ff4e28c2d00aacfc96a42c6d6f303 distributionSha256Sum=39e2d5803bbd5eaf6c8efe07067b0e5a00235e8c71318642b2ed262920b27721

7
gradlew vendored
View File

@@ -65,7 +65,12 @@ cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`" APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar if [ "$1" == "--old-gradle" ]; then
shift
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper-old.jar
else
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
fi
# Determine the Java command to use to start the JVM. # Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then if [ -n "$JAVA_HOME" ] ; then

2
jGui

Submodule jGui updated: 097131041b...79b0aaf3e9

View File

@@ -1,12 +1,47 @@
def f = file('versions/core/mcVersion')
def coreMcVersion = f.exists() ? f.readLines().first() as int : 11302
def jGuiVersions = []
def replayModVersions = []
if (gradle.gradleVersion.startsWith('3.')) {
if (coreMcVersion <= 10710) {
jGuiVersions += ['core']
replayModVersions += ['core']
}
jGuiVersions += [
'1.7.10',
]
replayModVersions += [
'1.7.10',
]
} else {
if (coreMcVersion > 10710) {
jGuiVersions += ['core']
replayModVersions += ['core']
}
jGuiVersions += [
'1.8',
'1.8.9',
'1.9.4',
'1.12',
'1.13.2'
]
replayModVersions += [
'1.8',
'1.8.9',
'1.9.4',
'1.10.2',
'1.11',
'1.11.2',
'1.12',
'1.12.1',
'1.12.2',
'1.13.2'
]
}
include ':jGui' include ':jGui'
[ jGuiVersions.each { version ->
'core',
'1.7.10',
'1.8',
'1.8.9',
'1.9.4',
'1.12'
].each { version ->
include ":jGui:$version" include ":jGui:$version"
project(":jGui:$version").with { project(":jGui:$version").with {
projectDir = file("jGui/versions/$version") projectDir = file("jGui/versions/$version")
@@ -14,20 +49,7 @@ include ':jGui'
} }
} }
def versions = [ replayModVersions.each { version ->
'core',
'1.7.10',
'1.8',
'1.8.9',
'1.9.4',
'1.10.2',
'1.11',
'1.11.2',
'1.12',
'1.12.1',
'1.12.2'
]
versions.each { version ->
include ":$version" include ":$version"
project(":$version").with { project(":$version").with {
projectDir = file("versions/$version") projectDir = file("versions/$version")

View File

@@ -27,7 +27,13 @@ public class OptifineReflection {
public static void reloadLang() { public static void reloadLang() {
try { try {
Class.forName("Lang").getDeclaredMethod("resourcesReloaded").invoke(null); Class<?> langClass;
try {
langClass = Class.forName("Lang"); // Pre Optifine 1.12.2 E1
} catch (ClassNotFoundException ignore) {
langClass = Class.forName("net.optifine.Lang"); // Post Optifine 1.12.2 E1
}
langClass.getDeclaredMethod("resourcesReloaded").invoke(null);
} catch (ClassNotFoundException ignore) { } catch (ClassNotFoundException ignore) {
// no optifine installed // no optifine installed
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {

View File

@@ -25,16 +25,19 @@ public class ShaderReflection {
static { static {
try { try {
shaders_frameTimeCounter = Class.forName("shadersmod.client.Shaders") Class<?> shadersClass;
.getDeclaredField("frameTimeCounter"); try {
shadersClass = Class.forName("shadersmod.client.Shaders"); // Pre Optifine 1.12.2 E1
} catch (ClassNotFoundException ignore) {
shadersClass = Class.forName("net.optifine.shaders.Shaders"); // Post Optifine 1.12.2 E1
}
shaders_frameTimeCounter = shadersClass.getDeclaredField("frameTimeCounter");
shaders_frameTimeCounter.setAccessible(true); shaders_frameTimeCounter.setAccessible(true);
shaders_isShadowPass = Class.forName("shadersmod.client.Shaders") shaders_isShadowPass = shadersClass.getDeclaredField("isShadowPass");
.getDeclaredField("isShadowPass");
shaders_isShadowPass.setAccessible(true); shaders_isShadowPass.setAccessible(true);
shaders_beginRender = Class.forName("shadersmod.client.Shaders") shaders_beginRender = shadersClass.getDeclaredMethod("beginRender", Minecraft.class, float.class, long.class);
.getDeclaredMethod("beginRender", Minecraft.class, float.class, long.class);
shaders_beginRender.setAccessible(true); shaders_beginRender.setAccessible(true);
renderGlobal_chunksToUpdateForced = Class.forName("net.minecraft.client.renderer.RenderGlobal") renderGlobal_chunksToUpdateForced = Class.forName("net.minecraft.client.renderer.RenderGlobal")

View File

@@ -9,7 +9,10 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Pseudo @Pseudo
@Mixin(targets = "shadersmod/client/ShadersRender", remap = false) @Mixin(targets = {
"shadersmod/client/ShadersRender", // Pre Optifine 1.12.2 E1
"net/optifine/shaders/ShadersRender" // Post Optifine 1.12.2 E1
}, remap = false)
public abstract class MixinShadersRender { public abstract class MixinShadersRender {
@Inject(method = "renderHand0", at = @At("HEAD"), cancellable = true) @Inject(method = "renderHand0", at = @At("HEAD"), cancellable = true)

View File

@@ -6,12 +6,19 @@ import com.replaymod.core.versions.MCVer;
import net.minecraft.client.settings.KeyBinding; import net.minecraft.client.settings.KeyBinding;
import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReport;
import net.minecraft.crash.CrashReportCategory; import net.minecraft.crash.CrashReportCategory;
import net.minecraft.util.ReportedException;
import org.lwjgl.input.Keyboard; //#if MC>=11300
//#else
//$$ import org.lwjgl.input.Keyboard;
//#endif
//#if MC>=10800 //#if MC>=10800
//#if MC>=11300
import net.minecraftforge.eventbus.api.SubscribeEvent;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#endif
import net.minecraftforge.fml.client.registry.ClientRegistry; import net.minecraftforge.fml.client.registry.ClientRegistry;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.InputEvent; import net.minecraftforge.fml.common.gameevent.InputEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent; import net.minecraftforge.fml.common.gameevent.TickEvent;
//#else //#else
@@ -95,24 +102,25 @@ public class KeyBindingRegistry {
} catch (Throwable cause) { } catch (Throwable cause) {
CrashReport crashReport = CrashReport.makeCrashReport(cause, "Handling Key Binding"); CrashReport crashReport = CrashReport.makeCrashReport(cause, "Handling Key Binding");
CrashReportCategory category = crashReport.makeCategory("Key Binding"); CrashReportCategory category = crashReport.makeCategory("Key Binding");
category.addCrashSection("Key Binding", keyBinding); MCVer.addDetail(category, "Key Binding", keyBinding::toString);
MCVer.addDetail(category, "Handler", runnable::toString); MCVer.addDetail(category, "Handler", runnable::toString);
throw new ReportedException(crashReport); throw newReportedException(crashReport);
} }
} }
} }
public void handleRaw() { public void handleRaw() {
int keyCode = Keyboard.getEventKey() == 0 ? Keyboard.getEventCharacter() + 256 : Keyboard.getEventKey(); //int keyCode = Keyboard.getEventKey() == 0 ? Keyboard.getEventCharacter() + 256 : Keyboard.getEventKey();
int keyCode = -1; // FIXME
for (final Runnable runnable : rawHandlers.get(keyCode)) { for (final Runnable runnable : rawHandlers.get(keyCode)) {
try { try {
runnable.run(); runnable.run();
} catch (Throwable cause) { } catch (Throwable cause) {
CrashReport crashReport = CrashReport.makeCrashReport(cause, "Handling Raw Key Binding"); CrashReport crashReport = CrashReport.makeCrashReport(cause, "Handling Raw Key Binding");
CrashReportCategory category = crashReport.makeCategory("Key Binding"); CrashReportCategory category = crashReport.makeCategory("Key Binding");
category.addCrashSection("Key Code", keyCode); MCVer.addDetail(category, "Key Code", () -> "" + keyCode);
MCVer.addDetail(category, "Handler", runnable::toString); MCVer.addDetail(category, "Handler", runnable::toString);
throw new ReportedException(crashReport); throw newReportedException(crashReport);
} }
} }
} }

View File

@@ -1,12 +1,13 @@
package com.replaymod.core; package com.replaymod.core;
import org.apache.logging.log4j.LogManager; //#if MC<11300
import org.spongepowered.asm.launch.MixinBootstrap; //$$ import org.apache.logging.log4j.LogManager;
import org.spongepowered.asm.mixin.Mixins; //$$ import org.spongepowered.asm.launch.MixinBootstrap;
//$$ import org.spongepowered.asm.mixin.Mixins;
//$$
//#if MC>=10800 //#if MC>=10800
import net.minecraftforge.fml.relauncher.CoreModManager; //$$ import net.minecraftforge.fml.relauncher.CoreModManager;
import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin; //$$ import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin;
//#else //#else
//$$ import com.replaymod.core.asm.GLErrorTransformer; //$$ import com.replaymod.core.asm.GLErrorTransformer;
//$$ import com.replaymod.core.asm.GLStateTrackerTransformer; //$$ import com.replaymod.core.asm.GLStateTrackerTransformer;
@@ -16,57 +17,57 @@ import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin;
//$$ import java.util.ArrayList; //$$ import java.util.ArrayList;
//$$ import java.util.List; //$$ import java.util.List;
//#endif //#endif
//$$
import java.io.File; //$$ import java.io.File;
import java.net.URISyntaxException; //$$ import java.net.URISyntaxException;
import java.net.URL; //$$ import java.net.URL;
import java.security.CodeSource; //$$ import java.security.CodeSource;
import java.util.Map; //$$ import java.util.Map;
//$$
@IFMLLoadingPlugin.TransformerExclusions("com.replaymod.core.asm.") //$$ @IFMLLoadingPlugin.TransformerExclusions("com.replaymod.core.asm.")
public class LoadingPlugin implements IFMLLoadingPlugin { //$$ public class LoadingPlugin implements IFMLLoadingPlugin {
//$$
public LoadingPlugin() { //$$ public LoadingPlugin() {
MixinBootstrap.init(); //$$ MixinBootstrap.init();
Mixins.addConfiguration("mixins.recording.replaymod.json"); //$$ Mixins.addConfiguration("mixins.recording.replaymod.json");
Mixins.addConfiguration("mixins.render.replaymod.json"); //$$ Mixins.addConfiguration("mixins.render.replaymod.json");
Mixins.addConfiguration("mixins.render.blend.replaymod.json"); //$$ Mixins.addConfiguration("mixins.render.blend.replaymod.json");
Mixins.addConfiguration("mixins.replay.replaymod.json"); //$$ Mixins.addConfiguration("mixins.replay.replaymod.json");
Mixins.addConfiguration("mixins.compat.mapwriter.replaymod.json"); //$$ Mixins.addConfiguration("mixins.compat.mapwriter.replaymod.json");
Mixins.addConfiguration("mixins.compat.shaders.replaymod.json"); //$$ Mixins.addConfiguration("mixins.compat.shaders.replaymod.json");
Mixins.addConfiguration("mixins.extras.playeroverview.replaymod.json"); //$$ Mixins.addConfiguration("mixins.extras.playeroverview.replaymod.json");
//$$
CodeSource codeSource = getClass().getProtectionDomain().getCodeSource(); //$$ CodeSource codeSource = getClass().getProtectionDomain().getCodeSource();
if (codeSource != null) { //$$ if (codeSource != null) {
URL location = codeSource.getLocation(); //$$ URL location = codeSource.getLocation();
try { //$$ try {
File file = new File(location.toURI()); //$$ File file = new File(location.toURI());
if (file.isFile()) { //$$ if (file.isFile()) {
// This forces forge to reexamine the jar file for FML mods //$$ // This forces forge to reexamine the jar file for FML mods
// Should eventually be handled by Mixin itself, maybe? //$$ // Should eventually be handled by Mixin itself, maybe?
//#if MC>=10809 //#if MC>=10809
CoreModManager.getIgnoredMods().remove(file.getName()); //$$ CoreModManager.getIgnoredMods().remove(file.getName());
//#else //#else
//$$ CoreModManager.getLoadedCoremods().remove(file.getName()); //$$ CoreModManager.getLoadedCoremods().remove(file.getName());
//#if MC<=10710 //#if MC<=10710
//$$ CoreModManager.getReparseableCoremods().add(file.getName()); //$$ CoreModManager.getReparseableCoremods().add(file.getName());
//#endif //#endif
//#endif //#endif
} //$$ }
} catch (URISyntaxException e) { //$$ } catch (URISyntaxException e) {
e.printStackTrace(); //$$ e.printStackTrace();
} //$$ }
} else { //$$ } else {
LogManager.getLogger().warn("No CodeSource, if this is not a development environment we might run into problems!"); //$$ LogManager.getLogger().warn("No CodeSource, if this is not a development environment we might run into problems!");
LogManager.getLogger().warn(getClass().getProtectionDomain()); //$$ LogManager.getLogger().warn(getClass().getProtectionDomain());
} //$$ }
} //$$ }
//$$
@Override //$$ @Override
public String[] getASMTransformerClass() { //$$ public String[] getASMTransformerClass() {
//#if MC>=10800 //#if MC>=10800
return new String[]{ //$$ return new String[]{
}; //$$ };
//#else //#else
//$$ List<String> transformers = new ArrayList<>(); //$$ List<String> transformers = new ArrayList<>();
//$$ if ("true".equals(System.getProperty("replaymod.glerrors", "false"))) { //$$ if ("true".equals(System.getProperty("replaymod.glerrors", "false"))) {
@@ -75,25 +76,26 @@ public class LoadingPlugin implements IFMLLoadingPlugin {
//$$ transformers.add(GLStateTrackerTransformer.class.getName()); //$$ transformers.add(GLStateTrackerTransformer.class.getName());
//$$ return transformers.stream().toArray(String[]::new); //$$ return transformers.stream().toArray(String[]::new);
//#endif //#endif
} //$$ }
//$$
@Override //$$ @Override
public String getModContainerClass() { //$$ public String getModContainerClass() {
return null; //$$ return null;
} //$$ }
//$$
@Override //$$ @Override
public String getSetupClass() { //$$ public String getSetupClass() {
return null; //$$ return null;
} //$$ }
//$$
@Override //$$ @Override
public void injectData(Map<String, Object> data) { //$$ public void injectData(Map<String, Object> data) {
} //$$ }
//$$
@Override //$$ @Override
public String getAccessTransformerClass() { //$$ public String getAccessTransformerClass() {
return null; //$$ return null;
} //$$ }
//$$
} //$$ }
//#endif

View File

@@ -0,0 +1,10 @@
package com.replaymod.core;
public interface Module {
// FMLCommonSetupEvent for 1.13+, FMLInitializationEvent below
default void initCommon() {}
// FMLClientSetupEvent for 1.13+, FMLInitializationEvent (if client) below
default void initClient() {}
// FMLClientSetupEvent for 1.13+, FMLInitializationEvent below
default void registerKeyBindings(KeyBindingRegistry registry) {}
}

View File

@@ -2,21 +2,42 @@ package com.replaymod.core;
import com.google.common.io.Files; import com.google.common.io.Files;
import com.google.common.util.concurrent.ListenableFutureTask; import com.google.common.util.concurrent.ListenableFutureTask;
import com.replaymod.core.gui.GuiBackgroundProcesses;
import com.replaymod.core.gui.GuiReplaySettings; import com.replaymod.core.gui.GuiReplaySettings;
import com.replaymod.core.gui.RestoreReplayGui; import com.replaymod.core.gui.RestoreReplayGui;
import com.replaymod.core.handler.MainMenuHandler; import com.replaymod.core.handler.MainMenuHandler;
import com.replaymod.core.utils.OpenGLUtils; import com.replaymod.core.versions.MCVer;
import com.replaymod.editor.ReplayModEditor;
import com.replaymod.extras.ReplayModExtras;
import com.replaymod.online.ReplayModOnline;
import com.replaymod.recording.ReplayModRecording;
import com.replaymod.render.ReplayModRender;
import com.replaymod.replay.ReplayModReplay;
import com.replaymod.replaystudio.util.I18n; import com.replaymod.replaystudio.util.I18n;
import com.replaymod.simplepathing.ReplayModSimplePathing;
import de.johni0702.minecraft.gui.container.GuiScreen; import de.johni0702.minecraft.gui.container.GuiScreen;
import lombok.Getter; import lombok.Getter;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.FolderResourcePack;
import net.minecraft.client.resources.IResourcePack;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraftforge.common.config.Configuration;
import org.apache.commons.io.Charsets; import org.apache.commons.io.Charsets;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
//#if MC>=11300
import com.replaymod.core.versions.LangResourcePack;
import net.minecraft.resources.FolderPack;
import net.minecraft.resources.IPackFinder;
import net.minecraft.resources.ResourcePackInfo;
import net.minecraftforge.fml.DeferredWorkQueue;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.versions.mcp.MCPVersion;
//#else
//$$ import net.minecraft.client.resources.FolderResourcePack;
//$$ import net.minecraft.client.resources.IResourcePack;
//$$ import net.minecraftforge.common.config.Configuration;
//#endif
//#if MC>=10904 //#if MC>=10904
import net.minecraft.util.text.*; import net.minecraft.util.text.*;
//#else //#else
@@ -28,18 +49,23 @@ import net.minecraft.util.text.*;
//#endif //#endif
//#if MC>=10800 //#if MC>=10800
import net.minecraft.client.settings.GameSettings; //#if MC>=11300
import net.minecraftforge.fml.client.FMLClientHandler; import net.minecraft.client.GameSettings;
import net.minecraftforge.fml.common.Loader; import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.ModContainer;
import net.minecraftforge.fml.ModList;
//#else
//$$ import net.minecraft.client.settings.GameSettings;
//$$ import net.minecraftforge.fml.client.FMLClientHandler;
//$$ import net.minecraftforge.fml.common.Loader;
//$$ import net.minecraftforge.fml.common.Mod.EventHandler;
//$$ import net.minecraftforge.fml.common.Mod.Instance;
//$$ import net.minecraftforge.fml.common.ModContainer;
//$$ import net.minecraftforge.fml.common.event.FMLInitializationEvent;
//$$ import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#endif
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.Mod.EventHandler;
import net.minecraftforge.fml.common.Mod.Instance;
import net.minecraftforge.fml.common.ModContainer;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPostInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.eventhandler.EventBus;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent; import net.minecraftforge.fml.common.gameevent.TickEvent;
//#else //#else
//$$ import cpw.mods.fml.common.Loader; //$$ import cpw.mods.fml.common.Loader;
@@ -48,9 +74,7 @@ import net.minecraftforge.fml.common.gameevent.TickEvent;
//$$ import cpw.mods.fml.common.Mod.Instance; //$$ import cpw.mods.fml.common.Mod.Instance;
//$$ import cpw.mods.fml.common.ModContainer; //$$ import cpw.mods.fml.common.ModContainer;
//$$ import cpw.mods.fml.common.event.FMLInitializationEvent; //$$ import cpw.mods.fml.common.event.FMLInitializationEvent;
//$$ import cpw.mods.fml.common.event.FMLPostInitializationEvent;
//$$ import cpw.mods.fml.common.event.FMLPreInitializationEvent; //$$ import cpw.mods.fml.common.event.FMLPreInitializationEvent;
//$$ import cpw.mods.fml.common.eventhandler.EventBus;
//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent; //$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
//$$ import cpw.mods.fml.common.gameevent.TickEvent; //$$ import cpw.mods.fml.common.gameevent.TickEvent;
//$$ import com.replaymod.replay.InputReplayTimer; //$$ import com.replaymod.replay.InputReplayTimer;
@@ -62,48 +86,108 @@ import java.io.ByteArrayInputStream;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Queue; import java.util.Queue;
import java.util.concurrent.FutureTask; import java.util.concurrent.FutureTask;
import static com.replaymod.core.versions.MCVer.*; import static com.replaymod.core.versions.MCVer.*;
@Mod(modid = ReplayMod.MOD_ID, //#if MC>=11300
useMetadata = true, @Mod(ReplayMod.MOD_ID)
version = "@MOD_VERSION@", //#else
acceptedMinecraftVersions = "@MC_VERSION@", //$$ @Mod(modid = ReplayMod.MOD_ID,
acceptableRemoteVersions = "*", //$$ useMetadata = true,
//$$ version = "@MOD_VERSION@",
//$$ acceptedMinecraftVersions = "@MC_VERSION@",
//$$ acceptableRemoteVersions = "*",
//#if MC>=10800 //#if MC>=10800
clientSideOnly = true, //$$ clientSideOnly = true,
updateJSON = "https://raw.githubusercontent.com/ReplayMod/ReplayMod/master/versions.json", //$$ updateJSON = "https://raw.githubusercontent.com/ReplayMod/ReplayMod/master/versions.json",
//#endif //#endif
guiFactory = "com.replaymod.core.gui.GuiFactory") //$$ guiFactory = "com.replaymod.core.gui.GuiFactory")
public class ReplayMod { //#endif
public class ReplayMod implements Module {
public static ModContainer getContainer() { public static ModContainer getContainer() {
return Loader.instance().getIndexedModList().get(MOD_ID); //#if MC>=11300
return ModList.get().getModContainerById(MOD_ID).get();
//#else
//$$ return Loader.instance().getIndexedModList().get(MOD_ID);
//#endif
} }
@Getter(lazy = true) @Getter(lazy = true)
private static final String minecraftVersion = Loader.MC_VERSION; //#if MC>=11300
private static final String minecraftVersion = MCPVersion.getMCVersion();
//#else
//$$ private static final String minecraftVersion = Loader.MC_VERSION;
//#endif
public static final String MOD_ID = "replaymod"; public static final String MOD_ID = "replaymod";
public static final ResourceLocation TEXTURE = new ResourceLocation("replaymod", "replay_gui.png"); public static final ResourceLocation TEXTURE = new ResourceLocation("replaymod", "replay_gui.png");
public static final int TEXTURE_SIZE = 256; public static final int TEXTURE_SIZE = 256;
private static final Minecraft mc = Minecraft.getMinecraft(); private static final Minecraft mc = MCVer.getMinecraft();
@Deprecated //#if MC<11300
public static Configuration config; //$$ @Deprecated
//$$ public static Configuration config;
//#endif
private final KeyBindingRegistry keyBindingRegistry = new KeyBindingRegistry(); private final KeyBindingRegistry keyBindingRegistry = new KeyBindingRegistry();
private final SettingsRegistry settingsRegistry = new SettingsRegistry(); private final SettingsRegistry settingsRegistry = new SettingsRegistry();
{
settingsRegistry.register(Setting.class);
}
// The instance of your mod that Forge uses. // The instance of your mod that Forge uses.
@Instance(MOD_ID) //#if MC>=11300
{ instance = this; }
//#else
//$$ @Instance(MOD_ID)
//#endif
public static ReplayMod instance; public static ReplayMod instance;
private final List<Module> modules = new ArrayList<>();
private final GuiBackgroundProcesses backgroundProcesses = new GuiBackgroundProcesses();
public ReplayMod() {
I18n.setI18n(net.minecraft.client.resources.I18n::format);
//#if MC>=11300
DeferredWorkQueue.runLater(() -> MCVer.getMinecraft().resourcePackRepository.addPackFinder(new LangResourcePack.Finder()));
//#endif
// Register all RM modules
modules.add(this);
modules.add(new ReplayModRecording(this));
ReplayModReplay replayModule = new ReplayModReplay(this);
modules.add(replayModule);
modules.add(new ReplayModOnline(this, replayModule));
modules.add(new ReplayModRender(this));
modules.add(new ReplayModSimplePathing(this));
modules.add(new ReplayModEditor(this));
modules.add(new ReplayModExtras(this));
//#if MC>=11300
settingsRegistry.register();
//#endif
}
//#if MC<=11300
//$$ @EventHandler
//$$ public void init(FMLPreInitializationEvent event) {
//$$ config = new Configuration(event.getSuggestedConfigurationFile());
//$$ config.load();
//$$ settingsRegistry.setConfiguration(config);
//$$ settingsRegistry.save(); // Save default values to disk
//$$ }
//#endif
public KeyBindingRegistry getKeyBindingRegistry() { public KeyBindingRegistry getKeyBindingRegistry() {
return keyBindingRegistry; return keyBindingRegistry;
} }
@@ -114,33 +198,28 @@ public class ReplayMod {
public File getReplayFolder() throws IOException { public File getReplayFolder() throws IOException {
String path = getSettingsRegistry().get(Setting.RECORDING_PATH); String path = getSettingsRegistry().get(Setting.RECORDING_PATH);
File folder = new File(path.startsWith("./") ? getMinecraft().mcDataDir : null, path); File folder = new File(path.startsWith("./") ? mcDataDir(getMinecraft()) : null, path);
FileUtils.forceMkdir(folder); FileUtils.forceMkdir(folder);
return folder; return folder;
} }
@EventHandler
public void preInit(FMLPreInitializationEvent event) {
// Initialize the static OpenGL info field from the minecraft main thread
// Unfortunately lwjgl uses static methods so we have to make use of magic init calls as well
OpenGLUtils.init();
I18n.setI18n(net.minecraft.client.resources.I18n::format);
config = new Configuration(event.getSuggestedConfigurationFile());
config.load();
settingsRegistry.setConfiguration(config);
}
//#ifdef DEV_ENV //#ifdef DEV_ENV
static { // Note: even preInit is too late and we'd have to issue another resource reload static { // Note: even preInit is too late and we'd have to issue another resource reload
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
List<IResourcePack> defaultResourcePacks = mc.defaultResourcePacks; //#if MC>=11300
FolderResourcePack jGuiResourcePack = new FolderResourcePack(new File("../jGui/src/main/resources")) { FolderPack jGuiResourcePack = new FolderPack(new File("../jGui/src/main/resources")) {
@Override @Override
protected InputStream getInputStreamByName(String resourceName) throws IOException { protected InputStream getInputStream(String resourceName) throws IOException {
try { try {
return super.getInputStreamByName(resourceName); return super.getInputStream(resourceName);
//#else
//$$ List<IResourcePack> defaultResourcePacks = mc.defaultResourcePacks;
//$$ FolderResourcePack jGuiResourcePack = new FolderResourcePack(new File("../jGui/src/main/resources")) {
//$$ @Override
//$$ protected InputStream getInputStreamByName(String resourceName) throws IOException {
//$$ try {
//$$ return super.getInputStreamByName(resourceName);
//#endif
} catch (IOException e) { } catch (IOException e) {
if ("pack.mcmeta".equals(resourceName)) { if ("pack.mcmeta".equals(resourceName)) {
return new ByteArrayInputStream(("{\"pack\": {\"description\": \"dummy pack for jGui resources in dev-env\", \"pack_format\": 1}}").getBytes(Charsets.UTF_8)); return new ByteArrayInputStream(("{\"pack\": {\"description\": \"dummy pack for jGui resources in dev-env\", \"pack_format\": 1}}").getBytes(Charsets.UTF_8));
@@ -149,7 +228,16 @@ public class ReplayMod {
} }
} }
}; };
defaultResourcePacks.add(jGuiResourcePack); //#if MC>=11300
mc.resourcePackRepository.addPackFinder(new IPackFinder() {
@Override
public <T extends ResourcePackInfo> void addPackInfosToMap(Map<String, T> map, ResourcePackInfo.IFactory<T> factory) {
map.put("jgui", ResourcePackInfo.func_195793_a("jgui", true, () -> jGuiResourcePack, factory, ResourcePackInfo.Priority.BOTTOM));
}
});
//#else
//$$ defaultResourcePacks.add(jGuiResourcePack);
//#endif
//#if MC<=10710 //#if MC<=10710
//$$ FolderResourcePack mainResourcePack = new FolderResourcePack(new File("../src/main/resources")) { //$$ FolderResourcePack mainResourcePack = new FolderResourcePack(new File("../src/main/resources")) {
//$$ @Override //$$ @Override
@@ -169,30 +257,43 @@ public class ReplayMod {
} }
//#endif //#endif
@EventHandler //#if MC>=11300
public void init(FMLInitializationEvent event) { {
getSettingsRegistry().register(Setting.class); FMLJavaModLoadingContext.get().getModEventBus().addListener((FMLCommonSetupEvent event) -> modules.forEach(Module::initCommon));
FMLJavaModLoadingContext.get().getModEventBus().addListener((FMLClientSetupEvent event) -> modules.forEach(Module::initClient));
FMLJavaModLoadingContext.get().getModEventBus().addListener((FMLClientSetupEvent event) -> modules.forEach(m -> m.registerKeyBindings(keyBindingRegistry)));
}
//#else
//$$ @EventHandler
//$$ public void init(FMLInitializationEvent event) {
//$$ modules.forEach(Module::initCommon);
//$$ modules.forEach(Module::initClient);
//$$ modules.forEach(m -> m.registerKeyBindings(keyBindingRegistry));
//$$ }
//#endif
@Override
public void registerKeyBindings(KeyBindingRegistry registry) {
registry.registerKeyBinding("replaymod.input.settings", 0, () -> {
new GuiReplaySettings(null, settingsRegistry).display();
});
}
@Override
public void initClient() {
new MainMenuHandler().register(); new MainMenuHandler().register();
//#if MC<=10710 //#if MC<=10710
//$$ FML_BUS.register(this); // For runLater(Runnable) //$$ FML_BUS.register(this); // For runLater(Runnable)
//#endif //#endif
FML_BUS.register(keyBindingRegistry); FML_BUS.register(keyBindingRegistry);
FORGE_BUS.register(backgroundProcesses);
getKeyBindingRegistry().registerKeyBinding("replaymod.input.settings", 0, () -> {
new GuiReplaySettings(null, settingsRegistry).display();
});
}
@EventHandler
public void postInit(FMLPostInitializationEvent event) throws IOException {
settingsRegistry.save(); // Save default values to disk
// 1.7.10 crashes when render distance > 16 // 1.7.10 crashes when render distance > 16
//#if MC>=10800 //#if MC>=10800
if(!FMLClientHandler.instance().hasOptifine()) //if(!FMLClientHandler.instance().hasOptifine()) FIXME 1.13 update
GameSettings.Options.RENDER_DISTANCE.setValueMax(64f); GameSettings.Options.RENDER_DISTANCE.setValueMax(64f);
//#endif //#endif
testIfMoeshAndExitMinecraft(); testIfMoeshAndExitMinecraft();
@@ -240,19 +341,18 @@ public class ReplayMod {
public void runLater(Runnable runnable) { public void runLater(Runnable runnable) {
if (mc.isCallingFromMinecraftThread() && inRunLater) { if (mc.isCallingFromMinecraftThread() && inRunLater) {
EventBus bus = FORGE_BUS;
//#if MC>=10800 //#if MC>=10800
bus.register(new Object() { FORGE_BUS.register(new Object() {
@SubscribeEvent @SubscribeEvent
public void onRenderTick(TickEvent.RenderTickEvent event) { public void onRenderTick(TickEvent.RenderTickEvent event) {
if (event.phase == TickEvent.Phase.START) { if (event.phase == TickEvent.Phase.START) {
runLater(runnable); runLater(runnable);
bus.unregister(this); FORGE_BUS.unregister(this);
} }
} }
}); });
//#else //#else
//$$ bus.register(new RunLaterHelper(runnable)); //$$ FORGE_BUS.register(new RunLaterHelper(runnable));
//#endif //#endif
return; return;
} }
@@ -309,7 +409,11 @@ public class ReplayMod {
//#endif //#endif
public String getVersion() { public String getVersion() {
return getContainer().getVersion(); //#if MC>=11300
return getContainer().getModInfo().getVersion().toString();
//#else
//$$ return getContainer().getVersion();
//#endif
} }
private void testIfMoeshAndExitMinecraft() { private void testIfMoeshAndExitMinecraft() {
@@ -355,4 +459,8 @@ public class ReplayMod {
mc.ingameGUI.getChatGUI().printChatMessage(text); mc.ingameGUI.getChatGUI().printChatMessage(text);
} }
} }
public GuiBackgroundProcesses getBackgroundProcesses() {
return backgroundProcesses;
}
} }

View File

@@ -1,30 +1,67 @@
package com.replaymod.core; package com.replaymod.core;
import com.replaymod.core.events.SettingsChangedEvent; import com.replaymod.core.events.SettingsChangedEvent;
import net.minecraft.client.resources.I18n;
import net.minecraftforge.common.config.Configuration;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
//#if MC>=11300
import net.minecraftforge.common.ForgeConfigSpec;
import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.config.ModConfig;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
//#else
//$$ import net.minecraftforge.common.config.Configuration;
//#endif
import static com.replaymod.core.versions.MCVer.*; import static com.replaymod.core.versions.MCVer.*;
public class SettingsRegistry { public class SettingsRegistry {
private static final Object NULL_OBJECT = new Object(); private static final Object NULL_OBJECT = new Object();
private Map<SettingKey<?>, Object> settings = new ConcurrentHashMap<>(); private Map<SettingKey<?>, Object> settings = new ConcurrentHashMap<>();
private Configuration configuration; //#if MC>=11300
private ForgeConfigSpec spec;
private ModConfig config;
//#else
//$$ private Configuration configuration;
//#endif
public void setConfiguration(Configuration configuration) { //#if MC>=11300
this.configuration = configuration; public void register() {
if (spec == null) {
ForgeConfigSpec.Builder builder = new ForgeConfigSpec.Builder();
for (SettingKey<?> key : settings.keySet()) {
builder
.translation(key.getDisplayString())
.define(key.getCategory() + "." + key.getKey(), key.getDefault());
}
spec = builder.build();
}
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::load);
ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, spec);
}
List<SettingKey<?>> keys = new ArrayList<>(settings.keySet()); private void load(ModConfig.Loading event) {
settings.clear(); config = event.getConfig();
for (SettingKey key : keys) { for (Map.Entry<SettingKey<?>, Object> entry : settings.entrySet()) {
register(key); SettingKey<?> key = entry.getKey();
Object value = config.getConfigData().get(key.getCategory() + "." + key.getKey());
entry.setValue(value == null ? key.getDefault() : value);
} }
} }
//#else
//$$ public void setConfiguration(Configuration configuration) {
//$$ this.configuration = configuration;
//$$
//$$ List<SettingKey<?>> keys = new ArrayList<>(settings.keySet());
//$$ settings.clear();
//$$ for (SettingKey key : keys) {
//$$ register(key);
//$$ }
//$$ }
//#endif
public void register(Class<?> settingsClass) { public void register(Class<?> settingsClass) {
for (Field field : settingsClass.getDeclaredFields()) { for (Field field : settingsClass.getDeclaredFields()) {
@@ -40,23 +77,30 @@ public class SettingsRegistry {
} }
public void register(SettingKey<?> key) { public void register(SettingKey<?> key) {
Object value; //#if MC>=11300
if (configuration != null) { if (spec != null) {
if (key.getDefault() instanceof Boolean) { throw new IllegalStateException("Cannot register more settings are spec has been built.");
value = configuration.get(key.getCategory(), key.getKey(), (Boolean) key.getDefault()).getBoolean();
} else if (key.getDefault() instanceof Integer) {
value = configuration.get(key.getCategory(), key.getKey(), (Integer) key.getDefault()).getInt();
} else if (key.getDefault() instanceof Double) {
value = configuration.get(key.getCategory(), key.getKey(), (Double) key.getDefault()).getDouble();
} else if (key.getDefault() instanceof String) {
value = configuration.get(key.getCategory(), key.getKey(), (String) key.getDefault()).getString();
} else {
throw new IllegalArgumentException("Default type " + key.getDefault().getClass() + " not supported.");
}
} else {
value = NULL_OBJECT;
} }
settings.put(key, value); settings.put(key, NULL_OBJECT);
//#else
//$$ Object value;
//$$ if (configuration != null) {
//$$ if (key.getDefault() instanceof Boolean) {
//$$ value = configuration.get(key.getCategory(), key.getKey(), (Boolean) key.getDefault()).getBoolean();
//$$ } else if (key.getDefault() instanceof Integer) {
//$$ value = configuration.get(key.getCategory(), key.getKey(), (Integer) key.getDefault()).getInt();
//$$ } else if (key.getDefault() instanceof Double) {
//$$ value = configuration.get(key.getCategory(), key.getKey(), (Double) key.getDefault()).getDouble();
//$$ } else if (key.getDefault() instanceof String) {
//$$ value = configuration.get(key.getCategory(), key.getKey(), (String) key.getDefault()).getString();
//$$ } else {
//$$ throw new IllegalArgumentException("Default type " + key.getDefault().getClass() + " not supported.");
//$$ }
//$$ } else {
//$$ value = NULL_OBJECT;
//$$ }
//$$ settings.put(key, value);
//#endif
} }
public Set<SettingKey<?>> getSettings() { public Set<SettingKey<?>> getSettings() {
@@ -72,23 +116,35 @@ public class SettingsRegistry {
} }
public <T> void set(SettingKey<T> key, T value) { public <T> void set(SettingKey<T> key, T value) {
if (key.getDefault() instanceof Boolean) { //#if MC>=11300
configuration.get(key.getCategory(), key.getKey(), (Boolean) key.getDefault()).set((Boolean) value); if (config != null) {
} else if (key.getDefault() instanceof Integer) { config.getConfigData().set(key.getCategory() + "." + key.getKey(), value);
configuration.get(key.getCategory(), key.getKey(), (Integer) key.getDefault()).set((Integer) value);
} else if (key.getDefault() instanceof Double) {
configuration.get(key.getCategory(), key.getKey(), (Double) key.getDefault()).set((Double) value);
} else if (key.getDefault() instanceof String) {
configuration.get(key.getCategory(), key.getKey(), (String) key.getDefault()).set((String) value);
} else {
throw new IllegalArgumentException("Default type " + key.getDefault().getClass() + " not supported.");
} }
//#else
//$$ if (key.getDefault() instanceof Boolean) {
//$$ configuration.get(key.getCategory(), key.getKey(), (Boolean) key.getDefault()).set((Boolean) value);
//$$ } else if (key.getDefault() instanceof Integer) {
//$$ configuration.get(key.getCategory(), key.getKey(), (Integer) key.getDefault()).set((Integer) value);
//$$ } else if (key.getDefault() instanceof Double) {
//$$ configuration.get(key.getCategory(), key.getKey(), (Double) key.getDefault()).set((Double) value);
//$$ } else if (key.getDefault() instanceof String) {
//$$ configuration.get(key.getCategory(), key.getKey(), (String) key.getDefault()).set((String) value);
//$$ } else {
//$$ throw new IllegalArgumentException("Default type " + key.getDefault().getClass() + " not supported.");
//$$ }
//#endif
settings.put(key, value); settings.put(key, value);
FML_BUS.post(new SettingsChangedEvent(this, key)); FML_BUS.post(new SettingsChangedEvent(this, key));
} }
public void save() { public void save() {
configuration.save(); //#if MC>=11300
if (config != null) {
config.save();
}
//#else
//$$ configuration.save();
//#endif
} }
public interface SettingKey<T> { public interface SettingKey<T> {
@@ -127,7 +183,7 @@ public class SettingsRegistry {
@Override @Override
public String getDisplayString() { public String getDisplayString() {
return displayString == null ? null : I18n.format(displayString); return displayString;
} }
@Override @Override

View File

@@ -5,7 +5,11 @@ import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
//#if MC>=10800 //#if MC>=10800
import net.minecraftforge.fml.common.eventhandler.Event; //#if MC>=11300
import net.minecraftforge.eventbus.api.Event;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.Event;
//#endif
//#else //#else
//$$ import cpw.mods.fml.common.eventhandler.Event; //$$ import cpw.mods.fml.common.eventhandler.Event;
//#endif //#endif

View File

@@ -0,0 +1,135 @@
package com.replaymod.core.gui;
import de.johni0702.minecraft.gui.GuiRenderer;
import de.johni0702.minecraft.gui.RenderInfo;
import de.johni0702.minecraft.gui.container.AbstractGuiContainer;
import de.johni0702.minecraft.gui.container.GuiPanel;
import de.johni0702.minecraft.gui.container.GuiScreen;
import de.johni0702.minecraft.gui.container.VanillaGuiScreen;
import de.johni0702.minecraft.gui.element.GuiElement;
import de.johni0702.minecraft.gui.layout.CustomLayout;
import de.johni0702.minecraft.gui.layout.VerticalLayout;
import de.johni0702.minecraft.gui.utils.lwjgl.Dimension;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import net.minecraftforge.client.event.GuiScreenEvent;
import net.minecraftforge.common.MinecraftForge;
//#if MC>=10800
//#if MC>=11300
import net.minecraftforge.eventbus.api.SubscribeEvent;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#endif
//#else
//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
//#endif
import static com.replaymod.core.versions.MCVer.getGui;
import static com.replaymod.core.versions.MCVer.getMinecraft;
public class GuiBackgroundProcesses {
private GuiPanel panel = new GuiPanel().setLayout(new VerticalLayout().setSpacing(10));
public void register() {
MinecraftForge.EVENT_BUS.register(this);
}
public void unregister() {
MinecraftForge.EVENT_BUS.unregister(this);
}
@SubscribeEvent
public void onGuiInit(GuiScreenEvent.InitGuiEvent.Post event) {
if (getGui(event) != getMinecraft().currentScreen) return; // people tend to construct GuiScreens without opening them
VanillaGuiScreen.setup(getGui(event)).setLayout(new CustomLayout<GuiScreen>() {
@Override
protected void layout(GuiScreen container, int width, int height) {
pos(panel, width - 5 - width(panel), 5);
}
}).addElements(null, panel);
}
public void addProcess(GuiElement<?> element) {
panel.addElements(new VerticalLayout.Data(1.0),
new Element(element));
}
public void removeProcess(GuiElement<?> element) {
for (GuiElement child : panel.getChildren()) {
if (((Element) child).inner == element) {
panel.removeElement(child);
return;
}
}
}
private static class Element extends AbstractGuiContainer<Element> {
private GuiElement inner;
Element(GuiElement inner) {
this.inner = inner;
addElements(null, inner);
setLayout(new CustomLayout<Element>() {
@Override
protected void layout(Element container, int width, int height) {
pos(inner, 10, 10);
size(inner, width - 20, height - 20);
}
});
}
@Override
public ReadableDimension getMinSize() {
ReadableDimension minSize = inner.getMinSize();
return new Dimension(minSize.getWidth() + 20, minSize.getHeight() + 20);
}
@Override
public void draw(GuiRenderer renderer, ReadableDimension size, RenderInfo renderInfo) {
final int u0 = 0;
final int v0 = 39;
renderer.bindTexture(TEXTURE);
int w = size.getWidth();
int h = size.getHeight();
// Corners
renderer.drawTexturedRect(0, 0, u0, v0, 5, 5); // Top left
renderer.drawTexturedRect(w - 5, 0, u0 + 12, v0, 5, 5); // Top right
renderer.drawTexturedRect(0, h - 5, u0, v0 + 12, 5, 5); // Bottom left
renderer.drawTexturedRect(w - 5, h - 5, u0 + 12, v0 + 12, 5, 5); // Bottom right
// Top and bottom edge
for (int x = 5; x < w - 5; x += 5) {
int rx = Math.min(5, w - 5 - x);
renderer.drawTexturedRect(x, 0, u0 + 6, v0, rx, 5); // Top
renderer.drawTexturedRect(x, h - 5, u0 + 6, v0 + 12, rx, 5); // Bottom
}
// Left and right edge
for (int y = 5; y < h - 5; y += 5) {
int ry = Math.min(5, h - 5 - y);
renderer.drawTexturedRect(0, y, u0, v0 + 6, 5, ry); // Left
renderer.drawTexturedRect(w - 5, y, u0 + 12, v0 + 6, 5, ry); // Right
}
// Center
for (int x = 5; x < w - 5; x += 5) {
for (int y = 5; y < h - 5; y += 5) {
int rx = Math.min(5, w - 5 - x);
int ry = Math.min(5, h - 5 - y);
renderer.drawTexturedRect(x, y, u0 + 6, v0 + 6, rx, ry);
}
}
super.draw(renderer, size, renderInfo);
}
@Override
protected Element getThis() {
return this;
}
}
}

View File

@@ -41,7 +41,7 @@ public class GuiReplaySettings extends AbstractGuiScreen<GuiReplaySettings> {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final SettingsRegistry.SettingKey<Boolean> booleanKey = (SettingsRegistry.SettingKey<Boolean>) key; final SettingsRegistry.SettingKey<Boolean> booleanKey = (SettingsRegistry.SettingKey<Boolean>) key;
final GuiToggleButton button = new GuiToggleButton<>().setSize(150, 20) final GuiToggleButton button = new GuiToggleButton<>().setSize(150, 20)
.setLabel(key.getDisplayString()).setSelected(settingsRegistry.get(booleanKey) ? 0 : 1) .setI18nLabel(key.getDisplayString()).setSelected(settingsRegistry.get(booleanKey) ? 0 : 1)
.setValues(I18n.format("options.on"), I18n.format("options.off")); .setValues(I18n.format("options.on"), I18n.format("options.off"));
element = button.onClick(new Runnable() { element = button.onClick(new Runnable() {
@Override @Override
@@ -60,7 +60,7 @@ public class GuiReplaySettings extends AbstractGuiScreen<GuiReplaySettings> {
for (int j = 0; j < entries.length; j++) { for (int j = 0; j < entries.length; j++) {
Object value = values.get(j); Object value = values.get(j);
entries[j] = new MultipleChoiceDropdownEntry(value, entries[j] = new MultipleChoiceDropdownEntry(value,
multipleChoiceKey.getDisplayString() + ": " + I18n.format(value.toString())); I18n.format(multipleChoiceKey.getDisplayString()) + ": " + I18n.format(value.toString()));
if (currentValue.equals(value)) { if (currentValue.equals(value)) {
selected = j; selected = j;
} }

View File

@@ -43,6 +43,9 @@ public class RestoreReplayGui extends AbstractGuiScreen<RestoreReplayGui> {
yesButton.onClick(() -> { yesButton.onClick(() -> {
try { try {
ReplayFile replayFile = new ZipReplayFile(new ReplayStudio(), null, file); ReplayFile replayFile = new ZipReplayFile(new ReplayStudio(), null, file);
// Commit all not-yet-committed files into the main zip file.
// If we don't do this, then re-writing packet data below can actually overwrite uncommitted packet data!
replayFile.save();
ReplayMetaData metaData = replayFile.getMetaData(); ReplayMetaData metaData = replayFile.getMetaData();
if (metaData != null && metaData.getDuration() == 0) { if (metaData != null && metaData.getDuration() == 0) {
// Try to restore replay duration // Try to restore replay duration

View File

@@ -1,5 +1,6 @@
package com.replaymod.core.handler; package com.replaymod.core.handler;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiButton; import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiMainMenu; import net.minecraft.client.gui.GuiMainMenu;
import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.gui.GuiScreen;
@@ -9,13 +10,15 @@ import net.minecraftforge.common.MinecraftForge;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
//#if MC>=10800 //#if MC>=10800
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; //#if MC>=11300
import net.minecraftforge.eventbus.api.SubscribeEvent;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#endif
//#else //#else
//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent; //$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
//#endif //#endif
import java.io.IOException;
import static com.replaymod.core.versions.MCVer.*; import static com.replaymod.core.versions.MCVer.*;
/** /**
@@ -37,17 +40,18 @@ public class MainMenuHandler {
if (x(button) + button.width < gui.width / 2 - 100 if (x(button) + button.width < gui.width / 2 - 100
|| x(button) > gui.width / 2 + 100 || x(button) > gui.width / 2 + 100
|| y(button) > gui.height / 4 + 10 + 4 * 24) continue; || y(button) > gui.height / 4 + 10 + 4 * 24) continue;
// Move button up to make space for two rows of buttons // Move button up to make space for one rows of buttons
// and then move back down by 10 to compensate for the space to the exit button that was already there // and then move back down by 10 to compensate for the space to the exit button that was already there
int offset = -2 * 24 + 10; int offset = -1 * 24 + 10;
y(button, y(button) + offset); y(button, y(button) + offset);
//#if MC>=11202
if (button == gui.realmsButton) { //#if MC>=11300
if (button.id == 14) {
realmsOffset = offset; realmsOffset = offset;
} }
//#endif //#endif
} }
//#if MC>=11202 //#if MC>=11300
if (realmsOffset != 0 && gui.realmsNotification instanceof GuiScreenRealmsProxy) { if (realmsOffset != 0 && gui.realmsNotification instanceof GuiScreenRealmsProxy) {
gui.realmsNotification = new RealmsNotificationProxy((GuiScreenRealmsProxy) gui.realmsNotification, realmsOffset); gui.realmsNotification = new RealmsNotificationProxy((GuiScreenRealmsProxy) gui.realmsNotification, realmsOffset);
} }
@@ -55,7 +59,7 @@ public class MainMenuHandler {
} }
} }
//#if MC>=11202 //#if MC>=11300
private static class RealmsNotificationProxy extends GuiScreen { private static class RealmsNotificationProxy extends GuiScreen {
private final GuiScreenRealmsProxy proxy; private final GuiScreenRealmsProxy proxy;
private final int offset; private final int offset;
@@ -66,30 +70,25 @@ public class MainMenuHandler {
} }
@Override @Override
public void setGuiSize(int w, int h) { public void setWorldAndResolution(Minecraft mc, int width, int height) {
proxy.setGuiSize(w, h); proxy.setWorldAndResolution(mc, width, height);
} }
@Override @Override
public void initGui() { public void tick() {
proxy.initGui(); proxy.tick();
} }
@Override @Override
public void updateScreen() { public void render(int mouseX, int mouseY, float partialTicks) {
proxy.updateScreen();
}
@Override
public void drawScreen(int mouseX, int mouseY, float partialTicks) {
GL11.glTranslated(0, offset, 0); GL11.glTranslated(0, offset, 0);
proxy.drawScreen(mouseX, mouseY - offset, partialTicks); proxy.render(mouseX, mouseY - offset, partialTicks);
GL11.glTranslated(0, -offset, 0); GL11.glTranslated(0, -offset, 0);
} }
@Override @Override
public void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException { public boolean mouseClicked(double mouseX, double mouseY, int mouseButton) {
proxy.mouseClicked(mouseX, mouseY - offset, mouseButton); return proxy.mouseClicked(mouseX, mouseY - offset, mouseButton);
} }
@Override @Override

View File

@@ -11,11 +11,16 @@ import net.minecraftforge.registries.RegistryManager;
//#else //#else
//$$ import cpw.mods.fml.common.registry.GameData; //$$ import cpw.mods.fml.common.registry.GameData;
//#endif //#endif
//$$ import java.util.stream.Stream;
//#endif //#endif
//#if MC>=10800 //#if MC>=10800
import net.minecraftforge.fml.common.Loader; //#if MC>=11300
import net.minecraftforge.fml.common.ModContainer; import net.minecraftforge.fml.ModList;
//#else
//$$ import net.minecraftforge.fml.common.Loader;
//$$ import net.minecraftforge.fml.common.ModContainer;
//#endif
//#else //#else
//$$ import cpw.mods.fml.common.Loader; //$$ import cpw.mods.fml.common.Loader;
//$$ import cpw.mods.fml.common.ModContainer; //$$ import cpw.mods.fml.common.ModContainer;
@@ -24,22 +29,32 @@ import net.minecraftforge.fml.common.ModContainer;
import java.util.*; import java.util.*;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
public class ModCompat { public class ModCompat {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static Collection<ModInfo> getInstalledNetworkMods() { public static Collection<ModInfo> getInstalledNetworkMods() {
Map<String, ModContainer> ignoreCaseMap = Loader.instance().getModList().stream() //#if MC>=11300
.collect(Collectors.toMap(m -> m.getModId().toLowerCase(), Function.identity())); Map<String, ModInfo> modInfoMap = ModList.get().getMods().stream()
//#if MC>=11200 .map(m -> new ModInfo(m.getModId(), m.getDisplayName(), m.getVersion().toString()))
.collect(Collectors.toMap(ModInfo::getId, Function.identity()));
return RegistryManager.ACTIVE.takeSnapshot(false).keySet().stream() return RegistryManager.ACTIVE.takeSnapshot(false).keySet().stream()
.map(RegistryManager.ACTIVE::getRegistry) .map(RegistryManager.ACTIVE::getRegistry)
.map(ForgeRegistry::getKeys).flatMap(Set::stream) .map(ForgeRegistry::getKeys).flatMap(Set::stream)
.map(ResourceLocation::getResourceDomain).filter(s -> !s.equals("minecraft")).distinct() .map(ResourceLocation::getNamespace).filter(s -> !s.equals("minecraft")).distinct()
.map(String::toLowerCase).map(ignoreCaseMap::get).filter(mod -> mod != null) .map(modInfoMap::get).filter(Objects::nonNull)
.map(mod -> new ModInfo(mod.getModId(), mod.getName(), mod.getVersion()))
.collect(Collectors.toList()); .collect(Collectors.toList());
//#else //#else
//$$ Map<String, ModContainer> ignoreCaseMap = Loader.instance().getModList().stream()
//$$ .collect(Collectors.toMap(m -> m.getModId().toLowerCase(), Function.identity()));
//#if MC>=11200
//$$ return RegistryManager.ACTIVE.takeSnapshot(false).keySet().stream()
//$$ .map(RegistryManager.ACTIVE::getRegistry)
//$$ .map(ForgeRegistry::getKeys).flatMap(Set::stream)
//$$ .map(ResourceLocation::getResourceDomain).filter(s -> !s.equals("minecraft")).distinct()
//$$ .map(String::toLowerCase).map(ignoreCaseMap::get).filter(mod -> mod != null)
//$$ .map(mod -> new ModInfo(mod.getModId(), mod.getName(), mod.getVersion()))
//$$ .collect(Collectors.toList());
//#else
//#if MC>=10800 //#if MC>=10800
//$$ return Stream.concat( //$$ return Stream.concat(
//$$ ((Set<ResourceLocation>) GameData.getBlockRegistry().getKeys()).stream(), //$$ ((Set<ResourceLocation>) GameData.getBlockRegistry().getKeys()).stream(),
@@ -61,6 +76,7 @@ public class ModCompat {
//$$ .collect(Collectors.toList()); //$$ .collect(Collectors.toList());
//#endif //#endif
//#endif //#endif
//#endif
} }
public static final class ModInfoDifference { public static final class ModInfoDifference {

View File

@@ -1,26 +0,0 @@
package com.replaymod.core.utils;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import java.nio.IntBuffer;
public class OpenGLUtils {
public static final int VIEWPORT_MAX_WIDTH;
public static final int VIEWPORT_MAX_HEIGHT;
static {
IntBuffer buffer = BufferUtils.createIntBuffer(16);
GL11.glGetInteger(GL11.GL_MAX_VIEWPORT_DIMS, buffer);
VIEWPORT_MAX_WIDTH = buffer.get();
VIEWPORT_MAX_HEIGHT = buffer.get();
}
/**
* Magic init method which has to be called from the OpenGL thread so the variables in this class
* can be initialized successfully.
* Does not perform any work on its own.
*/
public static void init() {
}
}

View File

@@ -3,6 +3,7 @@ package com.replaymod.core.utils;
import net.minecraft.network.PacketBuffer; import net.minecraft.network.PacketBuffer;
//#if MC>=10904 //#if MC>=10904
import net.minecraft.network.play.server.SPacketCustomPayload; import net.minecraft.network.play.server.SPacketCustomPayload;
import net.minecraft.util.ResourceLocation;
//#else //#else
//$$ import net.minecraft.network.play.server.S3FPacketCustomPayload; //$$ import net.minecraft.network.play.server.S3FPacketCustomPayload;
//#endif //#endif
@@ -18,7 +19,11 @@ import static com.replaymod.core.versions.MCVer.readString;
* @see <a href="https://gist.github.com/Johni0702/2547c463e51f65f312cb">Replay Restrictions Gist</a> * @see <a href="https://gist.github.com/Johni0702/2547c463e51f65f312cb">Replay Restrictions Gist</a>
*/ */
public class Restrictions { public class Restrictions {
public static final String PLUGIN_CHANNEL = "Replay|Restrict"; //#if MC>=11300
public static final ResourceLocation PLUGIN_CHANNEL = new ResourceLocation("replaymod", "restrict");
//#else
//$$ public static final String PLUGIN_CHANNEL = "Replay|Restrict";
//#endif
private boolean noXray; private boolean noXray;
private boolean noNoclip; private boolean noNoclip;
private boolean onlyFirstPerson; private boolean onlyFirstPerson;

View File

@@ -18,15 +18,20 @@ import de.johni0702.minecraft.gui.layout.HorizontalLayout;
import de.johni0702.minecraft.gui.layout.VerticalLayout; import de.johni0702.minecraft.gui.layout.VerticalLayout;
import de.johni0702.minecraft.gui.popup.GuiInfoPopup; import de.johni0702.minecraft.gui.popup.GuiInfoPopup;
import de.johni0702.minecraft.gui.utils.Colors; import de.johni0702.minecraft.gui.utils.Colors;
import de.johni0702.minecraft.gui.utils.lwjgl.Dimension;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import de.johni0702.minecraft.gui.versions.MCVer;
import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.gui.GuiScreen;
import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReport;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import org.apache.commons.io.Charsets; import org.apache.commons.io.Charsets;
import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.FilenameUtils;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.lwjgl.input.Keyboard;
import org.lwjgl.util.Dimension; //#if MC>=11300
import org.lwjgl.util.ReadableDimension; //#else
//$$ import org.lwjgl.input.Keyboard;
//#endif
//#if MC>=10800 //#if MC>=10800
import net.minecraft.client.network.NetworkPlayerInfo; import net.minecraft.client.network.NetworkPlayerInfo;
@@ -64,16 +69,30 @@ import java.util.Date;
import java.util.UUID; import java.util.UUID;
import java.util.function.Consumer; import java.util.function.Consumer;
import static net.minecraft.client.Minecraft.getMinecraft; import static com.replaymod.core.versions.MCVer.Minecraft_mcDataDir;
import static com.replaymod.core.versions.MCVer.getMinecraft;
public class Utils { public class Utils {
private static InputStream getResourceAsStream(String path) {
// FIXME this seems broken in 1.13, hence the workaround. probably want to open an issue with modlauncher (or forge?)
//#ifdef DEV_ENV
try {
return new java.io.FileInputStream(new File("../src/main/resources" + path));
} catch (java.io.FileNotFoundException e) {
throw new RuntimeException(e);
}
//#else
//$$ return Utils.class.getResourceAsStream(path);
//#endif
}
public static final BufferedImage DEFAULT_THUMBNAIL; public static final BufferedImage DEFAULT_THUMBNAIL;
static { static {
BufferedImage thumbnail; BufferedImage thumbnail;
try { try {
thumbnail = ImageIO.read(Utils.class.getClassLoader().getResourceAsStream("default_thumb.jpg")); thumbnail = ImageIO.read(getResourceAsStream("/default_thumb.jpg"));
} catch (Exception e) { } catch (Exception e) {
thumbnail = new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR); thumbnail = new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR);
e.printStackTrace(); e.printStackTrace();
@@ -98,7 +117,7 @@ public class Utils {
static { static {
// Largely from https://community.letsencrypt.org/t/134/37 // Largely from https://community.letsencrypt.org/t/134/37
try (InputStream in = Utils.class.getResourceAsStream("/dst_root_ca_x3.pem")){ try (InputStream in = getResourceAsStream("/dst_root_ca_x3.pem")){
Certificate certificate = CertificateFactory.getInstance("X.509").generateCertificate(in); Certificate certificate = CertificateFactory.getInstance("X.509").generateCertificate(in);
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
@@ -189,7 +208,11 @@ public class Utils {
} }
public static boolean isCtrlDown() { public static boolean isCtrlDown() {
return Keyboard.isKeyDown(Keyboard.KEY_LCONTROL) || Keyboard.isKeyDown(Keyboard.KEY_RCONTROL); //#if MC>=11300
return GuiScreen.isCtrlKeyDown();
//#else
//$$ return Keyboard.isKeyDown(Keyboard.KEY_LCONTROL) || Keyboard.isKeyDown(Keyboard.KEY_RCONTROL);
//#endif
} }
public static <T> void addCallback(ListenableFuture<T> future, Consumer<T> onSuccess, Consumer<Throwable> onFailure) { public static <T> void addCallback(ListenableFuture<T> future, Consumer<T> onSuccess, Consumer<Throwable> onFailure) {
@@ -216,7 +239,7 @@ public class Utils {
// Try to save the crash report // Try to save the crash report
if (crashReport.getFile() == null) { if (crashReport.getFile() == null) {
try { try {
File folder = new File(getMinecraft().mcDataDir, "crash-reports"); File folder = new File(Minecraft_mcDataDir(getMinecraft()), "crash-reports");
File file = new File(folder, "crash-" + (new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss")).format(new Date()) + "-client.txt"); File file = new File(folder, "crash-" + (new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss")).format(new Date()) + "-client.txt");
logger.debug("Saving crash report to file: {}", file); logger.debug("Saving crash report to file: {}", file);
crashReport.saveToFile(file); crashReport.saveToFile(file);
@@ -263,7 +286,7 @@ public class Utils {
// Replace close button with panel containing close and copy buttons // Replace close button with panel containing close and copy buttons
GuiButton copyToClipboardButton = new GuiButton().setI18nLabel("chat.copy").onClick(() -> GuiButton copyToClipboardButton = new GuiButton().setI18nLabel("chat.copy").onClick(() ->
GuiScreen.setClipboardString(crashReport)).setSize(150, 20); MCVer.setClipboardString(crashReport)).setSize(150, 20);
GuiButton closeButton = getCloseButton(); GuiButton closeButton = getCloseButton();
popup.removeElement(closeButton); popup.removeElement(closeButton);
popup.addElements(new VerticalLayout.Data(1), popup.addElements(new VerticalLayout.Data(1),
@@ -281,4 +304,17 @@ public class Utils {
super.draw(renderer, size, renderInfo); super.draw(renderer, size, renderInfo);
} }
} }
public static <T extends Throwable> void throwIfInstanceOf(Throwable t, Class<T> cls) throws T {
if (cls.isInstance(t)) {
throw cls.cast(t);
}
}
public static void throwIfUnchecked(Throwable t) {
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else if (t instanceof Error) {
throw (Error) t;
}
}
} }

View File

@@ -8,15 +8,27 @@ public class WrappedTimer extends Timer {
protected final Timer wrapped; protected final Timer wrapped;
public WrappedTimer(Timer wrapped) { public WrappedTimer(Timer wrapped) {
super(0); //#if MC>=11300
super(0, 0);
//#else
//$$ super(0);
//#endif
this.wrapped = wrapped; this.wrapped = wrapped;
copy(wrapped, this); copy(wrapped, this);
} }
@Override @Override
public void updateTimer() { public void updateTimer(
//#if MC>=11300
long sysClock
//#endif
) {
copy(this, wrapped); copy(this, wrapped);
wrapped.updateTimer(); wrapped.updateTimer(
//#if MC>=11300
sysClock
//#endif
);
copy(wrapped, this); copy(wrapped, this);
} }

View File

@@ -0,0 +1,146 @@
//#if MC>=11300
package com.replaymod.core.versions;
import com.google.gson.Gson;
import com.replaymod.core.ReplayMod;
import net.minecraft.resources.AbstractResourcePack;
import net.minecraft.resources.IPackFinder;
import net.minecraft.resources.IResourcePack;
import net.minecraft.resources.ResourcePackFileNotFoundException;
import net.minecraft.resources.ResourcePackInfo;
import net.minecraft.resources.ResourcePackType;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
import net.minecraftforge.fml.packs.ModFileResourcePack;
import net.minecraftforge.fml.packs.ResourcePackLoader;
import org.apache.commons.io.IOUtils;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringBufferInputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* Resource pack which on-the-fly converts pre-1.13 language files into 1.13 json format.
*/
public class LangResourcePack extends AbstractResourcePack {
private static final Gson GSON = new Gson();
private static final String NAME = "replaymod_lang";
private static final Pattern JSON_FILE_PATTERN = Pattern.compile("^assets/" + ReplayMod.MOD_ID + "/lang/([a-z][a-z])_([a-z][a-z]).json$");
private static final Pattern LANG_FILE_NAME_PATTERN = Pattern.compile("^([a-z][a-z])_([a-z][a-z]).lang$");
LangResourcePack() {
super(new File(NAME));
}
private ModFileResourcePack getParent() {
return ResourcePackLoader.getResourcePackFor(ReplayMod.MOD_ID).orElseThrow(() -> new RuntimeException("Failed to get ReplayMod resource pack!"));
}
private String langName(String path) {
Matcher matcher = JSON_FILE_PATTERN.matcher(path);
if (!matcher.matches()) return null;
return String.format("%s_%s.lang", matcher.group(1), matcher.group(2).toUpperCase());
}
private Path langPath(String path) {
ModFileResourcePack parent = getParent();
if (parent == null) return null;
ModFile modFile = parent.getModFile();
String langName = langName(path);
if (langName == null) return null;
return modFile.getLocator().findPath(modFile, "assets", ReplayMod.MOD_ID, "lang", langName);
}
private String convertValue(String value) {
return value;
}
@Override
protected InputStream getInputStream(String path) throws IOException {
if ("pack.mcmeta".equals(path)) {
return new StringBufferInputStream("{\"pack\": {\"description\": \"ReplayMod language files\", \"pack_format\": 4}}");
}
Path langPath = langPath(path);
if (langPath == null) throw new ResourcePackFileNotFoundException(file, path);
String langFile;
try (InputStream in = Files.newInputStream(langPath)) {
langFile = IOUtils.toString(in);
}
Map<String, String> properties = new HashMap<>();
for (String line : langFile.split("\n")) {
if (line.trim().isEmpty() || line.trim().startsWith("#")) continue;
int i = line.indexOf('=');
String key = line.substring(0, i);
String value = line.substring(i + 1);
value = convertValue(value);
properties.put(key, value);
}
return new ByteArrayInputStream(GSON.toJson(properties).getBytes(StandardCharsets.UTF_8));
}
@Override
protected boolean resourceExists(String path) {
Path langPath = langPath(path);
return langPath != null && Files.exists(langPath);
}
@Override
public Collection<ResourceLocation> getAllResourceLocations(ResourcePackType resourcePackType, String path, int maxDepth, Predicate<String> filter) {
if (resourcePackType == ResourcePackType.CLIENT_RESOURCES && "lang".equals(path)) {
IResourcePack parent = getParent();
if (parent == null) return Collections.emptyList();
return parent.getAllResourceLocations(resourcePackType, path, maxDepth, name -> {
Matcher matcher = LANG_FILE_NAME_PATTERN.matcher(name);
if (matcher.matches()) {
return filter.test(String.format("%s_%s.json", matcher.group(1), matcher.group(1)));
} else {
return false;
}
}).stream().map(resourceLocation -> {
String p = resourceLocation.getPath().substring(0, "assets/lang/xx_XX".length()) + ".json";
return new ResourceLocation(resourceLocation.getNamespace(), p);
}).collect(Collectors.toList());
} else {
return Collections.emptyList();
}
}
@Override
public Set<String> getResourceNamespaces(ResourcePackType resourcePackType) {
if (resourcePackType == ResourcePackType.CLIENT_RESOURCES) {
return Collections.singleton("replaymod");
} else {
return Collections.emptySet();
}
}
@Override
public void close() {}
public static class Finder implements IPackFinder {
@Override
public <T extends ResourcePackInfo> void addPackInfosToMap(Map<String, T> packList, ResourcePackInfo.IFactory<T> factory) {
packList.put(NAME, ResourcePackInfo.func_195793_a(NAME, true, LangResourcePack::new, factory, ResourcePackInfo.Priority.BOTTOM));
}
}
}
//#endif

View File

@@ -6,9 +6,6 @@ import net.minecraft.client.entity.EntityPlayerSP;
import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.GuiButton; import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.ScaledResolution;
import net.minecraft.client.model.ModelBox;
import net.minecraft.client.model.ModelRenderer;
import net.minecraft.client.multiplayer.ServerData; import net.minecraft.client.multiplayer.ServerData;
import net.minecraft.client.multiplayer.ServerList; import net.minecraft.client.multiplayer.ServerList;
import net.minecraft.client.multiplayer.WorldClient; import net.minecraft.client.multiplayer.WorldClient;
@@ -16,8 +13,8 @@ import net.minecraft.client.network.NetHandlerPlayClient;
import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.entity.RenderManager; import net.minecraft.client.renderer.entity.RenderManager;
import net.minecraft.client.renderer.texture.TextureMap; import net.minecraft.client.renderer.texture.TextureMap;
import net.minecraft.client.resources.ResourcePackRepository;
import net.minecraft.client.settings.KeyBinding; import net.minecraft.client.settings.KeyBinding;
import net.minecraft.crash.CrashReport;
import net.minecraft.crash.CrashReportCategory; import net.minecraft.crash.CrashReportCategory;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.EntityLivingBase;
@@ -25,6 +22,7 @@ import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.network.Packet; import net.minecraft.network.Packet;
import net.minecraft.network.PacketBuffer; import net.minecraft.network.PacketBuffer;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraft.util.Util;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk; import net.minecraft.world.chunk.Chunk;
import net.minecraftforge.client.event.GuiScreenEvent; import net.minecraftforge.client.event.GuiScreenEvent;
@@ -32,6 +30,25 @@ import net.minecraftforge.client.event.RenderGameOverlayEvent;
import net.minecraftforge.client.event.RenderLivingEvent; import net.minecraftforge.client.event.RenderLivingEvent;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
//#if MC>=11300
import net.minecraft.client.MainWindow;
import net.minecraft.client.renderer.entity.model.ModelBox;
import net.minecraft.client.renderer.entity.model.ModelRenderer;
import net.minecraft.client.util.InputMappings;
import net.minecraft.crash.ReportedException;
import org.lwjgl.glfw.GLFW;
//#else
//$$ import net.minecraft.client.gui.ScaledResolution;
//$$ import net.minecraft.client.model.ModelBox;
//$$ import net.minecraft.client.model.ModelRenderer;
//$$ import net.minecraft.client.resources.ResourcePackRepository;
//$$ import net.minecraft.util.ReportedException;
//$$ import org.apache.logging.log4j.LogManager;
//$$ import org.lwjgl.Sys;
//$$ import java.awt.Desktop;
//#endif
//#if MC>=10904 //#if MC>=10904
//#if MC>=11200 //#if MC>=11200
import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.BufferBuilder;
@@ -63,7 +80,11 @@ import net.minecraft.client.renderer.GlStateManager.BooleanState;
import net.minecraft.client.renderer.vertex.VertexFormat; import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.client.renderer.vertex.VertexFormatElement; import net.minecraft.client.renderer.vertex.VertexFormatElement;
import net.minecraft.world.WorldType; import net.minecraft.world.WorldType;
import net.minecraftforge.fml.common.eventhandler.EventBus; //#if MC>=11300
import net.minecraftforge.eventbus.api.IEventBus;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.EventBus;
//#endif
//#else //#else
//$$ import com.google.common.util.concurrent.Futures; //$$ import com.google.common.util.concurrent.Futures;
//$$ import com.replaymod.render.hooks.GLStateTracker; //$$ import com.replaymod.render.hooks.GLStateTracker;
@@ -85,12 +106,17 @@ import java.util.concurrent.Callable;
* Abstraction over things that have changed between different MC versions. * Abstraction over things that have changed between different MC versions.
*/ */
public class MCVer { public class MCVer {
public static EventBus FORGE_BUS = MinecraftForge.EVENT_BUS; //#if MC>=11300
public static IEventBus FORGE_BUS = MinecraftForge.EVENT_BUS;
public static IEventBus FML_BUS = FORGE_BUS;
//#else
//$$ public static EventBus FORGE_BUS = MinecraftForge.EVENT_BUS;
//#if MC>=10809 //#if MC>=10809
public static EventBus FML_BUS = FORGE_BUS; //$$ public static EventBus FML_BUS = FORGE_BUS;
//#else //#else
//$$ public static EventBus FML_BUS = FMLCommonHandler.instance().bus(); //$$ public static EventBus FML_BUS = FMLCommonHandler.instance().bus();
//#endif //#endif
//#endif
public static void addDetail(CrashReportCategory category, String name, Callable<String> callable) { public static void addDetail(CrashReportCategory category, String name, Callable<String> callable) {
//#if MC>=10904 //#if MC>=10904
@@ -104,6 +130,22 @@ public class MCVer {
//#endif //#endif
} }
public static void addButton(GuiScreenEvent.InitGuiEvent event, GuiButton button) {
//#if MC>=11300
event.addButton(button);
//#else
//$$ getButtonList(event).add(button);
//#endif
}
public static void removeButton(GuiScreenEvent.InitGuiEvent event, GuiButton button) {
//#if MC>=11300
event.removeButton(button);
//#else
//$$ getButtonList(event).remove(button);
//#endif
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static List<GuiButton> getButtonList(GuiScreenEvent.InitGuiEvent event) { public static List<GuiButton> getButtonList(GuiScreenEvent.InitGuiEvent event) {
//#if MC>=10904 //#if MC>=10904
@@ -226,6 +268,14 @@ public class MCVer {
//#endif //#endif
//#endif //#endif
public static File mcDataDir(Minecraft mc) {
//#if MC>=11300
return mc.gameDir;
//#else
//$$ return mc.mcDataDir;
//#endif
}
//#if MC>=10800 //#if MC>=10800
public static Entity getRenderViewEntity(Minecraft mc) { public static Entity getRenderViewEntity(Minecraft mc) {
return mc.getRenderViewEntity(); return mc.getRenderViewEntity();
@@ -318,8 +368,11 @@ public class MCVer {
} }
public static BooleanState fog() { public static BooleanState fog() {
//#if MC>=11300
return GlStateManager.FOG.fog;
//#else
//#if MC>=10809 //#if MC>=10809
return GlStateManager.fogState.fog; //$$ return GlStateManager.fogState.fog;
//#else //#else
//#if MC>=10800 //#if MC>=10800
//$$ return GlStateManager.fogState.field_179049_a; //$$ return GlStateManager.fogState.field_179049_a;
@@ -327,11 +380,15 @@ public class MCVer {
//$$ return GLStateTracker.getInstance().fog; //$$ return GLStateTracker.getInstance().fog;
//#endif //#endif
//#endif //#endif
//#endif
} }
public static void fog(BooleanState fog) { public static void fog(BooleanState fog) {
//#if MC>=11300
GlStateManager.FOG.fog = fog;
//#else
//#if MC>=10809 //#if MC>=10809
GlStateManager.fogState.fog = fog; //$$ GlStateManager.fogState.fog = fog;
//#else //#else
//#if MC>=10800 //#if MC>=10800
//$$ GlStateManager.fogState.field_179049_a = fog; //$$ GlStateManager.fogState.field_179049_a = fog;
@@ -339,22 +396,31 @@ public class MCVer {
//$$ GLStateTracker.getInstance().fog = fog; //$$ GLStateTracker.getInstance().fog = fog;
//#endif //#endif
//#endif //#endif
//#endif
} }
public static BooleanState texture2DState(int index) { public static BooleanState texture2DState(int index) {
//#if MC>=11300
return GlStateManager.TEXTURES[index].texture2DState;
//#else
//#if MC>=10800 //#if MC>=10800
return GlStateManager.textureState[index].texture2DState; //$$ return GlStateManager.textureState[index].texture2DState;
//#else //#else
//$$ return GLStateTracker.getInstance().texture[index]; //$$ return GLStateTracker.getInstance().texture[index];
//#endif //#endif
//#endif
} }
public static void texture2DState(int index, BooleanState texture2DState) { public static void texture2DState(int index, BooleanState texture2DState) {
//#if MC>=11300
GlStateManager.TEXTURES[index].texture2DState = texture2DState;
//#else
//#if MC>=10800 //#if MC>=10800
GlStateManager.textureState[index].texture2DState = texture2DState; //$$ GlStateManager.textureState[index].texture2DState = texture2DState;
//#else //#else
//$$ GLStateTracker.getInstance().texture[index] = texture2DState; //$$ GLStateTracker.getInstance().texture[index] = texture2DState;
//#endif //#endif
//#endif
} }
public static void ServerList_saveSingleServer(ServerData serverData) { public static void ServerList_saveSingleServer(ServerData serverData) {
@@ -373,18 +439,29 @@ public class MCVer {
//#endif //#endif
} }
public static ScaledResolution newScaledResolution(Minecraft mc) {
//#if MC>=10809
return new ScaledResolution(mc);
//#else
//$$ return new ScaledResolution(mc, mc.displayWidth, mc.displayHeight);
//#endif
}
public static ListenableFuture setServerResourcePack(ResourcePackRepository repo, File file) { //#if MC>=11300
public static MainWindow newScaledResolution(Minecraft mc) {
return mc.mainWindow;
}
//#else
//$$ public static ScaledResolution newScaledResolution(Minecraft mc) {
//#if MC>=10809
//$$ return new ScaledResolution(mc);
//#else
//$$ return new ScaledResolution(mc, mc.displayWidth, mc.displayHeight);
//#endif
//$$ }
//#endif
public static ListenableFuture setServerResourcePack(File file) {
//#if MC>=11300
return getMinecraft().getPackFinder().func_195741_a(file);
//#else
//$$ ResourcePackRepository repo = getMinecraft().getResourcePackRepository();
//#if MC>=10809 //#if MC>=10809
//#if MC>=11200 //#if MC>=11200
return repo.setServerResourcePack(file); //$$ return repo.setServerResourcePack(file);
//#else //#else
//$$ return repo.setResourcePackInstance(file); //$$ return repo.setResourcePackInstance(file);
//#endif //#endif
@@ -398,6 +475,7 @@ public class MCVer {
//$$ return Futures.immediateFuture(null); //$$ return Futures.immediateFuture(null);
//#endif //#endif
//#endif //#endif
//#endif
} }
public static boolean isKeyDown(KeyBinding keyBinding) { public static boolean isKeyDown(KeyBinding keyBinding) {
@@ -566,12 +644,36 @@ public class MCVer {
public static RenderManager getRenderManager() { public static RenderManager getRenderManager() {
//#if MC>=10800 //#if MC>=10800
return Minecraft.getMinecraft().getRenderManager(); return getMinecraft().getRenderManager();
//#else //#else
//$$ return RenderManager.instance; //$$ return RenderManager.instance;
//#endif //#endif
} }
public static Minecraft getMinecraft() {
//#if MC>=11300
return Minecraft.getInstance();
//#else
//$$ return Minecraft.getMinecraft();
//#endif
}
public static long milliTime() {
//#if MC>=11300
return Util.milliTime();
//#else
//$$ return Minecraft.getSystemTime();
//#endif
}
public static File Minecraft_mcDataDir(Minecraft mc) {
//#if MC>=11300
return mc.gameDir;
//#else
//$$ return mc.mcDataDir;
//#endif
}
public static int floor(double val) { public static int floor(double val) {
//#if MC>=11102 //#if MC>=11102
return MathHelper.floor(val); return MathHelper.floor(val);
@@ -580,6 +682,14 @@ public class MCVer {
//#endif //#endif
} }
public static void bindTexture(ResourceLocation texture) {
//#if MC>=11300
getMinecraft().getTextureManager().bindTexture(texture);
//#else
//$$ getMinecraft().renderEngine.bindTexture(texture);
//#endif
}
public static float cos(float val) { public static float cos(float val) {
return MathHelper.cos(val); return MathHelper.cos(val);
} }
@@ -612,6 +722,40 @@ public class MCVer {
//#endif //#endif
} }
public static ReportedException newReportedException(CrashReport crashReport) {
return new ReportedException(crashReport);
}
public static void openFile(File file) {
//#if MC>=11300
Util.getOSType().openFile(file);
//#else
//$$ String path = file.getAbsolutePath();
//$$
//$$ // First try OS specific methods
//$$ try {
//$$ switch (Util.getOSType()) {
//$$ case WINDOWS:
//$$ Runtime.getRuntime().exec(String.format("cmd.exe /C start \"Open file\" \"%s\"", path));
//$$ return;
//$$ case OSX:
//$$ Runtime.getRuntime().exec(new String[]{"/usr/bin/open", path});
//$$ return;
//$$ }
//$$ } catch (IOException e) {
//$$ LogManager.getLogger().error("Cannot open file", e);
//$$ }
//$$
//$$ // Otherwise try to java way
//$$ try {
//$$ Desktop.getDesktop().browse(file.toURI());
//$$ } catch (Throwable throwable) {
//$$ // And if all fails, lwjgl
//$$ Sys.openURL("file://" + path);
//$$ }
//#endif
}
//#if MC<=10710 //#if MC<=10710
//$$ public static class GlStateManager { //$$ public static class GlStateManager {
//$$ public static void resetColor() { /* nop */ } //$$ public static void resetColor() { /* nop */ }
@@ -628,4 +772,59 @@ public class MCVer {
//$$ public static void rotate(float angle, float x, float y, float z) { glRotatef(angle, x, y, z); } //$$ public static void rotate(float angle, float x, float y, float z) { glRotatef(angle, x, y, z); }
//$$ } //$$ }
//#endif //#endif
//#if MC>=11300
public static void color(float r, float g, float b, float a) { GlStateManager.color4f(r, g, b, a); }
public static void enableAlpha() { GlStateManager.enableAlphaTest(); }
public static void disableAlpha() { GlStateManager.disableAlphaTest(); }
public static void tryBlendFuncSeparate(int l, int r, int vl, int vr) { GlStateManager.blendFuncSeparate(l, r, vl, vr); }
public static void colorLogicOp(int op) { GlStateManager.logicOp(op); }
public static abstract class Keyboard {
public static final int KEY_LCONTROL = GLFW.GLFW_KEY_LEFT_CONTROL;
public static final int KEY_LSHIFT = GLFW.GLFW_KEY_LEFT_SHIFT;
public static final int KEY_ESCAPE = GLFW.GLFW_KEY_ESCAPE;
public static final int KEY_HOME = GLFW.GLFW_KEY_HOME;
public static final int KEY_END = GLFW.GLFW_KEY_END;
public static final int KEY_UP = GLFW.GLFW_KEY_UP;
public static final int KEY_DOWN = GLFW.GLFW_KEY_DOWN;
public static final int KEY_LEFT = GLFW.GLFW_KEY_LEFT;
public static final int KEY_RIGHT = GLFW.GLFW_KEY_RIGHT;
public static final int KEY_BACK = GLFW.GLFW_KEY_BACKSPACE;
public static final int KEY_DELETE = GLFW.GLFW_KEY_DELETE;
public static final int KEY_RETURN = GLFW.GLFW_KEY_ENTER;
public static final int KEY_TAB = GLFW.GLFW_KEY_TAB;
public static final int KEY_F1 = GLFW.GLFW_KEY_F1;
public static final int KEY_A = GLFW.GLFW_KEY_A;
public static final int KEY_B = GLFW.GLFW_KEY_B;
public static final int KEY_C = GLFW.GLFW_KEY_C;
public static final int KEY_D = GLFW.GLFW_KEY_D;
public static final int KEY_E = GLFW.GLFW_KEY_E;
public static final int KEY_F = GLFW.GLFW_KEY_F;
public static final int KEY_G = GLFW.GLFW_KEY_G;
public static final int KEY_H = GLFW.GLFW_KEY_H;
public static final int KEY_I = GLFW.GLFW_KEY_I;
public static final int KEY_J = GLFW.GLFW_KEY_J;
public static final int KEY_K = GLFW.GLFW_KEY_K;
public static final int KEY_L = GLFW.GLFW_KEY_L;
public static final int KEY_M = GLFW.GLFW_KEY_M;
public static final int KEY_N = GLFW.GLFW_KEY_N;
public static final int KEY_O = GLFW.GLFW_KEY_O;
public static final int KEY_P = GLFW.GLFW_KEY_P;
public static final int KEY_Q = GLFW.GLFW_KEY_Q;
public static final int KEY_R = GLFW.GLFW_KEY_R;
public static final int KEY_S = GLFW.GLFW_KEY_S;
public static final int KEY_T = GLFW.GLFW_KEY_T;
public static final int KEY_U = GLFW.GLFW_KEY_U;
public static final int KEY_V = GLFW.GLFW_KEY_V;
public static final int KEY_W = GLFW.GLFW_KEY_W;
public static final int KEY_X = GLFW.GLFW_KEY_X;
public static final int KEY_Y = GLFW.GLFW_KEY_Y;
public static final int KEY_Z = GLFW.GLFW_KEY_Z;
public static boolean isKeyDown(int keyCode) {
return InputMappings.isKeyDown(keyCode);
}
}
//#endif
} }

View File

@@ -1,49 +1,29 @@
package com.replaymod.editor; package com.replaymod.editor;
import com.replaymod.core.Module;
import com.replaymod.core.ReplayMod; import com.replaymod.core.ReplayMod;
import com.replaymod.editor.handler.GuiHandler; import com.replaymod.editor.handler.GuiHandler;
import com.replaymod.online.Setting; import com.replaymod.online.Setting;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
//#if MC>=10800 public class ReplayModEditor implements Module {
import net.minecraftforge.fml.common.Mod; { instance = this; }
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
//#else
//$$ import cpw.mods.fml.common.Mod;
//$$ import cpw.mods.fml.common.event.FMLInitializationEvent;
//$$ import cpw.mods.fml.common.event.FMLPreInitializationEvent;
//#endif
@Mod(modid = ReplayModEditor.MOD_ID,
version = "@MOD_VERSION@",
acceptedMinecraftVersions = "@MC_VERSION@",
acceptableRemoteVersions = "*",
//#if MC>=10800
clientSideOnly = true,
//#endif
useMetadata = true)
public class ReplayModEditor {
public static final String MOD_ID = "replaymod-editor";
@Mod.Instance(MOD_ID)
public static ReplayModEditor instance; public static ReplayModEditor instance;
private ReplayMod core; private ReplayMod core;
public static Logger LOGGER; public static Logger LOGGER = LogManager.getLogger();
@Mod.EventHandler public ReplayModEditor(ReplayMod core) {
public void preInit(FMLPreInitializationEvent event) { this.core = core;
ReplayModEditor.LOGGER = event.getModLog();
core = ReplayMod.instance;
core.getSettingsRegistry().register(Setting.class); core.getSettingsRegistry().register(Setting.class);
} }
@Mod.EventHandler @Override
public void init(FMLInitializationEvent event) { public void initClient() {
new GuiHandler(this).register(); new GuiHandler().register();
} }
public ReplayMod getCore() { public ReplayMod getCore() {

View File

@@ -0,0 +1,262 @@
package com.replaymod.editor.gui;
import com.replaymod.core.ReplayMod;
import com.replaymod.core.utils.Utils;
import com.replaymod.editor.ReplayModEditor;
import com.replaymod.replay.gui.overlay.GuiMarkerTimeline;
import com.replaymod.replaystudio.data.Marker;
import com.replaymod.replaystudio.replay.ZipReplayFile;
import com.replaymod.replaystudio.studio.ReplayStudio;
import de.johni0702.minecraft.gui.GuiRenderer;
import de.johni0702.minecraft.gui.container.GuiContainer;
import de.johni0702.minecraft.gui.container.GuiPanel;
import de.johni0702.minecraft.gui.element.GuiButton;
import de.johni0702.minecraft.gui.element.GuiHorizontalScrollbar;
import de.johni0702.minecraft.gui.element.GuiTexturedButton;
import de.johni0702.minecraft.gui.element.GuiTooltip;
import de.johni0702.minecraft.gui.element.advanced.GuiProgressBar;
import de.johni0702.minecraft.gui.element.advanced.GuiTimelineTime;
import de.johni0702.minecraft.gui.layout.CustomLayout;
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
import de.johni0702.minecraft.gui.layout.VerticalLayout;
import de.johni0702.minecraft.gui.popup.AbstractGuiPopup;
import de.johni0702.minecraft.gui.utils.lwjgl.Color;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import net.minecraft.crash.CrashReport;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
public class GuiEditReplay extends AbstractGuiPopup<GuiEditReplay> {
private final Path inputPath;
private final EditTimeline timeline;
private final GuiTimelineTime<GuiMarkerTimeline> timelineTime = new GuiTimelineTime<>();
private final GuiHorizontalScrollbar scrollbar = new GuiHorizontalScrollbar().setSize(300, 9);
private final GuiTexturedButton zoomInButton = new GuiTexturedButton().setSize(9, 9)
.onClick(() -> zoomTimeline(2d / 3d))
.setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE).setTexturePosH(40, 20)
.setTooltip(new GuiTooltip().setI18nText("replaymod.gui.ingame.menu.zoomin"));
private final GuiTexturedButton zoomOutButton = new GuiTexturedButton().setSize(9, 9)
.onClick(() -> zoomTimeline(3d / 2d))
.setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE).setTexturePosH(40, 30)
.setTooltip(new GuiTooltip().setI18nText("replaymod.gui.ingame.menu.zoomout"));
private final GuiPanel zoomButtonPanel = new GuiPanel()
.setLayout(new VerticalLayout(VerticalLayout.Alignment.CENTER).setSpacing(2))
.addElements(null, zoomInButton, zoomOutButton);
private Set<Marker> markers;
protected GuiEditReplay(GuiContainer container, Path inputPath) throws IOException {
super(container);
this.inputPath = inputPath;
try (ZipReplayFile replayFile = new ZipReplayFile(new ReplayStudio(), inputPath.toFile())) {
markers = replayFile.getMarkers().or(HashSet::new);
timeline = new EditTimeline(new HashSet<>(markers), markers -> this.markers = markers);
timeline.setSize(300, 20)
.setMarkers()
.setLength(replayFile.getMetaData().getDuration())
.onClick(timeline::setCursorPosition);
}
timelineTime.setTimeline(timeline);
scrollbar.onValueChanged(() -> {
timeline.setOffset((int) (scrollbar.getPosition() * timeline.getLength()));
timeline.setZoom(scrollbar.getZoom());
}).setZoom(1);
GuiPanel timelinePanel = new GuiPanel()
.setSize(300, 40)
.setLayout(new CustomLayout<GuiPanel>() {
@Override
protected void layout(GuiPanel container, int width, int height) {
pos(zoomButtonPanel, width - width(zoomButtonPanel), 10);
pos(timelineTime, 0, 2);
size(timelineTime, x(zoomButtonPanel), 8);
pos(timeline, 0, y(timelineTime) + height(timelineTime));
size(timeline, x(zoomButtonPanel) - 2, 20);
pos(scrollbar, 0, y(timeline) + height(timeline) + 1);
size(scrollbar, x(zoomButtonPanel) - 2, 9);
}
})
.addElements(null, timelineTime, timeline, scrollbar, zoomButtonPanel);
GuiButton buttonAddSplit = new GuiButton()
.setSize(100, 20)
.setI18nLabel("replaymod.gui.edit.split")
.setTooltip(new GuiTooltip().setI18nText("replaymod.gui.edit.split.tooltip"))
.onClick(() -> {
Marker marker = new Marker();
marker.setTime(timeline.getCursorPosition());
marker.setName(MarkerProcessor.MARKER_NAME_SPLIT);
timeline.addMarker(marker);
});
GuiButton buttonInsertCut = new GuiButton()
.setSize(100, 20)
.setI18nLabel("replaymod.gui.edit.cut.start")
.setTooltip(new GuiTooltip().setI18nText("replaymod.gui.edit.cut.tooltip"))
.onClick(() -> {
Marker marker = new Marker();
marker.setTime(timeline.getCursorPosition());
marker.setName(MarkerProcessor.MARKER_NAME_START_CUT);
timeline.addMarker(marker);
});
GuiButton buttonEndCut = new GuiButton()
.setSize(100, 20)
.setI18nLabel("replaymod.gui.edit.cut.end")
.setTooltip(new GuiTooltip().setI18nText("replaymod.gui.edit.cut.tooltip"))
.onClick(() -> {
Marker marker = new Marker();
marker.setTime(timeline.getCursorPosition());
marker.setName(MarkerProcessor.MARKER_NAME_END_CUT);
timeline.addMarker(marker);
});
GuiPanel controlPanel = new GuiPanel()
.setLayout(new HorizontalLayout().setSpacing(4))
.addElements(null, buttonAddSplit, buttonInsertCut, buttonEndCut);
GuiButton applyButton = new GuiButton().setI18nLabel("replaymod.gui.edit.apply").setSize(150, 20).onClick(this::apply);
GuiButton closeButton = new GuiButton().setI18nLabel("replaymod.gui.close").setSize(150, 20).onClick(this::close);
GuiPanel buttonPanel = new GuiPanel()
.setLayout(new HorizontalLayout().setSpacing(8))
.addElements(null, applyButton, closeButton);
popup.setLayout(new VerticalLayout(VerticalLayout.Alignment.TOP).setSpacing(10));
popup.addElements(new VerticalLayout.Data(0.5), timelinePanel, controlPanel, buttonPanel);
}
private void zoomTimeline(double factor) {
scrollbar.setZoom(scrollbar.getZoom() * factor);
}
private void apply() {
ProgressPopup progressPopup = new ProgressPopup(this);
new Thread(() -> {
try (ZipReplayFile replayFile = new ZipReplayFile(new ReplayStudio(), inputPath.toFile())) {
replayFile.writeMarkers(markers);
replayFile.save();
} catch (IOException e) {
Utils.error(ReplayModEditor.LOGGER, this, CrashReport.makeCrashReport(e, "Writing markers"), this::close);
}
try {
MarkerProcessor.apply(inputPath, progressPopup.progressBar::setProgress);
} catch (IOException e) {
Utils.error(ReplayModEditor.LOGGER, this, CrashReport.makeCrashReport(e, "Running marker processor"), this::close);
}
ReplayMod.instance.runLater(() -> {
progressPopup.close();
close();
});
}).start();
}
@Override
public void open() {
super.open();
}
@Override
protected GuiEditReplay getThis() {
return this;
}
private class ProgressPopup extends AbstractGuiPopup<ProgressPopup> {
private final GuiProgressBar progressBar = new GuiProgressBar(popup).setSize(300, 20);
ProgressPopup(GuiContainer container) {
super(container);
open();
}
@Override
public void close() {
super.close();
}
@Override
protected ProgressPopup getThis() {
return this;
}
}
private class EditTimeline extends GuiMarkerTimeline {
EditTimeline(Set<Marker> markers, Consumer<Set<Marker>> saveMarkers) {
super(markers, saveMarkers);
}
@Override
protected void drawMarkers(GuiRenderer renderer, ReadableDimension size) {
drawCutQuads(renderer, size);
super.drawMarkers(renderer, size);
}
@Override
protected void drawMarker(GuiRenderer renderer, ReadableDimension size, Marker marker, int markerX) {
if (MarkerProcessor.MARKER_NAME_SPLIT.equals(marker.getName())) {
int height = size.getHeight() - BORDER_BOTTOM - BORDER_TOP - MARKER_SIZE + 1;
for (int y = 0; y < height; y += 3) {
renderer.drawRect(markerX, BORDER_TOP + y, 1, 2, Color.WHITE);
}
}
super.drawMarker(renderer, size, marker, markerX);
}
private void drawCutQuads(GuiRenderer renderer, ReadableDimension size) {
boolean inCut = false;
int startTime = 0;
for (Marker marker : markers.stream().sorted(Comparator.comparing(Marker::getTime)).collect(Collectors.toList())) {
if (MarkerProcessor.MARKER_NAME_START_CUT.equals(marker.getName()) && !inCut) {
inCut = true;
startTime = marker.getTime();
} else if (MarkerProcessor.MARKER_NAME_END_CUT.equals(marker.getName()) && inCut) {
drawCutQuad(renderer, size, startTime, marker.getTime());
inCut = false;
}
}
if (inCut) {
drawCutQuad(renderer, size, startTime, getLength());
}
}
private void drawCutQuad(GuiRenderer renderer, ReadableDimension size, int startFrameTime, int endFrameTime) {
int visibleWidth = size.getWidth() - BORDER_LEFT - BORDER_RIGHT;
int startTime = getOffset();
int visibleTime = (int) (getZoom() * getLength());
int endTime = getOffset() + visibleTime;
if (startFrameTime >= endTime || endFrameTime <= startTime) {
return; // Segment out of display range
}
double relativeStart = startFrameTime - startTime;
double relativeEnd = endFrameTime - startTime;
int startX = BORDER_LEFT + Math.max(0, (int) (relativeStart / visibleTime * visibleWidth) + MARKER_SIZE / 2 + 1);
int endX = BORDER_LEFT + Math.min(visibleWidth, (int) (relativeEnd / visibleTime * visibleWidth) - MARKER_SIZE / 2);
if (startX < endX) {
renderer.drawRect(startX + 1, size.getHeight() - BORDER_BOTTOM - MARKER_SIZE,
endX - startX - 2, MARKER_SIZE - 2, Color.RED);
}
}
}
}

View File

@@ -34,10 +34,10 @@ import de.johni0702.minecraft.gui.popup.AbstractGuiPopup;
import de.johni0702.minecraft.gui.popup.GuiInfoPopup; import de.johni0702.minecraft.gui.popup.GuiInfoPopup;
import de.johni0702.minecraft.gui.popup.GuiYesNoPopup; import de.johni0702.minecraft.gui.popup.GuiYesNoPopup;
import de.johni0702.minecraft.gui.utils.Colors; import de.johni0702.minecraft.gui.utils.Colors;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReport;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import org.lwjgl.util.ReadableDimension;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.io.File; import java.io.File;

View File

@@ -1,7 +1,10 @@
package com.replaymod.editor.gui; package com.replaymod.editor.gui;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.replaymod.core.utils.Utils; import com.replaymod.core.utils.Utils;
import com.replaymod.replay.gui.screen.GuiReplayViewer;
import com.replaymod.replaystudio.data.Marker; import com.replaymod.replaystudio.data.Marker;
import com.replaymod.replaystudio.filter.ChangeTimestampFilter; import com.replaymod.replaystudio.filter.ChangeTimestampFilter;
import com.replaymod.replaystudio.filter.RemoveFilter; import com.replaymod.replaystudio.filter.RemoveFilter;
@@ -12,21 +15,20 @@ import com.replaymod.replaystudio.replay.ZipReplayFile;
import com.replaymod.replaystudio.stream.PacketStream; import com.replaymod.replaystudio.stream.PacketStream;
import com.replaymod.replaystudio.studio.ReplayStudio; import com.replaymod.replaystudio.studio.ReplayStudio;
import de.johni0702.minecraft.gui.container.GuiPanel; import de.johni0702.minecraft.gui.container.GuiPanel;
import de.johni0702.minecraft.gui.element.GuiButton;
import de.johni0702.minecraft.gui.element.GuiLabel; import de.johni0702.minecraft.gui.element.GuiLabel;
import de.johni0702.minecraft.gui.element.GuiNumberField; import de.johni0702.minecraft.gui.element.GuiNumberField;
import de.johni0702.minecraft.gui.element.advanced.GuiDropdownMenu; import de.johni0702.minecraft.gui.element.advanced.GuiDropdownMenu;
import de.johni0702.minecraft.gui.layout.GridLayout; import de.johni0702.minecraft.gui.layout.GridLayout;
import de.johni0702.minecraft.gui.layout.HorizontalLayout; import de.johni0702.minecraft.gui.layout.HorizontalLayout;
import de.johni0702.minecraft.gui.layout.VerticalLayout; import de.johni0702.minecraft.gui.layout.VerticalLayout;
import de.johni0702.minecraft.gui.utils.lwjgl.Dimension;
import net.minecraft.client.resources.I18n; import net.minecraft.client.resources.I18n;
import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReport;
import org.apache.commons.io.IOCase;
import org.apache.commons.io.filefilter.SuffixFileFilter;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import org.lwjgl.util.Dimension;
import javax.annotation.Nullable;
import java.io.File; import java.io.File;
import java.io.FileFilter;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@@ -50,9 +52,10 @@ public class GuiTrimPanel extends GuiPanel {
private final GuiReplayEditor gui; private final GuiReplayEditor gui;
public final GuiDropdownMenu<File> inputReplays = new GuiDropdownMenu<File>(this) private final GuiButton inputReplayButton = new GuiButton(this)
.setMinSize(new Dimension(200, 20)).onSelection(i -> updateSelectedReplay()) .setMinSize(new Dimension(200, 20))
.setToString(f -> f == NO_REPLAY ? "" : Utils.fileNameToReplayName(f.getName())); .setMaxSize(new Dimension(350, 20));
private File inputReplay = NO_REPLAY;
public final GuiNumberField startHour = newGuiNumberField(); public final GuiNumberField startHour = newGuiNumberField();
public final GuiNumberField startMin = newGuiNumberField(); public final GuiNumberField startMin = newGuiNumberField();
@@ -104,24 +107,31 @@ public class GuiTrimPanel extends GuiPanel {
endMarker.setToString(toString).setMinSize(new Dimension(100, 20)).onSelection(i -> endMarker.setToString(toString).setMinSize(new Dimension(100, 20)).onSelection(i ->
onSelectedMarkerChanged(endMarker, endHour, endMin, endSec, endMilli)); onSelectedMarkerChanged(endMarker, endHour, endMin, endSec, endMilli));
File[] files = null; inputReplayButton.onClick(() -> {
try { File folder;
File folder = gui.getMod().getReplayFolder(); try {
files = folder.listFiles((FileFilter) new SuffixFileFilter(".mcpr", IOCase.INSENSITIVE)); folder = gui.getMod().getReplayFolder();
} catch (IOException e) { } catch (IOException e) {
Utils.error(LOGGER, gui, CrashReport.makeCrashReport(e, "Listing available replays"), gui.backButton::onClick); Utils.error(LOGGER, gui, CrashReport.makeCrashReport(e, "Getting replay folder"), gui.backButton::onClick);
} return;
if (files == null) { }
LOGGER.warn("Replay file list is null, has the replay folder been deleted?"); GuiReplayViewer.GuiSelectReplayPopup popup = GuiReplayViewer.GuiSelectReplayPopup.openGui(gui, folder);
files = new File[0]; Futures.addCallback(popup.getFuture(), new FutureCallback<File>() {
} else { @Override
LOGGER.debug("Found {} replays in the replay folder", files.length); public void onSuccess(@Nullable File result) {
} if (result != null) {
if (files.length == 0) { inputReplay = result;
inputReplays.setValues(NO_REPLAY); updateSelectedReplay();
} else { updateReadyState();
inputReplays.setValues(files); }
} }
@Override
public void onFailure(Throwable t) {
Utils.error(LOGGER, gui, CrashReport.makeCrashReport(t, "Selecting replay"), gui.backButton::onClick);
}
});
});
updateSelectedReplay(); updateSelectedReplay();
updateReadyState(); updateReadyState();
@@ -135,7 +145,7 @@ public class GuiTrimPanel extends GuiPanel {
JsonObject config = new JsonObject(); JsonObject config = new JsonObject();
config.addProperty("offset", -start); config.addProperty("offset", -start);
// Pass filters to save dialog // Pass filters to save dialog
gui.save(inputReplays.getSelectedValue(), gui.save(inputReplay,
Pair.of(new PacketStream.FilterInfo(new SquashFilter(), -1, start), new JsonObject()), Pair.of(new PacketStream.FilterInfo(new SquashFilter(), -1, start), new JsonObject()),
Pair.of(new PacketStream.FilterInfo(ctf, start, end), config), Pair.of(new PacketStream.FilterInfo(ctf, start, end), config),
Pair.of(new PacketStream.FilterInfo(new RemoveFilter(), end, -1), new JsonObject()) Pair.of(new PacketStream.FilterInfo(new RemoveFilter(), end, -1), new JsonObject())
@@ -161,7 +171,11 @@ public class GuiTrimPanel extends GuiPanel {
} }
private void updateSelectedReplay() { private void updateSelectedReplay() {
File file = inputReplays.getSelectedValue(); File file = inputReplay;
// Update input button label
inputReplayButton.setLabel(file == NO_REPLAY ? I18n.format("gui.none") : Utils.fileNameToReplayName(file.getName()));
// Load markers and meta data from replay file // Load markers and meta data from replay file
ReplayMetaData metaData; ReplayMetaData metaData;
Set<Marker> markers; Set<Marker> markers;
@@ -196,6 +210,6 @@ public class GuiTrimPanel extends GuiPanel {
} }
private void updateReadyState() { private void updateReadyState() {
gui.saveButton.setEnabled(inputReplays.getSelectedValue() != NO_REPLAY); gui.saveButton.setEnabled(inputReplay != NO_REPLAY);
} }
} }

View File

@@ -0,0 +1,180 @@
package com.replaymod.editor.gui;
import com.replaymod.replaystudio.PacketData;
import com.replaymod.replaystudio.Studio;
import com.replaymod.replaystudio.data.Marker;
import com.replaymod.replaystudio.filter.SquashFilter;
import com.replaymod.replaystudio.filter.StreamFilter;
import com.replaymod.replaystudio.io.ReplayInputStream;
import com.replaymod.replaystudio.io.ReplayOutputStream;
import com.replaymod.replaystudio.replay.ReplayMetaData;
import com.replaymod.replaystudio.replay.ZipReplayFile;
import com.replaymod.replaystudio.stream.IteratorStream;
import com.replaymod.replaystudio.studio.ReplayStudio;
import com.replaymod.replaystudio.util.Utils;
import org.apache.commons.io.FilenameUtils;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
public class MarkerProcessor {
/**
* Signals the start of a section which is to be cut from the replay.
*/
public static final String MARKER_NAME_START_CUT = "_RM_START_CUT";
/**
* Signals the end of a section which is to be cut from the replay.
*/
public static final String MARKER_NAME_END_CUT = "_RM_END_CUT";
/**
* Signals the point at which a replay is to be split into multiple replays.
* If it is found in a section which is to be cut, that section will be cut from both replays.
*/
public static final String MARKER_NAME_SPLIT = "_RM_SPLIT";
private static boolean hasWork(Path path) throws IOException {
try (ZipReplayFile inputReplayFile = new ZipReplayFile(new ReplayStudio(), path.toFile())) {
return inputReplayFile.getMarkers().or(HashSet::new).stream().anyMatch(m -> m.getName().startsWith("_RM_"));
}
}
public static void apply(Path path, Consumer<Float> progress) throws IOException {
if (!hasWork(path)) {
return;
}
String replayName = FilenameUtils.getBaseName(path.getFileName().toString());
int splitCounter = 0;
Studio studio = new ReplayStudio();
SquashFilter squashFilter = new SquashFilter();
squashFilter.init(studio, null);
Path inputPath = path.resolveSibling("raw").resolve(path.getFileName());
Files.createDirectories(inputPath.getParent());
Files.move(path, inputPath);
try (ZipReplayFile inputReplayFile = new ZipReplayFile(studio, inputPath.toFile())) {
List<Marker> markers = inputReplayFile.getMarkers().or(HashSet::new)
.stream().sorted(Comparator.comparing(Marker::getTime)).collect(Collectors.toList());
Iterator<Marker> markerIterator = markers.iterator();
boolean anySplit = markers.stream().anyMatch(m -> m.getName().equals(MARKER_NAME_SPLIT));
int inputDuration = inputReplayFile.getMetaData().getDuration();
ReplayInputStream replayInputStream = inputReplayFile.getPacketData(studio, true);
int timeOffset = 0;
SquashFilter cutFilter = null;
int startCutOffset = 0;
PacketData nextPacket = replayInputStream.readPacket();
Marker nextMarker = markerIterator.next();
while (nextPacket != null) {
try (ZipReplayFile outputReplayFile = new ZipReplayFile(studio, null,
(anySplit ? path.resolveSibling(replayName + "_" + splitCounter + ".mcpr") : path).toFile())) {
long duration = 0;
Set<Marker> outputMarkers = new HashSet<>();
ReplayMetaData metaData = inputReplayFile.getMetaData();
metaData.setDate(metaData.getDate() + timeOffset);
try (ReplayOutputStream replayOutputStream = outputReplayFile.writePacketData(true)) {
if (cutFilter != null) {
cutFilter = squashFilter;
} else if (splitCounter > 0) {
List<PacketData> packets = new ArrayList<>();
squashFilter.onEnd(
new IteratorStream(packets.listIterator(), (StreamFilter) null),
timeOffset
);
for (PacketData packet : packets) {
replayOutputStream.write(0, packet.getPacket());
}
}
while (nextPacket != null) {
if (nextMarker != null && nextPacket.getTime() > nextMarker.getTime()) {
if (MARKER_NAME_START_CUT.equals(nextMarker.getName())) {
startCutOffset = nextMarker.getTime();
cutFilter = new SquashFilter();
} else if (MARKER_NAME_END_CUT.equals(nextMarker.getName())) {
timeOffset += nextMarker.getTime() - startCutOffset;
if (cutFilter != null) {
List<PacketData> packets = new ArrayList<>();
cutFilter.onEnd(
new IteratorStream(packets.listIterator(), (StreamFilter) null),
nextMarker.getTime()
);
for (PacketData packet : packets) {
replayOutputStream.write(nextMarker.getTime() - timeOffset, packet.getPacket());
}
cutFilter = null;
}
} else if (MARKER_NAME_SPLIT.equals(nextMarker.getName())) {
splitCounter++;
timeOffset = nextMarker.getTime();
startCutOffset = timeOffset;
nextMarker = markerIterator.hasNext() ? markerIterator.next() : null;
break;
} else {
nextMarker.setTime(nextMarker.getTime() - timeOffset);
outputMarkers.add(nextMarker);
}
nextMarker = markerIterator.hasNext() ? markerIterator.next() : null;
continue;
}
squashFilter.onPacket(null, nextPacket);
if (cutFilter != null) {
if (cutFilter != squashFilter) {
cutFilter.onPacket(null, nextPacket);
}
} else {
replayOutputStream.write(nextPacket.getTime() - timeOffset, nextPacket.getPacket());
duration = nextPacket.getTime() - timeOffset;
}
nextPacket = replayInputStream.readPacket();
if (nextPacket != null) {
progress.accept((float) nextPacket.getTime() / (float) inputDuration);
} else {
progress.accept(1f);
}
}
}
metaData.setDuration((int) duration);
outputReplayFile.writeMetaData(metaData);
outputReplayFile.writeMarkers(outputMarkers);
outputReplayFile.writeModInfo(inputReplayFile.getModInfo());
// We could filter these but ReplayStudio doesn't yet provide a nice method for determining
// which ones are used.
Map<Integer, String> resourcePackIndex = inputReplayFile.getResourcePackIndex();
if (resourcePackIndex != null) {
outputReplayFile.writeResourcePackIndex(resourcePackIndex);
for (String hash : resourcePackIndex.values()) {
try (InputStream in = inputReplayFile.getResourcePack(hash).get();
OutputStream out = outputReplayFile.writeResourcePack(hash)) {
Utils.copy(in, out);
}
}
}
outputReplayFile.save();
}
}
}
}
}

View File

@@ -1,55 +1,55 @@
package com.replaymod.editor.handler; package com.replaymod.editor.handler;
import com.replaymod.core.utils.Utils;
import com.replaymod.editor.ReplayModEditor; import com.replaymod.editor.ReplayModEditor;
import com.replaymod.editor.gui.GuiReplayEditor; import com.replaymod.editor.gui.GuiEditReplay;
import com.replaymod.replay.gui.screen.GuiReplayViewer;
import de.johni0702.minecraft.gui.container.AbstractGuiScreen;
import de.johni0702.minecraft.gui.container.GuiScreen; import de.johni0702.minecraft.gui.container.GuiScreen;
import net.minecraft.client.gui.GuiButton; import de.johni0702.minecraft.gui.element.GuiButton;
import net.minecraft.client.gui.GuiMainMenu; import net.minecraft.crash.CrashReport;
import net.minecraft.client.resources.I18n;
import net.minecraftforge.client.event.GuiScreenEvent; import net.minecraftforge.client.event.GuiScreenEvent;
//#if MC>=10800 //#if MC>=10800
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; //#if MC>=11300
import net.minecraftforge.eventbus.api.SubscribeEvent;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#endif
//#else //#else
//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent; //$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
//#endif //#endif
import java.io.IOException;
import static com.replaymod.core.versions.MCVer.*; import static com.replaymod.core.versions.MCVer.*;
public class GuiHandler { public class GuiHandler {
private static final int BUTTON_REPLAY_EDITOR = 17890237;
private final ReplayModEditor mod;
public GuiHandler(ReplayModEditor mod) {
this.mod = mod;
}
public void register() { public void register() {
FML_BUS.register(this); FML_BUS.register(this);
FORGE_BUS.register(this); FORGE_BUS.register(this);
} }
@SubscribeEvent @SubscribeEvent
public void injectIntoMainMenu(GuiScreenEvent.InitGuiEvent event) { public void injectIntoReplayViewer(GuiScreenEvent.InitGuiEvent.Post event) {
if (!(getGui(event) instanceof GuiMainMenu)) { AbstractGuiScreen guiScreen = GuiScreen.from(getGui(event));
if (!(guiScreen instanceof GuiReplayViewer)) {
return; return;
} }
final GuiReplayViewer replayViewer = (GuiReplayViewer) guiScreen;
GuiButton button = new GuiButton(BUTTON_REPLAY_EDITOR, getGui(event).width / 2 + 2, // Inject Edit button
getGui(event).height / 4 + 10 + 3 * 24, I18n.format("replaymod.gui.replayeditor")); replayViewer.replaySpecificButtons.add(new GuiButton(replayViewer.editorButton).onClick(() -> {
button.width = button.width / 2 - 2; try {
getButtonList(event).add(button); new GuiEditReplay(replayViewer, replayViewer.list.getSelected().file.toPath()) {
} @Override
protected void close() {
@SubscribeEvent super.close();
public void onButton(GuiScreenEvent.ActionPerformedEvent.Pre event) { replayViewer.list.load();
if(!getButton(event).enabled) return; }
}.open();
if (getGui(event) instanceof GuiMainMenu) { } catch (IOException e) {
if (getButton(event).id == BUTTON_REPLAY_EDITOR) { Utils.error(ReplayModEditor.LOGGER, replayViewer, CrashReport.makeCrashReport(e, "Opening replay editor"), () -> {});
new GuiReplayEditor(GuiScreen.wrap(getGui(event)), mod.getCore()).display();
} }
} }).setSize(73, 20).setI18nLabel("replaymod.gui.edit").setDisabled());
} }
} }

View File

@@ -8,11 +8,21 @@ import com.replaymod.replay.gui.overlay.GuiReplayOverlay;
import de.johni0702.minecraft.gui.element.GuiImage; import de.johni0702.minecraft.gui.element.GuiImage;
import de.johni0702.minecraft.gui.element.IGuiImage; import de.johni0702.minecraft.gui.element.IGuiImage;
import de.johni0702.minecraft.gui.layout.HorizontalLayout; import de.johni0702.minecraft.gui.layout.HorizontalLayout;
import net.minecraft.client.settings.GameSettings;
import org.lwjgl.input.Keyboard; //#if MC>=11300
import com.replaymod.core.versions.MCVer.Keyboard;
import net.minecraft.client.GameSettings;
//#else
//$$ import net.minecraft.client.settings.GameSettings;
//$$ import org.lwjgl.input.Keyboard;
//#endif
//#if MC>=10800 //#if MC>=10800
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; //#if MC>=11300
import net.minecraftforge.eventbus.api.SubscribeEvent;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#endif
import net.minecraftforge.fml.common.gameevent.TickEvent; import net.minecraftforge.fml.common.gameevent.TickEvent;
//#else //#else
//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent; //$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
@@ -28,7 +38,11 @@ public class FullBrightness implements Extra {
private GameSettings gameSettings; private GameSettings gameSettings;
private boolean active; private boolean active;
private float originalGamma; //#if MC>=11300
private double originalGamma;
//#else
//$$ private float originalGamma;
//#endif
@Override @Override
public void register(final ReplayMod mod) throws Exception { public void register(final ReplayMod mod) throws Exception {
@@ -39,7 +53,11 @@ public class FullBrightness implements Extra {
@Override @Override
public void run() { public void run() {
active = !active; active = !active;
mod.getMinecraft().entityRenderer.lightmapUpdateNeeded = true; //#if MC>=11300
mod.getMinecraft().entityRenderer.tick(); // need to tick once to update lightmap when replay is paused
//#else
//$$ mod.getMinecraft().entityRenderer.lightmapUpdateNeeded = true;
//#endif
ReplayHandler replayHandler = module.getReplayHandler(); ReplayHandler replayHandler = module.getReplayHandler();
if (replayHandler != null) { if (replayHandler != null) {
updateIndicator(replayHandler.getOverlay()); updateIndicator(replayHandler.getOverlay());

View File

@@ -16,13 +16,21 @@ import de.johni0702.minecraft.gui.layout.CustomLayout;
import de.johni0702.minecraft.gui.layout.GridLayout; import de.johni0702.minecraft.gui.layout.GridLayout;
import de.johni0702.minecraft.gui.layout.HorizontalLayout; import de.johni0702.minecraft.gui.layout.HorizontalLayout;
import de.johni0702.minecraft.gui.layout.LayoutData; import de.johni0702.minecraft.gui.layout.LayoutData;
import de.johni0702.minecraft.gui.utils.lwjgl.Dimension;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import net.minecraft.client.resources.I18n; import net.minecraft.client.resources.I18n;
import org.lwjgl.input.Keyboard;
import org.lwjgl.util.Dimension; //#if MC>=11300
import org.lwjgl.util.ReadableDimension; //#else
//$$ import org.lwjgl.input.Keyboard;
//#endif
//#if MC>=10800 //#if MC>=10800
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; //#if MC>=11300
import net.minecraftforge.eventbus.api.SubscribeEvent;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#endif
//#else //#else
//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent; //$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
//#endif //#endif
@@ -89,7 +97,11 @@ public class HotkeyButtons implements Extra {
// There doesn't seem to be an KeyBindingUpdate event, so we'll just update it every time // There doesn't seem to be an KeyBindingUpdate event, so we'll just update it every time
String keyName = "???"; String keyName = "???";
try { try {
keyName = Keyboard.getKeyName(keyBinding.getKeyCode()); //#if MC>=11300
keyName = keyBinding.func_197978_k();
//#else
//$$ keyName = Keyboard.getKeyName(keyBinding.getKeyCode());
//#endif
} catch (ArrayIndexOutOfBoundsException e) { } catch (ArrayIndexOutOfBoundsException e) {
// Apparently windows likes to press strange keys, see https://www.replaymod.com/forum/thread/55 // Apparently windows likes to press strange keys, see https://www.replaymod.com/forum/thread/55
} }

View File

@@ -1,134 +0,0 @@
package com.replaymod.extras;
import com.google.common.collect.ImmutableSet;
import com.replaymod.compat.optifine.OptifineReflection;
import com.replaymod.core.ReplayMod;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.IResourcePack;
import net.minecraft.client.resources.data.IMetadataSection;
import net.minecraft.util.ResourceLocation;
import org.apache.commons.io.IOUtils;
//#if MC>=10904
import net.minecraft.client.resources.data.MetadataSerializer;
//#else
//$$ import net.minecraft.client.resources.data.IMetadataSerializer;
//#endif
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import static com.replaymod.extras.ReplayModExtras.LOGGER;
public class LocalizationExtra implements Extra {
private static final String ZIP_FILE_URL = "https://github.com/ReplayMod/Translations/archive/master.zip";
private static final String LANG_PREFIX = "Translations-master/";
@Override
public void register(ReplayMod mod) throws Exception {
final Minecraft mc = mod.getMinecraft();
if (Boolean.parseBoolean(System.getProperty("replaymod.offline", "false"))) {
return;
}
Thread localizedResourcePackLoader = new Thread(() -> {
try {
// Download zip of lang files
LOGGER.debug("Downloading languages from {}", ZIP_FILE_URL);
Map<String, byte[]> languages = new HashMap<>();
try (InputStream urlIn = new URL(ZIP_FILE_URL).openStream();
ZipInputStream in = new ZipInputStream(urlIn)) {
ZipEntry entry;
while ((entry = in.getNextEntry()) != null) {
String name = entry.getName();
if (!name.startsWith(LANG_PREFIX) || !name.endsWith(".lang")) {
continue;
}
name = name.substring(LANG_PREFIX.length());
languages.put(name, IOUtils.toByteArray(in));
LOGGER.debug("Added language file {}", name);
}
}
LOGGER.debug("Downloaded {} languages", languages.size());
// Add lang files as resource pack
mc.addScheduledTask(() -> {
@SuppressWarnings("unchecked")
List<IResourcePack> defaultResourcePacks = mc.defaultResourcePacks;
defaultResourcePacks.add(new LocalizedResourcePack(languages));
mc.getLanguageManager().onResourceManagerReload(mc.getResourceManager());
// Optifine does its own, additional language data loading
OptifineReflection.reloadLang();
LOGGER.debug("Added language files to resource packs and reloaded LanguageManager");
});
} catch (Throwable t) {
LOGGER.error("Loading localized resource pack:", t);
}
}, "localizedResourcePackLoader");
localizedResourcePackLoader.setDaemon(true);
localizedResourcePackLoader.start();
}
public static class LocalizedResourcePack implements IResourcePack {
private final Pattern LANG_PATTERN = Pattern.compile("^lang/([.+].lang)$");
private final Map<String, byte[]> languages;
public LocalizedResourcePack(Map<String, byte[]> languages) {
this.languages = languages;
}
@Override
public InputStream getInputStream(ResourceLocation loc) {
if (!"replaymod".equals(loc.getResourceDomain())) return null;
Matcher matcher = LANG_PATTERN.matcher(loc.getResourcePath());
if (matcher.matches()) {
byte[] bytes = languages.get(matcher.group());
if (bytes != null) {
return new ByteArrayInputStream(bytes);
}
}
return null;
}
@Override
public boolean resourceExists(ResourceLocation loc) {
// Assumes that getInputStream returns a ByteArrayInputStream that doesn't need to be closed
return getInputStream(loc) != null;
}
@Override
public Set<String> getResourceDomains() {
return ImmutableSet.of("replaymod");
}
//#if MC>=10904
@Override
public <T extends IMetadataSection> T getPackMetadata(MetadataSerializer metadataSerializer, String metadataSectionName) throws IOException {
//#else
//$$ @Override
//$$ public IMetadataSection getPackMetadata(IMetadataSerializer metadataSerializer, String metadataSectionName) throws IOException {
//#endif
return null;
}
@Override
public BufferedImage getPackImage() throws IOException {
return null;
}
@Override
public String getPackName() {
return "ReplayModLocalizationResourcePack";
}
}
}

View File

@@ -1,7 +1,7 @@
package com.replaymod.extras; package com.replaymod.extras;
import com.replaymod.core.ReplayMod; import com.replaymod.core.ReplayMod;
import com.replaymod.core.Setting; import com.replaymod.core.versions.MCVer;
import de.johni0702.minecraft.gui.container.AbstractGuiScreen; import de.johni0702.minecraft.gui.container.AbstractGuiScreen;
import de.johni0702.minecraft.gui.container.GuiContainer; import de.johni0702.minecraft.gui.container.GuiContainer;
import de.johni0702.minecraft.gui.container.GuiPanel; import de.johni0702.minecraft.gui.container.GuiPanel;
@@ -16,11 +16,15 @@ import de.johni0702.minecraft.gui.popup.AbstractGuiPopup;
import de.johni0702.minecraft.gui.utils.Colors; import de.johni0702.minecraft.gui.utils.Colors;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
//#if MC>=11300
import net.minecraftforge.fml.ModList;
//#else
//#if MC>=10800 //#if MC>=10800
import net.minecraftforge.fml.common.Loader; //$$ import net.minecraftforge.fml.common.Loader;
//#else //#else
//$$ import cpw.mods.fml.common.Loader; //$$ import cpw.mods.fml.common.Loader;
//#endif //#endif
//#endif
import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.HttpsURLConnection;
import java.io.File; import java.io.File;
@@ -34,17 +38,20 @@ import static com.replaymod.core.utils.Utils.SSL_SOCKET_FACTORY;
import static com.replaymod.extras.ReplayModExtras.LOGGER; import static com.replaymod.extras.ReplayModExtras.LOGGER;
public class OpenEyeExtra implements Extra { public class OpenEyeExtra implements Extra {
private static final String DOWNLOAD_URL = "https://www.replaymod.com/dl/openeye/" + Loader.MC_VERSION; private static final String DOWNLOAD_URL = "https://www.replaymod.com/dl/openeye/" + ReplayMod.getMinecraftVersion();
private static final Setting<Boolean> ASK_FOR_OPEN_EYE = new Setting<>("advanced", "askForOpenEye", null, true);
private ReplayMod mod; private ReplayMod mod;
@Override @Override
public void register(ReplayMod mod) throws Exception { public void register(ReplayMod mod) throws Exception {
this.mod = mod; this.mod = mod;
mod.getSettingsRegistry().register(ASK_FOR_OPEN_EYE);
if (!Loader.isModLoaded("OpenEye") && mod.getSettingsRegistry().get(ASK_FOR_OPEN_EYE)) { //#if MC>=11300
boolean isOpenEyeLoaded = ModList.get().isLoaded("openeye");
//#else
//$$ boolean isOpenEyeLoaded = Loader.isModLoaded("OpenEye");
//#endif
if (!isOpenEyeLoaded && mod.getSettingsRegistry().get(Setting.ASK_FOR_OPEN_EYE)) {
new Thread(() -> { new Thread(() -> {
try { try {
LOGGER.trace("Checking for OpenEye availability"); LOGGER.trace("Checking for OpenEye availability");
@@ -93,7 +100,7 @@ public class OpenEyeExtra implements Extra {
GuiPopup popup = new GuiPopup(OfferGui.this); GuiPopup popup = new GuiPopup(OfferGui.this);
new Thread(() -> { new Thread(() -> {
try { try {
File targetFile = new File(mod.getMinecraft().mcDataDir, "mods/" + Loader.MC_VERSION + "/OpenEye.jar"); File targetFile = new File(MCVer.mcDataDir(mod.getMinecraft()), "mods/" + ReplayMod.getMinecraftVersion() + "/OpenEye.jar");
FileUtils.forceMkdir(targetFile.getParentFile()); FileUtils.forceMkdir(targetFile.getParentFile());
HttpsURLConnection connection = (HttpsURLConnection) new URL(DOWNLOAD_URL).openConnection(); HttpsURLConnection connection = (HttpsURLConnection) new URL(DOWNLOAD_URL).openConnection();
@@ -112,7 +119,7 @@ public class OpenEyeExtra implements Extra {
}).start(); }).start();
}); });
noButton.onClick(() -> { noButton.onClick(() -> {
mod.getSettingsRegistry().set(ASK_FOR_OPEN_EYE, false); mod.getSettingsRegistry().set(Setting.ASK_FOR_OPEN_EYE, false);
mod.getSettingsRegistry().save(); mod.getSettingsRegistry().save();
parent.display(); parent.display();
}); });

View File

@@ -1,40 +1,22 @@
package com.replaymod.extras; package com.replaymod.extras;
import com.replaymod.core.Module;
import com.replaymod.core.ReplayMod; import com.replaymod.core.ReplayMod;
import com.replaymod.extras.advancedscreenshots.AdvancedScreenshots; import com.replaymod.extras.advancedscreenshots.AdvancedScreenshots;
import com.replaymod.extras.playeroverview.PlayerOverview; import com.replaymod.extras.playeroverview.PlayerOverview;
import com.replaymod.extras.urischeme.UriSchemeExtra; import com.replaymod.extras.urischeme.UriSchemeExtra;
import com.replaymod.extras.youtube.YoutubeUpload; import com.replaymod.extras.youtube.YoutubeUpload;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
//#if MC>=10800
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
//#else
//$$ import cpw.mods.fml.common.Mod;
//$$ import cpw.mods.fml.common.event.FMLInitializationEvent;
//$$ import cpw.mods.fml.common.event.FMLPreInitializationEvent;
//#endif
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
@Mod(modid = ReplayModExtras.MOD_ID, public class ReplayModExtras implements Module {
version = "@MOD_VERSION@", { instance = this; }
acceptedMinecraftVersions = "@MC_VERSION@",
acceptableRemoteVersions = "*",
//#if MC>=10800
clientSideOnly = true,
//#endif
useMetadata = true)
public class ReplayModExtras {
public static final String MOD_ID = "replaymod-extras";
@Mod.Instance(MOD_ID)
public static ReplayModExtras instance; public static ReplayModExtras instance;
private static final List<Class<? extends Extra>> builtin = Arrays.asList( private static final List<Class<? extends Extra>> builtin = Arrays.asList(
@@ -44,22 +26,19 @@ public class ReplayModExtras {
YoutubeUpload.class, YoutubeUpload.class,
FullBrightness.class, FullBrightness.class,
HotkeyButtons.class, HotkeyButtons.class,
LocalizationExtra.class,
OpenEyeExtra.class OpenEyeExtra.class
); );
private final Map<Class<? extends Extra>, Extra> instances = new HashMap<>(); private final Map<Class<? extends Extra>, Extra> instances = new HashMap<>();
public static Logger LOGGER; public static Logger LOGGER = LogManager.getLogger();
@Mod.EventHandler public ReplayModExtras(ReplayMod core) {
public void preInit(FMLPreInitializationEvent event) { core.getSettingsRegistry().register(Setting.class);
LOGGER = event.getModLog();
ReplayMod.instance.getSettingsRegistry().register(Setting.class);
} }
@Mod.EventHandler @Override
public void init(FMLInitializationEvent event) { public void initClient() {
for (Class<? extends Extra> cls : builtin) { for (Class<? extends Extra> cls : builtin) {
try { try {
Extra extra = cls.newInstance(); Extra extra = cls.newInstance();

View File

@@ -3,6 +3,8 @@ package com.replaymod.extras;
import com.replaymod.core.SettingsRegistry; import com.replaymod.core.SettingsRegistry;
public final class Setting<T> { public final class Setting<T> {
public static final SettingsRegistry.SettingKey<Boolean> ASK_FOR_OPEN_EYE =
new SettingsRegistry.SettingKeys<>("advanced", "askForOpenEye", null, true);
public static final SettingsRegistry.SettingKey<Boolean> SKIP_POST_SCREENSHOT_GUI = public static final SettingsRegistry.SettingKey<Boolean> SKIP_POST_SCREENSHOT_GUI =
new SettingsRegistry.SettingKeys<>("advanced", "skipPostScreenshotGui", null, false); new SettingsRegistry.SettingKeys<>("advanced", "skipPostScreenshotGui", null, false);
} }

View File

@@ -2,14 +2,22 @@ package com.replaymod.extras.advancedscreenshots;
import com.replaymod.core.ReplayMod; import com.replaymod.core.ReplayMod;
import com.replaymod.extras.Extra; import com.replaymod.extras.Extra;
import com.replaymod.replay.events.ReplayDispatchKeypressesEvent;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiControls;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import org.lwjgl.input.Keyboard;
//#if MC<11300
//$$ import com.replaymod.core.versions.MCVer;
//$$ import com.replaymod.replay.events.ReplayDispatchKeypressesEvent;
//$$ import net.minecraft.client.Minecraft;
//$$ import net.minecraft.client.gui.GuiControls;
//$$ import net.minecraftforge.client.event.GuiScreenEvent;
//$$ import org.lwjgl.input.Keyboard;
//#endif
//#if MC>=10800 //#if MC>=10800
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; //#if MC>=11300
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#endif
//#else //#else
//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent; //$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
//#endif //#endif
@@ -18,29 +26,36 @@ public class AdvancedScreenshots implements Extra {
private ReplayMod mod; private ReplayMod mod;
private final Minecraft mc = Minecraft.getMinecraft();
@Override @Override
public void register(ReplayMod mod) throws Exception { public void register(ReplayMod mod) {
this.mod = mod; this.mod = mod;
MinecraftForge.EVENT_BUS.register(this); MinecraftForge.EVENT_BUS.register(this);
} }
@SubscribeEvent //#if MC>=11300
public void onDispatchKeypresses(ReplayDispatchKeypressesEvent.Pre event) { private static AdvancedScreenshots instance; { instance = this; }
int keyCode = Keyboard.getEventKey() == 0 ? Keyboard.getEventCharacter() : Keyboard.getEventKey(); public static void take() {
if (instance != null) {
// all the conditions required to trigger a screenshot condensed in a single if statement instance.takeScreenshot();
if (keyCode != 0 && !Keyboard.isRepeatEvent()
&& (!(mc.currentScreen instanceof GuiControls) || ((GuiControls) mc.currentScreen).time <= mc.getSystemTime() - 20L)
&& Keyboard.getEventKeyState()
&& keyCode == mc.gameSettings.keyBindScreenshot.getKeyCode()) {
ReplayMod.instance.runLater(() -> {
new GuiCreateScreenshot(mod).display();
});
event.setCanceled(true);
} }
} }
//#else
//$$ @SubscribeEvent
//$$ public void onDispatchKeypresses(ReplayDispatchKeypressesEvent.Pre event) {
//$$ Minecraft mc = MCVer.getMinecraft();
//$$ if (mc.currentScreen instanceof GuiControls) return;
//$$ if (!Keyboard.getEventKeyState()) return;
//$$ if (Keyboard.isRepeatEvent()) return;
//$$ int keyCode = Keyboard.getEventKey() == 0 ? Keyboard.getEventCharacter() : Keyboard.getEventKey();
//$$ if (keyCode == 0 || keyCode != mc.gameSettings.keyBindScreenshot.getKeyCode()) return;
//$$
//$$ takeScreenshot();
//$$
//$$ event.setCanceled(true);
//$$ }
//#endif
private void takeScreenshot() {
ReplayMod.instance.runLater(() -> new GuiCreateScreenshot(mod).display());
}
} }

View File

@@ -1,6 +1,7 @@
package com.replaymod.extras.advancedscreenshots; package com.replaymod.extras.advancedscreenshots;
import com.replaymod.core.ReplayMod; import com.replaymod.core.ReplayMod;
import com.replaymod.core.versions.MCVer;
import com.replaymod.render.RenderSettings; import com.replaymod.render.RenderSettings;
import com.replaymod.render.gui.GuiRenderSettings; import com.replaymod.render.gui.GuiRenderSettings;
import com.replaymod.replay.ReplayModReplay; import com.replaymod.replay.ReplayModReplay;
@@ -12,10 +13,9 @@ import de.johni0702.minecraft.gui.layout.GridLayout;
import de.johni0702.minecraft.gui.layout.VerticalLayout; import de.johni0702.minecraft.gui.layout.VerticalLayout;
import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReport;
import net.minecraft.util.ScreenShotHelper; import net.minecraft.util.ScreenShotHelper;
import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.common.config.Property;
import java.io.File; import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import static com.replaymod.core.utils.Utils.error; import static com.replaymod.core.utils.Utils.error;
@@ -82,13 +82,12 @@ public class GuiCreateScreenshot extends GuiRenderSettings implements Loadable {
@Override @Override
protected File generateOutputFile(RenderSettings.EncodingPreset encodingPreset) { protected File generateOutputFile(RenderSettings.EncodingPreset encodingPreset) {
File screenshotFolder = new File(getMinecraft().mcDataDir, "screenshots"); File screenshotFolder = new File(MCVer.mcDataDir(getMinecraft()), "screenshots");
return ScreenShotHelper.getTimestampedPNGFileForDirectory(screenshotFolder); return ScreenShotHelper.getTimestampedPNGFileForDirectory(screenshotFolder);
} }
@Override @Override
protected Property getConfigProperty(Configuration configuration) { protected Path getSettingsPath() {
return configuration.get("screenshotsettings", "settings", "{}", return MCVer.mcDataDir(getMinecraft()).toPath().resolve("config/replaymod-screenshotsettings.json");
"Last state of the screenshot settings GUI. Internal use only.");
} }
} }

View File

@@ -12,7 +12,7 @@ import de.johni0702.minecraft.gui.element.GuiLabel;
import de.johni0702.minecraft.gui.layout.HorizontalLayout; import de.johni0702.minecraft.gui.layout.HorizontalLayout;
import de.johni0702.minecraft.gui.layout.VerticalLayout; import de.johni0702.minecraft.gui.layout.VerticalLayout;
import de.johni0702.minecraft.gui.popup.AbstractGuiPopup; import de.johni0702.minecraft.gui.popup.AbstractGuiPopup;
import org.lwjgl.util.ReadableColor; import de.johni0702.minecraft.gui.utils.lwjgl.ReadableColor;
import java.awt.*; import java.awt.*;
import java.io.IOException; import java.io.IOException;

View File

@@ -1,14 +1,15 @@
package com.replaymod.extras.advancedscreenshots; package com.replaymod.extras.advancedscreenshots;
import com.replaymod.core.versions.MCVer;
import com.replaymod.render.RenderSettings; import com.replaymod.render.RenderSettings;
import com.replaymod.render.blend.BlendState; import com.replaymod.render.blend.BlendState;
import com.replaymod.render.capturer.RenderInfo; import com.replaymod.render.capturer.RenderInfo;
import com.replaymod.render.rendering.Pipelines; import com.replaymod.render.rendering.Pipelines;
import de.johni0702.minecraft.gui.utils.lwjgl.Dimension;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReport;
import org.lwjgl.util.Dimension;
import org.lwjgl.util.ReadableDimension;
//#if MC>=10800 //#if MC>=10800
import com.replaymod.render.hooks.ChunkLoadingRenderGlobal; import com.replaymod.render.hooks.ChunkLoadingRenderGlobal;
@@ -17,7 +18,7 @@ import com.replaymod.render.hooks.ChunkLoadingRenderGlobal;
@RequiredArgsConstructor @RequiredArgsConstructor
public class ScreenshotRenderer implements RenderInfo { public class ScreenshotRenderer implements RenderInfo {
private final Minecraft mc = Minecraft.getMinecraft(); private final Minecraft mc = MCVer.getMinecraft();
private final RenderSettings settings; private final RenderSettings settings;
@@ -25,8 +26,13 @@ public class ScreenshotRenderer implements RenderInfo {
public boolean renderScreenshot() throws Throwable { public boolean renderScreenshot() throws Throwable {
try { try {
int displayWidthBefore = mc.displayWidth; //#if MC>=11300
int displayHeightBefore = mc.displayHeight; int displayWidthBefore = mc.mainWindow.framebufferWidth;
int displayHeightBefore = mc.mainWindow.framebufferHeight;
//#else
//$$ int displayWidthBefore = mc.displayWidth;
//$$ int displayHeightBefore = mc.displayHeight;
//#endif
boolean hideGUIBefore = mc.gameSettings.hideGUI; boolean hideGUIBefore = mc.gameSettings.hideGUI;
mc.gameSettings.hideGUI = true; mc.gameSettings.hideGUI = true;
@@ -48,12 +54,18 @@ public class ScreenshotRenderer implements RenderInfo {
//#endif //#endif
mc.gameSettings.hideGUI = hideGUIBefore; mc.gameSettings.hideGUI = hideGUIBefore;
mc.resize(displayWidthBefore, displayHeightBefore); //#if MC>=11300
mc.mainWindow.framebufferWidth = displayWidthBefore;
mc.mainWindow.framebufferHeight = displayHeightBefore;
mc.getFramebuffer().createBindFramebuffer(displayWidthBefore, displayHeightBefore);
//#else
//$$ mc.resize(displayWidthBefore, displayHeightBefore);
//#endif
return true; return true;
} catch (OutOfMemoryError e) { } catch (OutOfMemoryError e) {
e.printStackTrace(); e.printStackTrace();
CrashReport report = CrashReport.makeCrashReport(e, "Creating Equirectangular Screenshot"); CrashReport report = CrashReport.makeCrashReport(e, "Creating Equirectangular Screenshot");
Minecraft.getMinecraft().crashed(report); MCVer.getMinecraft().crashed(report);
} }
return false; return false;
} }

View File

@@ -2,13 +2,13 @@ package com.replaymod.extras.advancedscreenshots;
import com.replaymod.core.ReplayMod; import com.replaymod.core.ReplayMod;
import com.replaymod.core.utils.Utils; import com.replaymod.core.utils.Utils;
import com.replaymod.core.versions.MCVer;
import com.replaymod.extras.ReplayModExtras; import com.replaymod.extras.ReplayModExtras;
import com.replaymod.render.frame.RGBFrame; import com.replaymod.render.frame.RGBFrame;
import com.replaymod.render.rendering.FrameConsumer; import com.replaymod.render.rendering.FrameConsumer;
import com.replaymod.replay.ReplayModReplay; import com.replaymod.replay.ReplayModReplay;
import net.minecraft.client.Minecraft; import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReport;
import org.lwjgl.util.ReadableDimension;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
@@ -48,7 +48,7 @@ public class ScreenshotWriter implements FrameConsumer<RGBFrame> {
} catch (OutOfMemoryError e) { } catch (OutOfMemoryError e) {
e.printStackTrace(); e.printStackTrace();
CrashReport report = CrashReport.makeCrashReport(e, "Exporting frame"); CrashReport report = CrashReport.makeCrashReport(e, "Exporting frame");
Minecraft.getMinecraft().crashed(report); MCVer.getMinecraft().crashed(report);
} catch (Throwable t) { } catch (Throwable t) {
CrashReport report = CrashReport.makeCrashReport(t, "Exporting frame"); CrashReport report = CrashReport.makeCrashReport(t, "Exporting frame");

View File

@@ -11,11 +11,19 @@ import com.replaymod.replay.events.ReplayOpenEvent;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraftforge.client.event.RenderHandEvent; import net.minecraftforge.client.event.RenderHandEvent;
import org.lwjgl.input.Keyboard;
//#if MC>=11300
//#else
//$$ import org.lwjgl.input.Keyboard;
//#endif
//#if MC>=10800 //#if MC>=10800
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; //#if MC>=11300
import net.minecraftforge.eventbus.api.SubscribeEvent;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#endif
//#else //#else
//$$ import cpw.mods.fml.common.eventhandler.EventPriority; //$$ import cpw.mods.fml.common.eventhandler.EventPriority;
//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent; //$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent;

View File

@@ -18,15 +18,13 @@ import de.johni0702.minecraft.gui.function.Closeable;
import de.johni0702.minecraft.gui.layout.CustomLayout; import de.johni0702.minecraft.gui.layout.CustomLayout;
import de.johni0702.minecraft.gui.layout.HorizontalLayout; import de.johni0702.minecraft.gui.layout.HorizontalLayout;
import de.johni0702.minecraft.gui.utils.Colors; import de.johni0702.minecraft.gui.utils.Colors;
import net.minecraft.client.audio.PositionedSoundRecord; import de.johni0702.minecraft.gui.utils.lwjgl.Dimension;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import org.lwjgl.util.Dimension;
import org.lwjgl.util.ReadableDimension;
//#if MC>=10904 //#if MC>=10904
import net.minecraft.init.MobEffects; import net.minecraft.init.MobEffects;
import net.minecraft.init.SoundEvents;
//#else //#else
//$$ import net.minecraft.potion.Potion; //$$ import net.minecraft.potion.Potion;
//#endif //#endif
@@ -55,30 +53,12 @@ public class PlayerOverviewGui extends GuiScreen implements Closeable {
public final GuiCheckbox checkAll = new GuiCheckbox(contentPanel){ public final GuiCheckbox checkAll = new GuiCheckbox(contentPanel){
@Override @Override
public void onClick() { public void onClick() {
//#if MC>=10904
getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(SoundEvents.UI_BUTTON_CLICK, 1.0F));
//#else
//#if MC>=10800
//$$ getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.create(BUTTON_SOUND, 1.0F));
//#else
//$$ getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.createPositionedSoundRecord(BUTTON_SOUND, 1.0F));
//#endif
//#endif
playersScrollable.forEach(IGuiCheckbox.class).setChecked(true); playersScrollable.forEach(IGuiCheckbox.class).setChecked(true);
} }
}.setLabel("").setChecked(true).setTooltip(new GuiTooltip().setI18nText("replaymod.gui.playeroverview.showall")); }.setLabel("").setChecked(true).setTooltip(new GuiTooltip().setI18nText("replaymod.gui.playeroverview.showall"));
public final GuiCheckbox uncheckAll = new GuiCheckbox(contentPanel){ public final GuiCheckbox uncheckAll = new GuiCheckbox(contentPanel){
@Override @Override
public void onClick() { public void onClick() {
//#if MC>=10904
getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(SoundEvents.UI_BUTTON_CLICK, 1.0F));
//#else
//#if MC>=10800
//$$ getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.create(BUTTON_SOUND, 1.0F));
//#else
//$$ getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.createPositionedSoundRecord(BUTTON_SOUND, 1.0F));
//#endif
//#endif
playersScrollable.forEach(IGuiCheckbox.class).setChecked(false); playersScrollable.forEach(IGuiCheckbox.class).setChecked(false);
} }
}.setLabel("").setChecked(false).setTooltip(new GuiTooltip().setI18nText("replaymod.gui.playeroverview.hideall")); }.setLabel("").setChecked(false).setTooltip(new GuiTooltip().setI18nText("replaymod.gui.playeroverview.hideall"));
@@ -135,11 +115,15 @@ public class PlayerOverviewGui extends GuiScreen implements Closeable {
} }
}.setSize(16, 16), }.setSize(16, 16),
new GuiLabel().setText( new GuiLabel().setText(
//#if MC>=11300
p.getName().getFormattedText()
//#else
//#if MC>=10800 //#if MC>=10800
p.getName() //$$ p.getName()
//#else //#else
//$$ p.getDisplayName() //$$ p.getDisplayName()
//#endif //#endif
//#endif
).setColor(isSpectator(p) ? Colors.DKGREY : Colors.WHITE) ).setColor(isSpectator(p) ? Colors.DKGREY : Colors.WHITE)
).onClick(new Runnable() { ).onClick(new Runnable() {
@Override @Override
@@ -196,11 +180,15 @@ public class PlayerOverviewGui extends GuiScreen implements Closeable {
public int compare(EntityPlayer o1, EntityPlayer o2) { public int compare(EntityPlayer o1, EntityPlayer o2) {
if (isSpectator(o1) && !isSpectator(o2)) return 1; if (isSpectator(o1) && !isSpectator(o2)) return 1;
if (isSpectator(o2) && !isSpectator(o1)) return -1; if (isSpectator(o2) && !isSpectator(o1)) return -1;
//#if MC>=11300
return o1.getName().getFormattedText().compareToIgnoreCase(o2.getName().getFormattedText());
//#else
//#if MC>=10800 //#if MC>=10800
return o1.getName().compareToIgnoreCase(o2.getName()); //$$ return o1.getName().compareToIgnoreCase(o2.getName());
//#else //#else
//$$ return o1.getDisplayName().compareToIgnoreCase(o2.getDisplayName()); //$$ return o1.getDisplayName().compareToIgnoreCase(o2.getDisplayName());
//#endif //#endif
//#endif
} }
} }
} }

View File

@@ -1,6 +1,5 @@
package com.replaymod.extras.urischeme; package com.replaymod.extras.urischeme;
import com.replaymod.core.ReplayMod;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.StringBuilderWriter; import org.apache.commons.io.output.StringBuilderWriter;
@@ -17,7 +16,7 @@ public class LinuxUriScheme extends UriScheme {
public void install() throws URISyntaxException, IOException { public void install() throws URISyntaxException, IOException {
File file = new File("replaymod.desktop"); File file = new File("replaymod.desktop");
File iconFile = new File("replaymod-icon.jpg"); File iconFile = new File("replaymod-icon.jpg");
String path = ReplayMod.getContainer().getSource().getAbsolutePath().replace("\\", "\\\\").replace("\"", "\\\""); String path = findJarFile().getAbsolutePath().replace("\\", "\\\\").replace("\"", "\\\"");
String content = String content =
"[Desktop Entry]\n" + "[Desktop Entry]\n" +
"Name=ReplayMod\n" + "Name=ReplayMod\n" +

View File

@@ -6,6 +6,9 @@ import javax.swing.*;
import java.io.*; import java.io.*;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.Socket; import java.net.Socket;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.CodeSource;
import java.util.Arrays; import java.util.Arrays;
public abstract class UriScheme { public abstract class UriScheme {
@@ -82,4 +85,19 @@ public abstract class UriScheme {
} }
public abstract void install() throws Exception; public abstract void install() throws Exception;
File findJarFile() throws IOException {
CodeSource codeSource = UriScheme.class.getProtectionDomain().getCodeSource();
if (codeSource != null) {
URL location = codeSource.getLocation();
if (location != null) {
try {
return new File(location.toURI());
} catch (URISyntaxException e) {
throw new IOException(e);
}
}
}
throw new IOException("No jar file found. Is this a development environment?");
}
} }

View File

@@ -1,6 +1,5 @@
package com.replaymod.extras.urischeme; package com.replaymod.extras.urischeme;
import com.replaymod.core.ReplayMod;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.StringBuilderWriter; import org.apache.commons.io.output.StringBuilderWriter;
@@ -9,7 +8,7 @@ import java.io.IOException;
public class WindowsUriScheme extends UriScheme { public class WindowsUriScheme extends UriScheme {
@Override @Override
public void install() throws IOException, InterruptedException { public void install() throws IOException, InterruptedException {
String path = ReplayMod.getContainer().getSource().getAbsolutePath().replace("\\", "\\\\").replace("\"", "\\\""); String path = findJarFile().getAbsolutePath().replace("\\", "\\\\").replace("\"", "\\\"");
regAdd("\\replaymod /f /ve /d \"URL:replaymod Protocol\""); regAdd("\\replaymod /f /ve /d \"URL:replaymod Protocol\"");
regAdd("\\replaymod /f /v \"URL Protocol\" /d \"\""); regAdd("\\replaymod /f /v \"URL Protocol\" /d \"\"");
regAdd("\\replaymod\\shell\\open\\command /f /ve /d \"java -cp \\\"" + path + "\\\" " + UriScheme.class.getName() + " \\\"%1\\\"\""); regAdd("\\replaymod\\shell\\open\\command /f /ve /d \"java -cp \\\"" + path + "\\\" " + UriScheme.class.getName() + " \\\"%1\\\"\"");

View File

@@ -18,12 +18,17 @@ import de.johni0702.minecraft.gui.element.advanced.GuiTextArea;
import de.johni0702.minecraft.gui.layout.CustomLayout; import de.johni0702.minecraft.gui.layout.CustomLayout;
import de.johni0702.minecraft.gui.layout.VerticalLayout; import de.johni0702.minecraft.gui.layout.VerticalLayout;
import de.johni0702.minecraft.gui.popup.GuiFileChooserPopup; import de.johni0702.minecraft.gui.popup.GuiFileChooserPopup;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import joptsimple.internal.Strings; import joptsimple.internal.Strings;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import net.minecraft.client.resources.I18n; import net.minecraft.client.resources.I18n;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.lwjgl.Sys;
import org.lwjgl.util.ReadableDimension; //#if MC>=11300
import net.minecraft.util.Util;
//#else
//$$ import org.lwjgl.Sys;
//#endif
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
@@ -213,7 +218,11 @@ public class GuiYoutubeUpload extends GuiScreen {
try { try {
Desktop.getDesktop().browse(new URL(url).toURI()); Desktop.getDesktop().browse(new URL(url).toURI());
} catch(Throwable throwable) { } catch(Throwable throwable) {
Sys.openURL(url); //#if MC>=11300
Util.getOSType().openURI(url);
//#else
//$$ Sys.openURL(url);
//#endif
} }
upload = null; upload = null;
progressBar.setLabel(I18n.format("replaymod.gui.ytuploadprogress.done", url)); progressBar.setLabel(I18n.format("replaymod.gui.ytuploadprogress.done", url));

View File

@@ -9,7 +9,11 @@ import net.minecraftforge.client.event.GuiScreenEvent;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
//#if MC>=10800 //#if MC>=10800
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; //#if MC>=11300
import net.minecraftforge.eventbus.api.SubscribeEvent;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#endif
//#else //#else
//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent; //$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
//#endif //#endif

View File

@@ -21,8 +21,10 @@ import com.google.api.services.youtube.model.VideoSnippet;
import com.google.api.services.youtube.model.VideoStatus; import com.google.api.services.youtube.model.VideoStatus;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.base.Suppliers; import com.google.common.base.Suppliers;
import com.google.common.io.Files;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture; import com.google.common.util.concurrent.SettableFuture;
import com.replaymod.core.versions.MCVer;
import com.replaymod.render.RenderSettings; import com.replaymod.render.RenderSettings;
import com.replaymod.render.metadata.MetadataInjector; import com.replaymod.render.metadata.MetadataInjector;
import lombok.Getter; import lombok.Getter;
@@ -41,8 +43,7 @@ import static com.replaymod.extras.ReplayModExtras.LOGGER;
public class YoutubeUploader { public class YoutubeUploader {
private static final String CLIENT_ID = "743126594724-mfe7pj1k7e47uu5pk4503c8st9vj9ibu.apps.googleusercontent.com"; private static final String CLIENT_ID = "743126594724-mfe7pj1k7e47uu5pk4503c8st9vj9ibu.apps.googleusercontent.com";
private static final String CLIENT_SECRET = "gMwcy3mRYCRamCIjJIYP7rqc"; private static final String CLIENT_SECRET = "gMwcy3mRYCRamCIjJIYP7rqc";
private static final String FFMPEG_ODS = private static final String FFMPEG_MP4 = "-i %s -c:v libx264 -preset slow -crf 16 %s";
"-i %s -vf scale=iw:iw*9/16,setdar=16:9 -c:v libx264 -preset slow -crf 16 %s";
private static final JsonFactory JSON_FACTORY = new GsonFactory(); private static final JsonFactory JSON_FACTORY = new GsonFactory();
private final NetHttpTransport httpTransport; private final NetHttpTransport httpTransport;
private final DataStoreFactory dataStoreFactory; private final DataStoreFactory dataStoreFactory;
@@ -78,7 +79,7 @@ public class YoutubeUploader {
this.videoVisibility = videoVisibility; this.videoVisibility = videoVisibility;
this.videoSnippet = videoSnippet; this.videoSnippet = videoSnippet;
this.httpTransport = GoogleNetHttpTransport.newTrustedTransport(); this.httpTransport = GoogleNetHttpTransport.newTrustedTransport();
this.dataStoreFactory = new FileDataStoreFactory(minecraft.mcDataDir); this.dataStoreFactory = new FileDataStoreFactory(MCVer.mcDataDir(minecraft));
} }
@@ -131,68 +132,73 @@ public class YoutubeUploader {
} }
private File preUpload() throws InterruptedException, IOException { private File preUpload() throws InterruptedException, IOException {
if (settings.getRenderMethod() == RenderSettings.RenderMethod.ODS) { File outputFile = videoFile;
File tmpFile = new File(videoFile.getParentFile(), System.currentTimeMillis() + ".mp4");
tmpFile.deleteOnExit();
String args = String.format(FFMPEG_ODS, videoFile.getName(), tmpFile.getName()); if (settings.getRenderMethod().isSpherical()) {
// inject spherical metadata for YouTube unless already done
CommandLine commandLine = new CommandLine(settings.getExportCommand()); boolean isMp4 = Files.getFileExtension(outputFile.getName()).equalsIgnoreCase("mp4");
commandLine.addArguments(args);
LOGGER.info("Re-encoding for ODS with {} {}", settings.getExportCommand(), args);
Process process = new ProcessBuilder(commandLine.toStrings()).directory(videoFile.getParentFile()).start();
final AtomicBoolean active = new AtomicBoolean(true); if (!isMp4) {
final InputStream in = process.getErrorStream(); // convert non-mp4 videos into mp4 format to be able to inject metadata
new Thread(() -> { outputFile = new File(outputFile.getParentFile(), System.currentTimeMillis() + ".mp4");
try { outputFile.deleteOnExit();
StringBuilder sb = new StringBuilder();
while (active.get()) { String args = String.format(FFMPEG_MP4, videoFile.getName(), outputFile.getName());
char c = (char) in.read();
if (c == '\r') { CommandLine commandLine = new CommandLine(settings.getExportCommandOrDefault());
String str = sb.toString(); commandLine.addArguments(args);
LOGGER.debug("[FFmpeg] {}", str); LOGGER.info("Re-encoding for metadata injection with {} {}", commandLine.getExecutable(), args);
if (str.startsWith("frame=")) { Process process = new ProcessBuilder(commandLine.toStrings()).directory(outputFile.getParentFile()).start();
str = str.substring(6).trim();
str = str.substring(0, str.indexOf(' ')); final AtomicBoolean active = new AtomicBoolean(true);
double frame = Integer.parseInt(str); final InputStream in = process.getErrorStream();
progress = Suppliers.ofInstance(frame / videoFrames); new Thread(() -> {
try {
StringBuilder sb = new StringBuilder();
while (active.get()) {
char c = (char) in.read();
if (c == '\r') {
String str = sb.toString();
LOGGER.debug("[FFmpeg] {}", str);
if (str.startsWith("frame=")) {
str = str.substring(6).trim();
str = str.substring(0, str.indexOf(' '));
double frame = Integer.parseInt(str);
progress = Suppliers.ofInstance(frame / videoFrames);
}
sb = new StringBuilder();
} else {
sb.append(c);
} }
sb = new StringBuilder();
} else {
sb.append(c);
} }
} catch (IOException e) {
e.printStackTrace();
} }
} catch (IOException e) { }).start();
e.printStackTrace();
int result;
try {
result = process.waitFor();
} catch (InterruptedException e) {
process.destroy();
throw e;
} finally {
active.set(false);
}
if (result != 0) {
throw new IOException("FFmpeg returned: " + result);
} }
}).start();
int result;
try {
result = process.waitFor();
} catch (InterruptedException e) {
process.destroy();
throw e;
} finally {
active.set(false);
}
if (result != 0) {
throw new IOException("FFmpeg returned: " + result);
} }
MetadataInjector.injectODSMetadata(tmpFile); if (!settings.isInjectSphericalMetadata() || !isMp4) {
MetadataInjector.injectMetadata(settings.getRenderMethod(), outputFile,
return tmpFile; settings.getTargetVideoWidth(), settings.getTargetVideoHeight(),
settings.getSphericalFovX(), settings.getSphericalFovY());
} else if (settings.getRenderMethod() == RenderSettings.RenderMethod.EQUIRECTANGULAR) {
// if metadata hasn't been injected before, inject it before the upload
if (!settings.isInject360Metadata()) {
MetadataInjector.inject360Metadata(videoFile);
} }
} }
return videoFile; return outputFile;
} }
private Video doUpload(YouTube youTube, File processedFile) throws IOException { private Video doUpload(YouTube youTube, File processedFile) throws IOException {

View File

@@ -1,6 +1,6 @@
package com.replaymod.online; package com.replaymod.online;
import net.minecraft.client.Minecraft; import com.replaymod.core.versions.MCVer;
import java.util.Random; import java.util.Random;
@@ -9,7 +9,7 @@ public class AuthenticationHash {
private static final Random random = new Random(); private static final Random random = new Random();
public AuthenticationHash() { public AuthenticationHash() {
username = Minecraft.getMinecraft().getSession().getUsername(); username = MCVer.getMinecraft().getSession().getUsername();
currentTime = System.currentTimeMillis(); currentTime = System.currentTimeMillis();
randomLong = random.nextLong(); randomLong = random.nextLong();
hash = getAuthenticationHash(); hash = getAuthenticationHash();

View File

@@ -1,63 +1,65 @@
package com.replaymod.online; //#if MC<11300
//$$ package com.replaymod.online;
import com.replaymod.online.api.ApiClient; //$$
import com.replaymod.online.api.AuthData; //$$ import com.replaymod.online.api.ApiClient;
import com.replaymod.online.api.replay.holders.AuthConfirmation; //$$ import com.replaymod.online.api.AuthData;
import net.minecraftforge.common.config.Configuration; //$$ import com.replaymod.online.api.replay.holders.AuthConfirmation;
import net.minecraftforge.common.config.Property; //$$ import net.minecraftforge.common.config.Configuration;
//$$ import net.minecraftforge.common.config.Property;
/** //$$
* Auth data stored in a {@link Configuration}. //$$ /**
*/ //$$ * Auth data stored in a {@link Configuration}.
public class ConfigurationAuthData implements AuthData { //$$ */
//$$ public class ConfigurationAuthData implements AuthData {
private final Configuration config; //$$
private String userName; //$$ private final Configuration config;
private String authKey; //$$ private String userName;
//$$ private String authKey;
public ConfigurationAuthData(Configuration config) { //$$
this.config = config; //$$ public ConfigurationAuthData(Configuration config) {
} //$$ this.config = config;
//$$ }
/** //$$
* Loads the data from the configuration and checks for its validity. //$$ /**
* If the data is invalid, it is removed from the config. //$$ * Loads the data from the configuration and checks for its validity.
* @param apiClient Api client used for validating the auth data //$$ * If the data is invalid, it is removed from the config.
*/ //$$ * @param apiClient Api client used for validating the auth data
public void load(ApiClient apiClient) { //$$ */
Property property = config.get("authkey", "authkey", (String) null); //$$ public void load(ApiClient apiClient) {
if (property != null) { //$$ Property property = config.get("authkey", "authkey", (String) null);
String authKey = property.getString(); //$$ if (property != null) {
AuthConfirmation result = apiClient.checkAuthkey(authKey); //$$ String authKey = property.getString();
if (result != null) { //$$ AuthConfirmation result = apiClient.checkAuthkey(authKey);
this.authKey = authKey; //$$ if (result != null) {
this.userName = result.getUsername(); //$$ this.authKey = authKey;
} else { //$$ this.userName = result.getUsername();
setData(null, null); //$$ } else {
} //$$ setData(null, null);
} //$$ }
} //$$ }
//$$ }
@Override //$$
public String getUserName() { //$$ @Override
return userName; //$$ public String getUserName() {
} //$$ return userName;
//$$ }
@Override //$$
public String getAuthKey() { //$$ @Override
return authKey; //$$ public String getAuthKey() {
} //$$ return authKey;
//$$ }
@Override //$$
public void setData(String userName, String authKey) { //$$ @Override
this.userName = userName; //$$ public void setData(String userName, String authKey) {
this.authKey = authKey; //$$ this.userName = userName;
//$$ this.authKey = authKey;
config.removeCategory(config.getCategory("authkey")); //$$
if (authKey != null) { //$$ config.removeCategory(config.getCategory("authkey"));
// Note: .get() actually creates the entry with the default value if it doesn't exist //$$ if (authKey != null) {
config.get("authkey", "authkey", authKey); //$$ // Note: .get() actually creates the entry with the default value if it doesn't exist
} //$$ config.get("authkey", "authkey", authKey);
config.save(); //$$ }
} //$$ config.save();
} //$$ }
//$$ }
//#endif

View File

@@ -0,0 +1,69 @@
package com.replaymod.online;
import com.replaymod.online.api.ApiClient;
import com.replaymod.online.api.AuthData;
import com.replaymod.online.api.replay.holders.AuthConfirmation;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
/**
* Auth data stored in a plain text file.
*/
public class PlainFileAuthData implements AuthData {
private final Path path;
private String userName;
private String authKey;
public PlainFileAuthData(Path path) {
this.path = path;
}
/**
* Loads the data from the JSON file and checks for its validity.
* If the data is invalid, the JSON file is removed.
* @param apiClient Api client used for validating the auth data
*/
public void load(ApiClient apiClient) throws IOException {
String authKey;
try {
authKey = new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
} catch (NoSuchFileException | FileNotFoundException ignored) {
return;
}
AuthConfirmation result = apiClient.checkAuthkey(authKey);
if (result != null) {
this.authKey = authKey;
this.userName = result.getUsername();
} else {
Files.deleteIfExists(path);
}
}
@Override
public String getUserName() {
return userName;
}
@Override
public String getAuthKey() {
return authKey;
}
@Override
public void setData(String userName, String authKey) {
this.userName = userName;
this.authKey = authKey;
try {
Files.write(path, authKey.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE_NEW);
} catch (IOException e) {
ReplayModOnline.LOGGER.error("Saving auth data:", e);
}
}
}

View File

@@ -1,7 +1,10 @@
package com.replaymod.online; package com.replaymod.online;
import com.replaymod.core.Module;
import com.replaymod.core.ReplayMod; import com.replaymod.core.ReplayMod;
import com.replaymod.core.versions.MCVer;
import com.replaymod.online.api.ApiClient; import com.replaymod.online.api.ApiClient;
import com.replaymod.online.api.AuthData;
import com.replaymod.online.gui.GuiLoginPrompt; import com.replaymod.online.gui.GuiLoginPrompt;
import com.replaymod.online.gui.GuiReplayDownloading; import com.replaymod.online.gui.GuiReplayDownloading;
import com.replaymod.online.gui.GuiSaveModifiedReplay; import com.replaymod.online.gui.GuiSaveModifiedReplay;
@@ -12,48 +15,36 @@ import com.replaymod.replaystudio.replay.ReplayFile;
import com.replaymod.replaystudio.replay.ZipReplayFile; import com.replaymod.replaystudio.replay.ZipReplayFile;
import com.replaymod.replaystudio.studio.ReplayStudio; import com.replaymod.replaystudio.studio.ReplayStudio;
import de.johni0702.minecraft.gui.container.GuiScreen; import de.johni0702.minecraft.gui.container.GuiScreen;
import net.minecraftforge.common.config.Configuration; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
//#if MC>=10800 //#if MC>=11300
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.event.FMLInitializationEvent; //#else
import net.minecraftforge.fml.common.event.FMLPostInitializationEvent; //$$ import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; //#if MC>=10800
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; //$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#else //#else
//$$ import cpw.mods.fml.common.Mod;
//$$ import cpw.mods.fml.common.event.FMLInitializationEvent;
//$$ import cpw.mods.fml.common.event.FMLPostInitializationEvent;
//$$ import cpw.mods.fml.common.event.FMLPreInitializationEvent;
//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent; //$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
//#endif //#endif
//#endif
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import static com.replaymod.core.versions.MCVer.*; import static com.replaymod.core.versions.MCVer.*;
import static net.minecraft.client.Minecraft.getMinecraft;
@Mod(modid = ReplayModOnline.MOD_ID, public class ReplayModOnline implements Module {
version = "@MOD_VERSION@", { instance = this; }
acceptedMinecraftVersions = "@MC_VERSION@",
acceptableRemoteVersions = "*",
//#if MC>=10800
clientSideOnly = true,
//#endif
useMetadata = true)
public class ReplayModOnline {
public static final String MOD_ID = "replaymod-online";
@Mod.Instance(MOD_ID)
public static ReplayModOnline instance; public static ReplayModOnline instance;
private ReplayMod core; private ReplayMod core;
private ReplayModReplay replayModule; private ReplayModReplay replayModule;
public static Logger LOGGER; public static Logger LOGGER = LogManager.getLogger();
private ApiClient apiClient; private ApiClient apiClient;
@@ -64,22 +55,31 @@ public class ReplayModOnline {
*/ */
private File currentReplayOutputFile; private File currentReplayOutputFile;
@Mod.EventHandler public ReplayModOnline(ReplayMod core, ReplayModReplay replayModule) {
public void preInit(FMLPreInitializationEvent event) { this.core = core;
LOGGER = event.getModLog(); this.replayModule = replayModule;
core = ReplayMod.instance;
replayModule = ReplayModReplay.instance;
core.getSettingsRegistry().register(Setting.class); core.getSettingsRegistry().register(Setting.class);
Configuration config = new Configuration(event.getSuggestedConfigurationFile());
ConfigurationAuthData authData = new ConfigurationAuthData(config);
apiClient = new ApiClient(authData);
authData.load(apiClient);
} }
@Mod.EventHandler @Override
public void init(FMLInitializationEvent event) { public void initClient() {
Path path = MCVer.mcDataDir(MCVer.getMinecraft()).toPath().resolve("config/replaymod-online.token");
PlainFileAuthData authData = new PlainFileAuthData(path);
apiClient = new ApiClient(authData);
if (Files.notExists(path)) {
AuthData oldAuthData = loadOldAuthData();
if (oldAuthData != null) {
authData.setData(oldAuthData.getUserName(), oldAuthData.getAuthKey());
}
} else {
try {
authData.load(apiClient);
} catch (IOException e) {
ReplayModOnline.LOGGER.error("Loading auth data:", e);
}
}
if (!getDownloadsFolder().exists()){ if (!getDownloadsFolder().exists()){
if (!getDownloadsFolder().mkdirs()) { if (!getDownloadsFolder().mkdirs()) {
LOGGER.warn("Failed to create downloads folder: " + getDownloadsFolder()); LOGGER.warn("Failed to create downloads folder: " + getDownloadsFolder());
@@ -88,10 +88,7 @@ public class ReplayModOnline {
new GuiHandler(this).register(); new GuiHandler(this).register();
FML_BUS.register(this); FML_BUS.register(this);
}
@Mod.EventHandler
public void postInit(FMLPostInitializationEvent event) {
// Initial login prompt // Initial login prompt
if (!core.getSettingsRegistry().get(Setting.SKIP_LOGIN_PROMPT)) { if (!core.getSettingsRegistry().get(Setting.SKIP_LOGIN_PROMPT)) {
if (!isLoggedIn()) { if (!isLoggedIn()) {
@@ -103,6 +100,23 @@ public class ReplayModOnline {
} }
} }
/**
* Loads old auth data from the legacy Configuration system and stores it in the new json file.
* Always returns null on 1.13+ where the Configuration system has been removed.
*/
private AuthData loadOldAuthData() {
//#if MC<11300
//$$ Path path = MCVer.mcDataDir(MCVer.getMinecraft()).toPath().resolve("config/replaymod-online.cfg");
//$$ Configuration config = new Configuration(path.toFile());
//$$ ConfigurationAuthData authData = new ConfigurationAuthData(config);
//$$ authData.load(new ApiClient(authData));
//$$ if (authData.getAuthKey() != null) {
//$$ return authData;
//$$ }
//#endif
return null;
}
public ReplayMod getCore() { public ReplayMod getCore() {
return core; return core;
} }
@@ -125,7 +139,7 @@ public class ReplayModOnline {
public File getDownloadsFolder() { public File getDownloadsFolder() {
String path = core.getSettingsRegistry().get(Setting.DOWNLOAD_PATH); String path = core.getSettingsRegistry().get(Setting.DOWNLOAD_PATH);
return new File(path.startsWith("./") ? getMinecraft().mcDataDir : null, path); return new File(path.startsWith("./") ? mcDataDir(getMinecraft()) : null, path);
} }
public File getDownloadedFile(int id) { public File getDownloadedFile(int id) {

View File

@@ -6,6 +6,7 @@ import com.google.gson.JsonParseException;
import com.google.gson.JsonParser; import com.google.gson.JsonParser;
import com.mojang.authlib.exceptions.AuthenticationException; import com.mojang.authlib.exceptions.AuthenticationException;
import com.replaymod.core.ReplayMod; import com.replaymod.core.ReplayMod;
import com.replaymod.core.versions.MCVer;
import com.replaymod.online.AuthenticationHash; import com.replaymod.online.AuthenticationHash;
import com.replaymod.online.api.replay.ReplayModApiMethods; import com.replaymod.online.api.replay.ReplayModApiMethods;
import com.replaymod.online.api.replay.SearchQuery; import com.replaymod.online.api.replay.SearchQuery;
@@ -30,7 +31,7 @@ import static com.replaymod.core.utils.Utils.SSL_SOCKET_FACTORY;
public class ApiClient { public class ApiClient {
private static final Minecraft mc = Minecraft.getMinecraft(); private static final Minecraft mc = MCVer.getMinecraft();
private static final Gson gson = new Gson(); private static final Gson gson = new Gson();
private static final JsonParser jsonParser = new JsonParser(); private static final JsonParser jsonParser = new JsonParser();

View File

@@ -9,7 +9,8 @@ public enum MinecraftVersion {
MC_1_11_2("Minecraft 1.11.2", "1.11.2"), MC_1_11_2("Minecraft 1.11.2", "1.11.2"),
MC_1_12("Minecraft 1.12", "1.12"), MC_1_12("Minecraft 1.12", "1.12"),
MC_1_12_1("Minecraft 1.12.1", "1.12.1"), MC_1_12_1("Minecraft 1.12.1", "1.12.1"),
MC_1_12_2("Minecraft 1.12.2", "1.12.2"); MC_1_12_2("Minecraft 1.12.2", "1.12.2"),
MC_1_13_2("Minecraft 1.13.2", "1.13.2");
private String niceName, apiName; private String niceName, apiName;

View File

@@ -1,7 +1,6 @@
package com.replaymod.online.gui; package com.replaymod.online.gui;
import com.mojang.authlib.exceptions.AuthenticationException; import com.mojang.authlib.exceptions.AuthenticationException;
import com.replaymod.core.versions.MCVer;
import com.replaymod.online.api.ApiClient; import com.replaymod.online.api.ApiClient;
import com.replaymod.online.api.ApiException; import com.replaymod.online.api.ApiException;
import de.johni0702.minecraft.gui.container.AbstractGuiScreen; import de.johni0702.minecraft.gui.container.AbstractGuiScreen;
@@ -13,9 +12,9 @@ import de.johni0702.minecraft.gui.layout.VerticalLayout;
import de.johni0702.minecraft.gui.utils.Consumers; import de.johni0702.minecraft.gui.utils.Consumers;
import de.johni0702.minecraft.gui.utils.Utils; import de.johni0702.minecraft.gui.utils.Utils;
import com.replaymod.core.utils.Patterns; import com.replaymod.core.utils.Patterns;
import de.johni0702.minecraft.gui.utils.lwjgl.Dimension;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableColor;
import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.gui.FontRenderer;
import org.lwjgl.util.Dimension;
import org.lwjgl.util.ReadableColor;
import static com.replaymod.core.versions.MCVer.*; import static com.replaymod.core.versions.MCVer.*;

View File

@@ -37,9 +37,9 @@ import de.johni0702.minecraft.gui.layout.VerticalLayout;
import de.johni0702.minecraft.gui.popup.GuiYesNoPopup; import de.johni0702.minecraft.gui.popup.GuiYesNoPopup;
import de.johni0702.minecraft.gui.utils.Colors; import de.johni0702.minecraft.gui.utils.Colors;
import de.johni0702.minecraft.gui.utils.Consumer; import de.johni0702.minecraft.gui.utils.Consumer;
import de.johni0702.minecraft.gui.utils.lwjgl.Dimension;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.lwjgl.util.Dimension;
import org.lwjgl.util.ReadableDimension;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.IOException; import java.io.IOException;
@@ -462,7 +462,7 @@ public class GuiReplayCenter extends GuiScreen {
@Override @Override
public ReadableDimension calcMinSize(GuiContainer<?> container) { public ReadableDimension calcMinSize(GuiContainer<?> container) {
return new org.lwjgl.util.Dimension(300, thumbnail.getMinSize().getHeight()); return new Dimension(300, thumbnail.getMinSize().getHeight());
} }
}); });
} }

View File

@@ -15,9 +15,14 @@ import de.johni0702.minecraft.gui.function.Typeable;
import de.johni0702.minecraft.gui.layout.GridLayout; import de.johni0702.minecraft.gui.layout.GridLayout;
import de.johni0702.minecraft.gui.popup.AbstractGuiPopup; import de.johni0702.minecraft.gui.popup.AbstractGuiPopup;
import de.johni0702.minecraft.gui.utils.Colors; import de.johni0702.minecraft.gui.utils.Colors;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadablePoint;
import net.minecraft.client.resources.I18n; import net.minecraft.client.resources.I18n;
import org.lwjgl.input.Keyboard;
import org.lwjgl.util.ReadablePoint; //#if MC>=11300
import com.replaymod.core.versions.MCVer.Keyboard;
//#else
//$$ import org.lwjgl.input.Keyboard;
//#endif
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;

View File

@@ -33,13 +33,16 @@ import de.johni0702.minecraft.gui.popup.AbstractGuiPopup;
import de.johni0702.minecraft.gui.popup.GuiYesNoPopup; import de.johni0702.minecraft.gui.popup.GuiYesNoPopup;
import de.johni0702.minecraft.gui.utils.Colors; import de.johni0702.minecraft.gui.utils.Colors;
import net.minecraft.client.resources.I18n; import net.minecraft.client.resources.I18n;
import net.minecraft.client.settings.GameSettings;
import net.minecraft.client.settings.KeyBinding; import net.minecraft.client.settings.KeyBinding;
import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReport;
import net.minecraft.util.ReportedException;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
//#if MC>=11300
//#else
//$$ import net.minecraft.client.settings.GameSettings;
//#endif
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.File; import java.io.File;
@@ -47,6 +50,7 @@ import java.io.IOException;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static com.replaymod.core.versions.MCVer.newReportedException;
import static java.util.Arrays.stream; import static java.util.Arrays.stream;
public class GuiUploadReplay extends GuiScreen { public class GuiUploadReplay extends GuiScreen {
@@ -130,7 +134,7 @@ public class GuiUploadReplay extends GuiScreen {
metaData = replayFile.getMetaData(); metaData = replayFile.getMetaData();
optThumbnail = replayFile.getThumb(); optThumbnail = replayFile.getThumb();
} catch (IOException e) { } catch (IOException e) {
throw new ReportedException(CrashReport.makeCrashReport(e, "Read replay file " + file.getName())); throw newReportedException(CrashReport.makeCrashReport(e, "Read replay file " + file.getName()));
} }
// Apply to gui // Apply to gui
@@ -147,7 +151,11 @@ public class GuiUploadReplay extends GuiScreen {
// Get name of key used for thumbnail creation // Get name of key used for thumbnail creation
KeyBindingRegistry registry = mod.getCore().getKeyBindingRegistry(); KeyBindingRegistry registry = mod.getCore().getKeyBindingRegistry();
KeyBinding keyBinding = registry.getKeyBindings().get("replaymod.input.thumbnail"); KeyBinding keyBinding = registry.getKeyBindings().get("replaymod.input.thumbnail");
String keyName = keyBinding == null ? "???" : GameSettings.getKeyDisplayString(keyBinding.getKeyCode()); //#if MC>=11300
String keyName = keyBinding == null ? "???" : keyBinding.func_197978_k();
//#else
//$$ String keyName = keyBinding == null ? "???" : GameSettings.getKeyDisplayString(keyBinding.getKeyCode());
//#endif
// No thumbnail, show default thumbnail and hint on how to create one // No thumbnail, show default thumbnail and hint on how to create one
thumbnail.setTexture(Utils.DEFAULT_THUMBNAIL); thumbnail.setTexture(Utils.DEFAULT_THUMBNAIL);

View File

@@ -15,7 +15,12 @@ import net.minecraft.client.resources.I18n;
import net.minecraftforge.client.event.GuiScreenEvent; import net.minecraftforge.client.event.GuiScreenEvent;
//#if MC>=10800 //#if MC>=10800
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; //#if MC>=11300
import net.minecraftforge.eventbus.api.SubscribeEvent;
import java.util.ArrayList;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#endif
//#else //#else
//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent; //$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
//#endif //#endif
@@ -44,9 +49,21 @@ public class GuiHandler {
return; return;
} }
GuiButton button = new GuiButton(BUTTON_REPLAY_CENTER, getGui(event).width / 2 - 100, GuiButton button = new GuiButton(
getGui(event).height / 4 + 10 + 4 * 24, I18n.format("replaymod.gui.replaycenter")); BUTTON_REPLAY_CENTER,
getButtonList(event).add(button); getGui(event).width / 2 + 2,
getGui(event).height / 4 + 10 + 4 * 24,
I18n.format("replaymod.gui.replaycenter")
) {
//#if MC>=11300
@Override
public void onClick(double mouseX, double mouseY) {
onButton(new GuiScreenEvent.ActionPerformedEvent.Pre(getGui(event), this, new ArrayList<>()));
}
//#endif
};
button.width = button.width / 2 - 2;
addButton(event, button);
} }
@SubscribeEvent @SubscribeEvent
@@ -57,22 +74,15 @@ public class GuiHandler {
} }
final GuiReplayViewer replayViewer = (GuiReplayViewer) guiScreen; final GuiReplayViewer replayViewer = (GuiReplayViewer) guiScreen;
// Inject Upload button // Inject Upload button
for (GuiElement element : replayViewer.replayButtonPanel.getChildren()) { replayViewer.replaySpecificButtons.add(new de.johni0702.minecraft.gui.element.GuiButton(replayViewer.uploadButton).onClick(() -> {
if (element instanceof GuiPanel && (((GuiPanel) element).getChildren().isEmpty())) { File replayFile = replayViewer.list.getSelected().file;
new de.johni0702.minecraft.gui.element.GuiButton((GuiPanel) element).onClick(new Runnable() { GuiUploadReplay uploadGui = new GuiUploadReplay(replayViewer, mod, replayFile);
@Override if (mod.isLoggedIn()) {
public void run() { uploadGui.display();
File replayFile = replayViewer.list.getSelected().file; } else {
GuiUploadReplay uploadGui = new GuiUploadReplay(replayViewer, mod, replayFile); new GuiLoginPrompt(mod.getApiClient(), replayViewer, uploadGui, true);
if (mod.isLoggedIn()) {
uploadGui.display();
} else {
new GuiLoginPrompt(mod.getApiClient(), replayViewer, uploadGui, true);
}
}
}).setSize(73, 20).setI18nLabel("replaymod.gui.upload").setDisabled();
} }
} }).setSize(73, 20).setI18nLabel("replaymod.gui.upload").setDisabled());
} }
@SubscribeEvent @SubscribeEvent

View File

@@ -28,8 +28,8 @@ import de.johni0702.minecraft.gui.layout.VerticalLayout;
import de.johni0702.minecraft.gui.popup.GuiYesNoPopup; import de.johni0702.minecraft.gui.popup.GuiYesNoPopup;
import de.johni0702.minecraft.gui.utils.Colors; import de.johni0702.minecraft.gui.utils.Colors;
import de.johni0702.minecraft.gui.utils.Consumer; import de.johni0702.minecraft.gui.utils.Consumer;
import org.lwjgl.util.Dimension; import de.johni0702.minecraft.gui.utils.lwjgl.Dimension;
import org.lwjgl.util.ReadableDimension; import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import java.io.IOException; import java.io.IOException;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
@@ -229,6 +229,7 @@ public class GuiKeyframeRepository extends GuiScreen implements Closeable {
timelines.putAll(replayFile.getTimelines(registry)); timelines.putAll(replayFile.getTimelines(registry));
for (Map.Entry<String, Timeline> entry : timelines.entrySet()) { for (Map.Entry<String, Timeline> entry : timelines.entrySet()) {
if (entry.getKey().isEmpty()) continue; // don't show auto-save slot
list.getListPanel().addElements(null, new Entry(entry.getKey())); list.getListPanel().addElements(null, new Entry(entry.getKey()));
} }
} }

View File

@@ -14,7 +14,11 @@ import com.replaymod.replaystudio.pathing.path.Timeline;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
//#if MC>=10800 //#if MC>=10800
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; //#if MC>=11300
import net.minecraftforge.eventbus.api.SubscribeEvent;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#endif
//#else //#else
//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent; //$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
//#endif //#endif
@@ -28,7 +32,7 @@ import static com.replaymod.core.versions.MCVer.*;
* Plays a timeline. * Plays a timeline.
*/ */
public abstract class AbstractTimelinePlayer { public abstract class AbstractTimelinePlayer {
private final Minecraft mc = Minecraft.getMinecraft(); private final Minecraft mc = getMinecraft();
private final ReplayHandler replayHandler; private final ReplayHandler replayHandler;
private Timeline timeline; private Timeline timeline;
protected long startOffset; protected long startOffset;

View File

@@ -4,7 +4,11 @@ import com.replaymod.core.utils.WrappedTimer;
import net.minecraft.util.Timer; import net.minecraft.util.Timer;
//#if MC>=10800 //#if MC>=10800
import net.minecraftforge.fml.common.eventhandler.Event; //#if MC>=11300
import net.minecraftforge.eventbus.api.Event;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.Event;
//#endif
//#else //#else
//$$ import cpw.mods.fml.common.eventhandler.Event; //$$ import cpw.mods.fml.common.eventhandler.Event;
//#endif //#endif
@@ -15,16 +19,28 @@ import static com.replaymod.core.versions.MCVer.*;
* Wrapper around the current timer that prevents the timer from advancing by itself. * Wrapper around the current timer that prevents the timer from advancing by itself.
*/ */
public class ReplayTimer extends WrappedTimer { public class ReplayTimer extends WrappedTimer {
private final Timer state = new Timer(0); //#if MC>=11300
private final Timer state = new Timer(0, 0);
//#else
//$$ private final Timer state = new Timer(0);
//#endif
public ReplayTimer(Timer wrapped) { public ReplayTimer(Timer wrapped) {
super(wrapped); super(wrapped);
} }
@Override @Override
public void updateTimer() { public void updateTimer(
//#if MC>=11300
long sysClock
//#endif
) {
copy(this, state); // Save our current state copy(this, state); // Save our current state
super.updateTimer(); // Update current state wrapped.updateTimer(
//#if MC>=11300
sysClock
//#endif
); // Update current state
copy(state, this); // Restore our old state copy(state, this); // Restore our old state
FML_BUS.post(new UpdatedEvent()); FML_BUS.post(new UpdatedEvent());
} }

View File

@@ -1,85 +1,99 @@
package com.replaymod.recording; package com.replaymod.recording;
import com.replaymod.core.KeyBindingRegistry;
import com.replaymod.core.Module;
import com.replaymod.core.ReplayMod; import com.replaymod.core.ReplayMod;
import com.replaymod.core.utils.Restrictions; import com.replaymod.core.utils.Restrictions;
import com.replaymod.recording.handler.ConnectionEventHandler; import com.replaymod.recording.handler.ConnectionEventHandler;
import com.replaymod.recording.handler.GuiHandler; import com.replaymod.recording.handler.GuiHandler;
import com.replaymod.recording.packet.PacketListener; import com.replaymod.recording.packet.PacketListener;
import io.netty.channel.Channel;
import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.util.AttributeKey;
import net.minecraft.network.NetworkManager; import net.minecraft.network.NetworkManager;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.lwjgl.input.Keyboard;
//#if MC>=11300
import com.replaymod.core.versions.MCVer.Keyboard;
//#else
//$$ import org.lwjgl.input.Keyboard;
//#endif
//#if MC>=10800 //#if MC>=10800
import net.minecraftforge.fml.common.Mod; //#if MC>=11300
import net.minecraftforge.fml.common.event.FMLInitializationEvent; import net.minecraftforge.fml.network.NetworkRegistry;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; //#else
import net.minecraftforge.fml.common.eventhandler.EventBus; //$$ import net.minecraftforge.fml.common.network.NetworkRegistry;
import net.minecraftforge.fml.common.network.NetworkRegistry; //#endif
//#else //#else
//$$ import cpw.mods.fml.common.Mod;
//$$ import cpw.mods.fml.common.event.FMLInitializationEvent;
//$$ import cpw.mods.fml.common.event.FMLPreInitializationEvent;
//$$ import cpw.mods.fml.common.eventhandler.EventBus;
//$$ import cpw.mods.fml.common.network.NetworkRegistry; //$$ import cpw.mods.fml.common.network.NetworkRegistry;
//#endif //#endif
import static com.replaymod.core.versions.MCVer.*; import static com.replaymod.core.versions.MCVer.*;
@Mod(modid = ReplayModRecording.MOD_ID, public class ReplayModRecording implements Module {
version = "@MOD_VERSION@",
acceptedMinecraftVersions = "@MC_VERSION@",
acceptableRemoteVersions = "*",
//#if MC>=10800
clientSideOnly = true,
//#endif
useMetadata = true)
public class ReplayModRecording {
public static final String MOD_ID = "replaymod-recording";
@Mod.Instance(MOD_ID) private static final Logger LOGGER = LogManager.getLogger();
//#if MC>=11300
private static final AttributeKey<Void> ATTR_CHECKED = AttributeKey.newInstance("ReplayModRecording_checked");
//#endif
{ instance = this; }
public static ReplayModRecording instance; public static ReplayModRecording instance;
private ReplayMod core; private ReplayMod core;
private Logger logger;
private ConnectionEventHandler connectionEventHandler; private ConnectionEventHandler connectionEventHandler;
@Mod.EventHandler public ReplayModRecording(ReplayMod mod) {
public void preInit(FMLPreInitializationEvent event) { core = mod;
logger = event.getModLog();
core = ReplayMod.instance;
core.getSettingsRegistry().register(Setting.class); core.getSettingsRegistry().register(Setting.class);
}
core.getKeyBindingRegistry().registerKeyBinding("replaymod.input.marker", Keyboard.KEY_M, new Runnable() { @Override
public void registerKeyBindings(KeyBindingRegistry registry) {
registry.registerKeyBinding("replaymod.input.marker", Keyboard.KEY_M, new Runnable() {
@Override @Override
public void run() { public void run() {
PacketListener packetListener = connectionEventHandler.getPacketListener(); PacketListener packetListener = connectionEventHandler.getPacketListener();
if (packetListener != null) { if (packetListener != null) {
packetListener.addMarker(); packetListener.addMarker(null);
core.printInfoToChat("replaymod.chat.addedmarker"); core.printInfoToChat("replaymod.chat.addedmarker");
} }
} }
}); });
} }
@Mod.EventHandler @Override
public void init(FMLInitializationEvent event) { public void initClient() {
EventBus bus = FML_BUS; FML_BUS.register(connectionEventHandler = new ConnectionEventHandler(LOGGER, core));
bus.register(connectionEventHandler = new ConnectionEventHandler(logger, core));
new GuiHandler(core).register(); new GuiHandler(core).register();
NetworkRegistry.INSTANCE.newChannel(Restrictions.PLUGIN_CHANNEL, new RestrictionsChannelHandler()); //#if MC>=11300
NetworkRegistry.newEventChannel(Restrictions.PLUGIN_CHANNEL, () -> "0", any -> true, any -> true);
//#else
//$$ NetworkRegistry.INSTANCE.newChannel(Restrictions.PLUGIN_CHANNEL, new RestrictionsChannelHandler());
//#endif
} }
@ChannelHandler.Sharable @ChannelHandler.Sharable
private static class RestrictionsChannelHandler extends ChannelDuplexHandler {} private static class RestrictionsChannelHandler extends ChannelDuplexHandler {}
public void initiateRecording(NetworkManager networkManager) { public void initiateRecording(NetworkManager networkManager) {
Channel channel = networkManager.channel();
if (channel.pipeline().get("ReplayModReplay_replaySender") != null) return;
//#if MC>=11300
if (channel.hasAttr(ATTR_CHECKED)) return;
channel.attr(ATTR_CHECKED).set(null);
//#endif
connectionEventHandler.onConnectedToServerEvent(networkManager); connectionEventHandler.onConnectedToServerEvent(networkManager);
} }
public ConnectionEventHandler getConnectionEventHandler() {
return connectionEventHandler;
}
} }

View File

@@ -6,12 +6,14 @@ public final class Setting<T> extends SettingsRegistry.SettingKeys<T> {
public static final Setting<Boolean> RECORD_SINGLEPLAYER = make("recordSingleplayer", "recordsingleplayer", true); public static final Setting<Boolean> RECORD_SINGLEPLAYER = make("recordSingleplayer", "recordsingleplayer", true);
public static final Setting<Boolean> RECORD_SERVER = make("recordServer", "recordserver", true); public static final Setting<Boolean> RECORD_SERVER = make("recordServer", "recordserver", true);
public static final Setting<Boolean> INDICATOR = make("indicator", "indicator", true); public static final Setting<Boolean> INDICATOR = make("indicator", "indicator", true);
public static final Setting<Boolean> AUTO_START_RECORDING = make("autoStartRecording", "autostartrecording", true);
public static final Setting<Boolean> AUTO_POST_PROCESS = make("autoPostProcess", null, true);
private static <T> Setting<T> make(String key, String displayName, T defaultValue) { private static <T> Setting<T> make(String key, String displayName, T defaultValue) {
return new Setting<>(key, displayName, defaultValue); return new Setting<>(key, displayName, defaultValue);
} }
public Setting(String key, String displayString, T defaultValue) { public Setting(String key, String displayString, T defaultValue) {
super("recording", key, "replaymod.gui.settings." + displayString, defaultValue); super("recording", key, displayString == null ? null : "replaymod.gui.settings." + displayString, defaultValue);
} }
} }

View File

@@ -0,0 +1,110 @@
package com.replaymod.recording.gui;
import com.replaymod.core.ReplayMod;
import com.replaymod.editor.gui.MarkerProcessor;
import com.replaymod.recording.Setting;
import com.replaymod.recording.packet.PacketListener;
import de.johni0702.minecraft.gui.container.GuiPanel;
import de.johni0702.minecraft.gui.container.GuiScreen;
import de.johni0702.minecraft.gui.container.VanillaGuiScreen;
import de.johni0702.minecraft.gui.element.GuiButton;
import de.johni0702.minecraft.gui.layout.CustomLayout;
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
import net.minecraft.client.gui.GuiIngameMenu;
import net.minecraftforge.client.event.GuiScreenEvent;
import net.minecraftforge.common.MinecraftForge;
//#if MC>=10800
//#if MC>=11300
import net.minecraftforge.eventbus.api.SubscribeEvent;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#endif
//#else
//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
//#endif
import static com.replaymod.core.versions.MCVer.getGui;
public class GuiRecordingControls {
private ReplayMod core;
private PacketListener packetListener;
private boolean paused;
private boolean stopped;
private GuiPanel panel = new GuiPanel().setLayout(new HorizontalLayout().setSpacing(4));
private GuiButton buttonPauseResume = new GuiButton(panel).onClick(() -> {
if (paused) {
packetListener.addMarker(MarkerProcessor.MARKER_NAME_END_CUT);
} else {
packetListener.addMarker(MarkerProcessor.MARKER_NAME_START_CUT);
}
paused = !paused;
updateState();
}).setSize(98, 20);
private GuiButton buttonStartStop = new GuiButton(panel).onClick(() -> {
if (stopped) {
paused = false;
packetListener.addMarker(MarkerProcessor.MARKER_NAME_END_CUT);
core.printInfoToChat("replaymod.chat.recordingstarted");
} else {
int timestamp = (int) packetListener.getCurrentDuration();
if (!paused) {
packetListener.addMarker(MarkerProcessor.MARKER_NAME_START_CUT, timestamp);
}
packetListener.addMarker(MarkerProcessor.MARKER_NAME_SPLIT, timestamp + 1);
}
stopped = !stopped;
updateState();
}).setSize(98, 20);
public GuiRecordingControls(ReplayMod core, PacketListener packetListener) {
this.core = core;
this.packetListener = packetListener;
paused = stopped = !core.getSettingsRegistry().get(Setting.AUTO_START_RECORDING);
updateState();
}
public void register() {
MinecraftForge.EVENT_BUS.register(this);
}
public void unregister() {
MinecraftForge.EVENT_BUS.unregister(this);
}
@SubscribeEvent
public void onGuiInit(GuiScreenEvent.InitGuiEvent.Post event) {
if (getGui(event) instanceof GuiIngameMenu) {
show((GuiIngameMenu) getGui(event));
}
}
private void updateState() {
buttonPauseResume.setI18nLabel("replaymod.gui.recording." + (paused ? "resume" : "pause"));
buttonStartStop.setI18nLabel("replaymod.gui.recording." + (stopped ? "start" : "stop"));
buttonPauseResume.setEnabled(!stopped);
}
public void show(GuiIngameMenu gui) {
VanillaGuiScreen.setup(gui).setLayout(new CustomLayout<GuiScreen>() {
@Override
protected void layout(GuiScreen container, int width, int height) {
pos(panel, width / 2 - 100, height / 4 + 128);
}
}).addElements(null, panel);
}
public boolean isPaused() {
return paused;
}
public boolean isStopped() {
return stopped;
}
}

View File

@@ -10,10 +10,15 @@ import net.minecraftforge.client.event.RenderGameOverlayEvent;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
//#if MC>=10800 //#if MC>=10800
import net.minecraft.client.renderer.GlStateManager; //#if MC>=11300
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.eventbus.api.SubscribeEvent;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#endif
import static net.minecraft.client.renderer.GlStateManager.*;
//#else //#else
//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent; //$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
//$$ import static com.replaymod.core.versions.MCVer.GlStateManager.*;
//#endif //#endif
import static com.replaymod.core.ReplayMod.TEXTURE; import static com.replaymod.core.ReplayMod.TEXTURE;
@@ -26,10 +31,12 @@ import static com.replaymod.core.versions.MCVer.*;
public class GuiRecordingOverlay { public class GuiRecordingOverlay {
private final Minecraft mc; private final Minecraft mc;
private final SettingsRegistry settingsRegistry; private final SettingsRegistry settingsRegistry;
private final GuiRecordingControls guiControls;
public GuiRecordingOverlay(Minecraft mc, SettingsRegistry settingsRegistry) { public GuiRecordingOverlay(Minecraft mc, SettingsRegistry settingsRegistry, GuiRecordingControls guiControls) {
this.mc = mc; this.mc = mc;
this.settingsRegistry = settingsRegistry; this.settingsRegistry = settingsRegistry;
this.guiControls = guiControls;
} }
public void register() { public void register() {
@@ -47,12 +54,14 @@ public class GuiRecordingOverlay {
@SubscribeEvent @SubscribeEvent
public void renderRecordingIndicator(RenderGameOverlayEvent.Post event) { public void renderRecordingIndicator(RenderGameOverlayEvent.Post event) {
if (getType(event) != RenderGameOverlayEvent.ElementType.ALL) return; if (getType(event) != RenderGameOverlayEvent.ElementType.ALL) return;
if (guiControls.isStopped()) return;
if (settingsRegistry.get(Setting.INDICATOR)) { if (settingsRegistry.get(Setting.INDICATOR)) {
FontRenderer fontRenderer = getFontRenderer(mc); FontRenderer fontRenderer = getFontRenderer(mc);
fontRenderer.drawString(I18n.format("replaymod.gui.recording").toUpperCase(), 30, 18 - (fontRenderer.FONT_HEIGHT / 2), 0xffffffff); String text = guiControls.isPaused() ? I18n.format("replaymod.gui.paused") : I18n.format("replaymod.gui.recording");
mc.renderEngine.bindTexture(TEXTURE); fontRenderer.drawString(text.toUpperCase(), 30, 18 - (fontRenderer.FONT_HEIGHT / 2), 0xffffffff);
GlStateManager.resetColor(); bindTexture(TEXTURE);
GlStateManager.enableAlpha(); resetColor();
enableAlpha();
Gui.drawModalRectWithCustomSizedTexture(10, 10, 58, 20, 16, 16, TEXTURE_SIZE, TEXTURE_SIZE); Gui.drawModalRectWithCustomSizedTexture(10, 10, 58, 20, 16, 16, TEXTURE_SIZE, TEXTURE_SIZE);
} }
} }

View File

@@ -3,7 +3,9 @@ package com.replaymod.recording.handler;
import com.replaymod.core.ReplayMod; import com.replaymod.core.ReplayMod;
import com.replaymod.core.utils.ModCompat; import com.replaymod.core.utils.ModCompat;
import com.replaymod.core.utils.Utils; import com.replaymod.core.utils.Utils;
import com.replaymod.editor.gui.MarkerProcessor;
import com.replaymod.recording.Setting; import com.replaymod.recording.Setting;
import com.replaymod.recording.gui.GuiRecordingControls;
import com.replaymod.recording.gui.GuiRecordingOverlay; import com.replaymod.recording.gui.GuiRecordingOverlay;
import com.replaymod.recording.packet.PacketListener; import com.replaymod.recording.packet.PacketListener;
import com.replaymod.replaystudio.replay.ReplayFile; import com.replaymod.replaystudio.replay.ReplayFile;
@@ -15,19 +17,19 @@ import net.minecraft.network.NetworkManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
//#if MC>=10800 //#if MC>=10800
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; //#if MC>=11300
import net.minecraftforge.fml.common.network.FMLNetworkEvent.ClientDisconnectionFromServerEvent; import net.minecraft.world.dimension.DimensionType;
//#endif
import static com.replaymod.core.versions.MCVer.WorldType_DEBUG_ALL_BLOCK_STATES; import static com.replaymod.core.versions.MCVer.WorldType_DEBUG_ALL_BLOCK_STATES;
//#else
//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
//$$ import cpw.mods.fml.common.network.FMLNetworkEvent.ClientDisconnectionFromServerEvent;
//#endif //#endif
import java.io.File; import java.io.File;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Calendar; import java.util.Calendar;
import static com.replaymod.core.versions.MCVer.getMinecraft;
/** /**
* Handles connection events and initiates recording if enabled. * Handles connection events and initiates recording if enabled.
*/ */
@@ -36,7 +38,7 @@ public class ConnectionEventHandler {
private static final String packetHandlerKey = "packet_handler"; private static final String packetHandlerKey = "packet_handler";
private static final String DATE_FORMAT = "yyyy_MM_dd_HH_mm_ss"; private static final String DATE_FORMAT = "yyyy_MM_dd_HH_mm_ss";
private static final SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT); private static final SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
private static final Minecraft mc = Minecraft.getMinecraft(); private static final Minecraft mc = getMinecraft();
private final Logger logger; private final Logger logger;
private final ReplayMod core; private final ReplayMod core;
@@ -44,6 +46,7 @@ public class ConnectionEventHandler {
private RecordingEventHandler recordingEventHandler; private RecordingEventHandler recordingEventHandler;
private PacketListener packetListener; private PacketListener packetListener;
private GuiRecordingOverlay guiOverlay; private GuiRecordingOverlay guiOverlay;
private GuiRecordingControls guiControls;
public ConnectionEventHandler(Logger logger, ReplayMod core) { public ConnectionEventHandler(Logger logger, ReplayMod core) {
this.logger = logger; this.logger = logger;
@@ -55,7 +58,11 @@ public class ConnectionEventHandler {
boolean local = networkManager.isLocalChannel(); boolean local = networkManager.isLocalChannel();
if (local) { if (local) {
//#if MC>=10800 //#if MC>=10800
if (mc.getIntegratedServer().getEntityWorld().getWorldType() == WorldType_DEBUG_ALL_BLOCK_STATES) { //#if MC>=11300
if (mc.getIntegratedServer().getWorld(DimensionType.OVERWORLD).getWorldType() == WorldType_DEBUG_ALL_BLOCK_STATES) {
//#else
//$$ if (mc.getIntegratedServer().getEntityWorld().getWorldType() == WorldType_DEBUG_ALL_BLOCK_STATES) {
//#endif
logger.info("Debug World recording is not supported."); logger.info("Debug World recording is not supported.");
return; return;
} }
@@ -74,10 +81,10 @@ public class ConnectionEventHandler {
String worldName; String worldName;
if (local) { if (local) {
worldName = mc.getIntegratedServer().getWorldName(); worldName = mc.getIntegratedServer().getWorldName();
} else if (Minecraft.getMinecraft().getCurrentServerData() != null) { } else if (mc.getCurrentServerData() != null) {
worldName = Minecraft.getMinecraft().getCurrentServerData().serverIP; worldName = mc.getCurrentServerData().serverIP;
//#if MC>=11100 //#if MC>=11100
} else if (Minecraft.getMinecraft().isConnectedToRealms()) { } else if (mc.isConnectedToRealms()) {
// we can't access the server name without tapping too deep in the Realms Library // we can't access the server name without tapping too deep in the Realms Library
worldName = "A Realms Server"; worldName = "A Realms Server";
//#endif //#endif
@@ -97,28 +104,36 @@ public class ConnectionEventHandler {
ReplayMetaData metaData = new ReplayMetaData(); ReplayMetaData metaData = new ReplayMetaData();
metaData.setSingleplayer(local); metaData.setSingleplayer(local);
metaData.setServerName(worldName); metaData.setServerName(worldName);
metaData.setGenerator("ReplayMod v" + ReplayMod.getContainer().getVersion()); metaData.setGenerator("ReplayMod v" + ReplayMod.instance.getVersion());
metaData.setDate(System.currentTimeMillis()); metaData.setDate(System.currentTimeMillis());
metaData.setMcVersion(ReplayMod.getMinecraftVersion()); metaData.setMcVersion(ReplayMod.getMinecraftVersion());
packetListener = new PacketListener(replayFile, metaData); packetListener = new PacketListener(core, currentFile.toPath(), replayFile, metaData);
networkManager.channel().pipeline().addBefore(packetHandlerKey, "replay_recorder", packetListener); networkManager.channel().pipeline().addBefore(packetHandlerKey, "replay_recorder", packetListener);
recordingEventHandler = new RecordingEventHandler(packetListener); recordingEventHandler = new RecordingEventHandler(packetListener);
recordingEventHandler.register(); recordingEventHandler.register();
guiOverlay = new GuiRecordingOverlay(mc, core.getSettingsRegistry()); guiControls = new GuiRecordingControls(core, packetListener);
guiControls.register();
guiOverlay = new GuiRecordingOverlay(mc, core.getSettingsRegistry(), guiControls);
guiOverlay.register(); guiOverlay.register();
core.printInfoToChat("replaymod.chat.recordingstarted"); if (core.getSettingsRegistry().get(Setting.AUTO_START_RECORDING)) {
core.printInfoToChat("replaymod.chat.recordingstarted");
} else {
packetListener.addMarker(MarkerProcessor.MARKER_NAME_START_CUT, 0);
}
} catch (Throwable e) { } catch (Throwable e) {
e.printStackTrace(); e.printStackTrace();
core.printWarningToChat("replaymod.chat.recordingfailed"); core.printWarningToChat("replaymod.chat.recordingfailed");
} }
} }
@SubscribeEvent public void reset() {
public void onDisconnectedFromServerEvent(ClientDisconnectionFromServerEvent event) {
if (packetListener != null) { if (packetListener != null) {
guiControls.unregister();
guiControls = null;
guiOverlay.unregister(); guiOverlay.unregister();
guiOverlay = null; guiOverlay = null;
recordingEventHandler.unregister(); recordingEventHandler.unregister();

View File

@@ -1,34 +1,36 @@
package com.replaymod.recording.handler; //#if MC<11300
//$$ package com.replaymod.recording.handler;
import io.netty.channel.ChannelHandlerContext; //$$
import io.netty.channel.SimpleChannelInboundHandler; //$$ import io.netty.channel.ChannelHandlerContext;
//$$ import io.netty.channel.SimpleChannelInboundHandler;
//$$
//#if MC>=10800 //#if MC>=10800
import net.minecraftforge.fml.common.network.handshake.FMLHandshakeMessage; //$$ import net.minecraftforge.fml.common.network.handshake.FMLHandshakeMessage;
//#else //#else
//$$ import cpw.mods.fml.common.network.handshake.FMLHandshakeMessage; //$$ import cpw.mods.fml.common.network.handshake.FMLHandshakeMessage;
//#endif //#endif
//$$
/** //$$ /**
* Filters out all handshake packets that were sent for recording but must //$$ * Filters out all handshake packets that were sent for recording but must
* not actually be handled. //$$ * not actually be handled.
* This handler is only present when connected to the integrated server as //$$ * This handler is only present when connected to the integrated server as
* otherwise all packets must be handled. //$$ * otherwise all packets must be handled.
* //$$ *
* When in single player, the game state packets must never be handled //$$ * When in single player, the game state packets must never be handled
* otherwise wired bugs related to semi-singletons can occur. //$$ * otherwise wired bugs related to semi-singletons can occur.
* See https://bugs.replaymod.com/show_bug.cgi?id=44 //$$ * See https://bugs.replaymod.com/show_bug.cgi?id=44
*/ //$$ */
public class FMLHandshakeFilter extends SimpleChannelInboundHandler<FMLHandshakeMessage> { //$$ public class FMLHandshakeFilter extends SimpleChannelInboundHandler<FMLHandshakeMessage> {
@Override //$$ @Override
protected void channelRead0(ChannelHandlerContext ctx, FMLHandshakeMessage msg) throws Exception { //$$ protected void channelRead0(ChannelHandlerContext ctx, FMLHandshakeMessage msg) throws Exception {
//#if MC>=10800 //#if MC>=10800
if (!(msg instanceof FMLHandshakeMessage.RegistryData)) { //$$ if (!(msg instanceof FMLHandshakeMessage.RegistryData)) {
//#else //#else
//$$ if (!(msg instanceof FMLHandshakeMessage.ModIdData)) { //$$ if (!(msg instanceof FMLHandshakeMessage.ModIdData)) {
//#endif //#endif
// Pass on everything but RegistryData messages //$$ // Pass on everything but RegistryData messages
ctx.fireChannelRead(msg); //$$ ctx.fireChannelRead(msg);
} //$$ }
} //$$ }
} //$$ }
//#endif

View File

@@ -17,7 +17,11 @@ import net.minecraftforge.client.event.GuiScreenEvent;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
//#if MC>=10800 //#if MC>=10800
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; //#if MC>=11300
import net.minecraftforge.eventbus.api.SubscribeEvent;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#endif
//#else //#else
//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent; //$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
//#endif //#endif

View File

@@ -7,11 +7,15 @@ import net.minecraft.item.ItemStack;
import net.minecraft.network.Packet; import net.minecraft.network.Packet;
import net.minecraft.network.play.server.*; import net.minecraft.network.play.server.*;
import net.minecraft.server.integrated.IntegratedServer; import net.minecraft.server.integrated.IntegratedServer;
import net.minecraftforge.event.entity.minecart.MinecartInteractEvent; // FIXME not (yet?) 1.13 import net.minecraftforge.event.entity.minecart.MinecartInteractEvent;
import net.minecraftforge.event.entity.player.PlayerSleepInBedEvent; import net.minecraftforge.event.entity.player.PlayerSleepInBedEvent;
//#if MC>=10904 //#if MC>=10904
import net.minecraft.entity.EntityLiving; //#if MC>=11300
import net.minecraft.entity.EntityLivingBase;
//#else
//$$ import net.minecraft.entity.EntityLiving;
//#endif
import net.minecraft.inventory.EntityEquipmentSlot; import net.minecraft.inventory.EntityEquipmentSlot;
import net.minecraft.network.datasync.EntityDataManager; import net.minecraft.network.datasync.EntityDataManager;
import net.minecraft.util.EnumHand; import net.minecraft.util.EnumHand;
@@ -25,7 +29,11 @@ import net.minecraft.util.math.BlockPos;
//#endif //#endif
//#if MC>=10800 //#if MC>=10800
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; //#if MC>=11300
import net.minecraftforge.eventbus.api.SubscribeEvent;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#endif
import net.minecraftforge.fml.common.gameevent.PlayerEvent.ItemPickupEvent; import net.minecraftforge.fml.common.gameevent.PlayerEvent.ItemPickupEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent; import net.minecraftforge.fml.common.gameevent.TickEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent.PlayerTickEvent; import net.minecraftforge.fml.common.gameevent.TickEvent.PlayerTickEvent;
@@ -42,7 +50,7 @@ import static com.replaymod.core.versions.MCVer.*;
public class RecordingEventHandler { public class RecordingEventHandler {
private final Minecraft mc = Minecraft.getMinecraft(); private final Minecraft mc = getMinecraft();
private final PacketListener packetListener; private final PacketListener packetListener;
private Double lastX, lastY, lastZ; private Double lastX, lastY, lastZ;
@@ -75,6 +83,12 @@ public class RecordingEventHandler {
} }
} }
//#if MC>=11300
public void onPacket(Packet<?> packet) {
packetListener.save(packet);
}
//#endif
public void onPlayerJoin() { public void onPlayerJoin() {
try { try {
//#if MC>=10904 //#if MC>=10904
@@ -171,9 +185,15 @@ public class RecordingEventHandler {
byte newPitch = (byte) ((int) (e.player.rotationPitch * 256.0F / 360.0F)); byte newPitch = (byte) ((int) (e.player.rotationPitch * 256.0F / 360.0F));
//#if MC>=10904 //#if MC>=10904
packet = new SPacketEntity.S17PacketEntityLookMove(e.player.getEntityId(), //#if MC>=11300
packet = new SPacketEntity.Move(
//#else
//$$ packet = new SPacketEntity.S17PacketEntityLookMove(
//#endif
e.player.getEntityId(),
(short) Math.round(dx * 4096), (short) Math.round(dy * 4096), (short) Math.round(dz * 4096), (short) Math.round(dx * 4096), (short) Math.round(dy * 4096), (short) Math.round(dz * 4096),
newYaw, newPitch, e.player.onGround); newYaw, newPitch, e.player.onGround
);
//#else //#else
//$$ packet = new S14PacketEntity.S17PacketEntityLookMove(e.player.getEntityId(), //$$ packet = new S14PacketEntity.S17PacketEntityLookMove(e.player.getEntityId(),
//$$ (byte) Math.round(dx * 32), (byte) Math.round(dy * 32), (byte) Math.round(dz * 32), //$$ (byte) Math.round(dx * 32), (byte) Math.round(dy * 32), (byte) Math.round(dz * 32),
@@ -300,9 +320,15 @@ public class RecordingEventHandler {
//Leaving Ride //Leaving Ride
if((!player(mc).isRiding() && lastRiding != -1) || //#if MC>=11300
(player(mc).isRiding() && lastRiding != getRidingEntity(player(mc)).getEntityId())) { if((!player(mc).isPassenger() && lastRiding != -1) ||
if(!player(mc).isRiding()) { (player(mc).isPassenger() && lastRiding != getRidingEntity(player(mc)).getEntityId())) {
if(!player(mc).isPassenger()) {
//#else
//$$ if((!player(mc).isRiding() && lastRiding != -1) ||
//$$ (player(mc).isRiding() && lastRiding != getRidingEntity(player(mc)).getEntityId())) {
//$$ if(!player(mc).isRiding()) {
//#endif
lastRiding = -1; lastRiding = -1;
} else { } else {
lastRiding = getRidingEntity(player(mc)).getEntityId(); lastRiding = getRidingEntity(player(mc)).getEntityId();
@@ -331,7 +357,11 @@ public class RecordingEventHandler {
lastActiveHand = player(mc).getActiveHand(); lastActiveHand = player(mc).getActiveHand();
EntityDataManager dataManager = new EntityDataManager(null); EntityDataManager dataManager = new EntityDataManager(null);
int state = (wasHandActive ? 1 : 0) | (lastActiveHand == EnumHand.OFF_HAND ? 2 : 0); int state = (wasHandActive ? 1 : 0) | (lastActiveHand == EnumHand.OFF_HAND ? 2 : 0);
dataManager.register(EntityLiving.HAND_STATES, (byte) state); //#if MC>=11300
dataManager.register(EntityLivingBase.LIVING_FLAGS, (byte) state);
//#else
//$$ dataManager.register(EntityLiving.HAND_STATES, (byte) state);
//#endif
packetListener.save(new SPacketEntityMetadata(player(mc).getEntityId(), dataManager, true)); packetListener.save(new SPacketEntityMetadata(player(mc).getEntityId(), dataManager, true));
} }
//#endif //#endif
@@ -346,8 +376,17 @@ public class RecordingEventHandler {
try { try {
//#if MC>=11100 //#if MC>=11100
//#if MC>=11200 //#if MC>=11200
packetListener.save(new SPacketCollectItem(event.pickedUp.getEntityId(), event.player.getEntityId(), //#if MC>=11300
event.pickedUp.getItem().getMaxStackSize())); ItemStack stack = event.getStack();
packetListener.save(new SPacketCollectItem(
event.getOriginalEntity().getEntityId(),
event.getPlayer().getEntityId(),
event.getStack().getCount()
));
//#else
//$$ packetListener.save(new SPacketCollectItem(event.pickedUp.getEntityId(), event.player.getEntityId(),
//$$ event.pickedUp.getItem().getMaxStackSize()));
//#endif
//#else //#else
//$$ packetListener.save(new SPacketCollectItem(event.pickedUp.getEntityId(), event.player.getEntityId(), //$$ packetListener.save(new SPacketCollectItem(event.pickedUp.getEntityId(), event.player.getEntityId(),
//$$ event.pickedUp.getEntityItem().getMaxStackSize())); //$$ event.pickedUp.getEntityItem().getMaxStackSize()));
@@ -394,6 +433,7 @@ public class RecordingEventHandler {
} }
} }
/* FIXME event not (yet?) on 1.13
@SubscribeEvent @SubscribeEvent
public void enterMinecart(MinecartInteractEvent event) { public void enterMinecart(MinecartInteractEvent event) {
try { try {
@@ -418,6 +458,7 @@ public class RecordingEventHandler {
e.printStackTrace(); e.printStackTrace();
} }
} }
*/
//#if MC>=10800 //#if MC>=10800
public void onBlockBreakAnim(int breakerId, BlockPos pos, int progress) { public void onBlockBreakAnim(int breakerId, BlockPos pos, int progress) {

View File

@@ -0,0 +1,36 @@
//#if MC>=11300
package com.replaymod.recording.mixin;
import com.google.common.util.concurrent.ListenableFuture;
import com.replaymod.recording.packet.ResourcePackRecorder;
import de.johni0702.minecraft.gui.utils.Consumer;
import net.minecraft.client.resources.DownloadingPackFinder;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import java.io.File;
@Mixin(DownloadingPackFinder.class)
public abstract class MixinDownloadingPackFinder implements ResourcePackRecorder.IDownloadingPackFinder {
private Consumer<File> requestCallback;
@Override
public void setRequestCallback(Consumer<File> callback) {
requestCallback = callback;
}
@Shadow
public abstract ListenableFuture<Object> func_195741_a(File file);
@Redirect(method = "downloadResourcePack", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/DownloadingPackFinder;func_195741_a(Ljava/io/File;)Lcom/google/common/util/concurrent/ListenableFuture;"))
private ListenableFuture<Object> recordDownloadedPack(DownloadingPackFinder downloadingPackFinder, File file) {
if (requestCallback != null) {
requestCallback.consume(file);
requestCallback = null;
}
return func_195741_a(file);
}
}
//#endif

View File

@@ -0,0 +1,45 @@
//#if MC>=11300
package com.replaymod.recording.mixin;
import com.replaymod.core.versions.MCVer;
import com.replaymod.extras.advancedscreenshots.AdvancedScreenshots;
import com.replaymod.replay.ReplayModReplay;
import net.minecraft.client.KeyboardListener;
import net.minecraft.client.settings.KeyBinding;
import net.minecraft.client.shader.Framebuffer;
import net.minecraft.util.ScreenShotHelper;
import net.minecraft.util.text.ITextComponent;
import net.minecraftforge.fml.hooks.BasicEventHooks;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.io.File;
import java.util.function.Consumer;
@Mixin(KeyboardListener.class)
public abstract class MixinKeyboardListener {
@Inject(method = "onKeyEvent", at = @At("HEAD"), cancellable = true)
private void dispatchKeyEvent(long windowPointer, int key, int scanCode, int action, int modifiers, CallbackInfo ci) {
// FIXME this is a hack and will hopefully be done properly by forge before its release (MinecraftForge#5481)
// (and therefore before RM's release), this code will have to be removed then and handling code updated
KeyBinding keyBindChat = MCVer.getMinecraft().gameSettings.keyBindChat;
if (action == 1 && keyBindChat.matchesKey(key, scanCode)) {
keyBindChat.pressTime++;
BasicEventHooks.fireKeyInput();
ci.cancel();
}
}
@Redirect(method = "onKeyEvent", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/ScreenShotHelper;saveScreenshot(Ljava/io/File;IILnet/minecraft/client/shader/Framebuffer;Ljava/util/function/Consumer;)V"))
private void takeScreenshot(File p_148260_0_, int p_148260_1_, int p_148260_2_, Framebuffer p_148260_3_, Consumer<ITextComponent> p_148260_4_) {
if (ReplayModReplay.instance.getReplayHandler() != null) {
AdvancedScreenshots.take();
} else {
ScreenShotHelper.saveScreenshot(p_148260_0_, p_148260_1_, p_148260_2_, p_148260_3_, p_148260_4_);
}
}
}
//#endif

View File

@@ -0,0 +1,41 @@
//#if MC>=11300
package com.replaymod.recording.mixin;
import com.replaymod.replay.InputReplayTimer;
import com.replaymod.replay.ReplayModReplay;
import net.minecraft.client.MouseHelper;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
@Mixin(MouseHelper.class)
public abstract class MixinMouseHelper {
//#ifdef DEV_ENV
@Shadow
private boolean mouseGrabbed;
@Inject(method = "grabMouse", at = @At("HEAD"), cancellable = true)
private void noGrab(CallbackInfo ci) {
// Used to be provided by Forge for 1.12.2 and below
if (Boolean.valueOf(System.getProperty("fml.noGrab", "false"))) {
mouseGrabbed = true;
ci.cancel();
}
}
//#endif
@Inject(method = "scrollCallback",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/entity/EntityPlayerSP;isSpectator()Z"),
locals = LocalCapture.CAPTURE_FAILHARD,
cancellable = true)
private void handleReplayModScroll(long _p0, double _p1, double _p2, CallbackInfo ci, double _l1, double yOffsetAccumulated) {
if (ReplayModReplay.instance.getReplayHandler() != null) {
InputReplayTimer.handleScroll((int) yOffsetAccumulated);
ci.cancel();
}
}
}
//#endif

View File

@@ -9,11 +9,17 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
//#if MC>=11300
import com.replaymod.core.versions.MCVer;
import com.replaymod.recording.handler.RecordingEventHandler.RecordingEventSender;
import net.minecraft.network.login.server.SPacketCustomPayloadLogin;
//#else
//#if MC>=10800 //#if MC>=10800
import net.minecraftforge.fml.common.network.FMLNetworkEvent; //$$ import net.minecraftforge.fml.common.network.FMLNetworkEvent;
//#else //#else
//$$ import cpw.mods.fml.common.network.FMLNetworkEvent; //$$ import cpw.mods.fml.common.network.FMLNetworkEvent;
//#endif //#endif
//#endif
@Mixin(NetHandlerLoginClient.class) @Mixin(NetHandlerLoginClient.class)
public abstract class MixinNetHandlerLoginClient { public abstract class MixinNetHandlerLoginClient {
@@ -25,19 +31,33 @@ public abstract class MixinNetHandlerLoginClient {
//$$ private NetworkManager field_147393_d; //$$ private NetworkManager field_147393_d;
//#endif //#endif
/** //#if MC>=11300
* Starts the recording right before switching into PLAY state. @Inject(method = "func_209521_a", at=@At("HEAD"))
* We cannot use the {@link FMLNetworkEvent.ClientConnectedToServerEvent} private void replayModRecording_initiateRecording(SPacketCustomPayloadLogin packet, CallbackInfo ci) {
* as it only fires after the forge handshake. RecordingEventSender eventSender = (RecordingEventSender) MCVer.getMinecraft().renderGlobal;
*/ if (eventSender.getRecordingEventHandler() != null) {
@Inject(method = "handleLoginSuccess", at=@At("HEAD")) return; // already recording
public void replayModRecording_initiateRecording(CallbackInfo cb) { }
//#if MC>=10800
ReplayModRecording.instance.initiateRecording(networkManager); ReplayModRecording.instance.initiateRecording(networkManager);
if (eventSender.getRecordingEventHandler() != null) {
eventSender.getRecordingEventHandler().onPacket(packet);
}
}
//#else
//$$ /**
//$$ * Starts the recording right before switching into PLAY state.
//$$ * We cannot use the {@link FMLNetworkEvent.ClientConnectedToServerEvent}
//$$ * as it only fires after the forge handshake.
//$$ */
//$$ @Inject(method = "handleLoginSuccess", at=@At("HEAD"))
//$$ public void replayModRecording_initiateRecording(CallbackInfo cb) {
//#if MC>=10800
//$$ ReplayModRecording.instance.initiateRecording(networkManager);
//#else //#else
//$$ ReplayModRecording.instance.initiateRecording(field_147393_d); //$$ ReplayModRecording.instance.initiateRecording(field_147393_d);
//#endif //#endif
} //$$ }
//#endif
//#if MC>=11200 //#if MC>=11200
@Inject(method = "handleLoginSuccess", at=@At("RETURN")) @Inject(method = "handleLoginSuccess", at=@At("RETURN"))

View File

@@ -1,5 +1,6 @@
package com.replaymod.recording.mixin; package com.replaymod.recording.mixin;
import com.replaymod.core.versions.MCVer;
import com.replaymod.recording.handler.RecordingEventHandler; import com.replaymod.recording.handler.RecordingEventHandler;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.network.NetHandlerPlayClient; import net.minecraft.client.network.NetHandlerPlayClient;
@@ -30,8 +31,7 @@ import static com.replaymod.core.versions.MCVer.*;
@Mixin(NetHandlerPlayClient.class) @Mixin(NetHandlerPlayClient.class)
public abstract class MixinNetHandlerPlayClient { public abstract class MixinNetHandlerPlayClient {
@Shadow private static Minecraft gameController = MCVer.getMinecraft();
private Minecraft gameController;
//#if MC>=10800 //#if MC>=10800
@Shadow @Shadow

View File

@@ -1,75 +1,77 @@
package com.replaymod.recording.mixin; //#if MC<11300
//$$ package com.replaymod.recording.mixin;
import com.replaymod.recording.handler.FMLHandshakeFilter; //$$
import io.netty.channel.ChannelPipeline; //$$ import com.replaymod.recording.handler.FMLHandshakeFilter;
import io.netty.channel.embedded.EmbeddedChannel; //$$ import io.netty.channel.ChannelPipeline;
import org.spongepowered.asm.mixin.Mixin; //$$ import io.netty.channel.embedded.EmbeddedChannel;
import org.spongepowered.asm.mixin.Shadow; //$$ import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; //$$ import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.Inject; //$$ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; //$$ import org.spongepowered.asm.mixin.injection.Inject;
//$$ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
//$$
//#if MC>=11200 //#if MC>=11200
import io.netty.channel.ChannelConfig; //$$ import io.netty.channel.ChannelConfig;
import net.minecraft.network.EnumConnectionState; //$$ import net.minecraft.network.EnumConnectionState;
import net.minecraft.network.NetworkManager; //$$ import net.minecraft.network.NetworkManager;
import org.spongepowered.asm.mixin.injection.Redirect; //$$ import org.spongepowered.asm.mixin.injection.Redirect;
//#endif //#endif
//$$
//#if MC>=10800 //#if MC>=10800
import net.minecraftforge.fml.common.network.handshake.FMLHandshakeCodec; //$$ import net.minecraftforge.fml.common.network.handshake.FMLHandshakeCodec;
import net.minecraftforge.fml.common.network.handshake.NetworkDispatcher; //$$ import net.minecraftforge.fml.common.network.handshake.NetworkDispatcher;
import net.minecraftforge.fml.relauncher.Side; //$$ import net.minecraftforge.fml.relauncher.Side;
//#else //#else
//$$ import cpw.mods.fml.common.network.handshake.FMLHandshakeCodec; //$$ import cpw.mods.fml.common.network.handshake.FMLHandshakeCodec;
//$$ import cpw.mods.fml.common.network.handshake.NetworkDispatcher; //$$ import cpw.mods.fml.common.network.handshake.NetworkDispatcher;
//$$ import cpw.mods.fml.relauncher.Side; //$$ import cpw.mods.fml.relauncher.Side;
//#endif //#endif
//$$
@Mixin(value = NetworkDispatcher.class, remap = false) //$$ @Mixin(value = NetworkDispatcher.class, remap = false)
public abstract class MixinNetworkDispatcher { //$$ public abstract class MixinNetworkDispatcher {
//$$
@Shadow //$$ @Shadow
private Side side; //$$ private Side side;
//$$
@Shadow //$$ @Shadow
private EmbeddedChannel handshakeChannel; //$$ private EmbeddedChannel handshakeChannel;
//$$
/** //$$ /**
* Always sets fml:isLocal to false on the server side. //$$ * Always sets fml:isLocal to false on the server side.
* This effectively removes the difference in the FML handshake between SP and MP //$$ * This effectively removes the difference in the FML handshake between SP and MP
* and forces the block/item ids, etc. to always be send. //$$ * and forces the block/item ids, etc. to always be send.
* Injects a {@link FMLHandshakeFilter} on the client side to filter out //$$ * Injects a {@link FMLHandshakeFilter} on the client side to filter out
* those extra, unexpected packets. //$$ * those extra, unexpected packets.
*/ //$$ */
@Inject(method = "insertIntoChannel", at=@At("HEAD")) //$$ @Inject(method = "insertIntoChannel", at=@At("HEAD"))
public void replayModRecording_setupForLocalRecording(CallbackInfo cb) { //$$ public void replayModRecording_setupForLocalRecording(CallbackInfo cb) {
// If we're in multiplayer, everything is fine as is //$$ // If we're in multiplayer, everything is fine as is
if (!handshakeChannel.attr(NetworkDispatcher.IS_LOCAL).get()) return; //$$ if (!handshakeChannel.attr(NetworkDispatcher.IS_LOCAL).get()) return;
//$$
if (side == Side.SERVER) { //$$ if (side == Side.SERVER) {
// On the server side, force all packets to be sent //$$ // On the server side, force all packets to be sent
handshakeChannel.attr(NetworkDispatcher.IS_LOCAL).set(false); //$$ handshakeChannel.attr(NetworkDispatcher.IS_LOCAL).set(false);
} else { //$$ } else {
// On the client side, discard additional packets //$$ // On the client side, discard additional packets
ChannelPipeline pipeline = handshakeChannel.pipeline(); //$$ ChannelPipeline pipeline = handshakeChannel.pipeline();
pipeline.addAfter(pipeline.context(FMLHandshakeCodec.class).name(), //$$ pipeline.addAfter(pipeline.context(FMLHandshakeCodec.class).name(),
"replaymod_filter", new FMLHandshakeFilter()); //$$ "replaymod_filter", new FMLHandshakeFilter());
} //$$ }
} //$$ }
//$$
//#if MC>=11200 //#if MC>=11200
@Redirect(method = "clientListenForServerHandshake", at = @At(value = "INVOKE", remap = true, target = //$$ @Redirect(method = "clientListenForServerHandshake", at = @At(value = "INVOKE", remap = true, target =
"Lnet/minecraft/network/NetworkManager;setConnectionState(Lnet/minecraft/network/EnumConnectionState;)V")) //$$ "Lnet/minecraft/network/NetworkManager;setConnectionState(Lnet/minecraft/network/EnumConnectionState;)V"))
public void replayModRecording_raceConditionWorkAround1(NetworkManager self, EnumConnectionState ignored) { } //$$ public void replayModRecording_raceConditionWorkAround1(NetworkManager self, EnumConnectionState ignored) { }
//$$
@Redirect(method = "insertIntoChannel", at = @At(value = "INVOKE", target = //$$ @Redirect(method = "insertIntoChannel", at = @At(value = "INVOKE", target =
"Lio/netty/channel/ChannelConfig;setAutoRead(Z)Lio/netty/channel/ChannelConfig;")) //$$ "Lio/netty/channel/ChannelConfig;setAutoRead(Z)Lio/netty/channel/ChannelConfig;"))
public ChannelConfig replayModRecording_raceConditionWorkAround2(ChannelConfig self, boolean autoRead) { //$$ public ChannelConfig replayModRecording_raceConditionWorkAround2(ChannelConfig self, boolean autoRead) {
if (side == Side.CLIENT) { //$$ if (side == Side.CLIENT) {
autoRead = false; //$$ autoRead = false;
} //$$ }
return self.setAutoRead(autoRead); //$$ return self.setAutoRead(autoRead);
} //$$ }
//#endif //#endif
} //$$ }
//#endif

View File

@@ -1,32 +1,32 @@
package com.replaymod.recording.mixin; package com.replaymod.recording.mixin;
//#if MC>=10904 //#if MC>=10904 && MC<11300
import com.replaymod.recording.handler.RecordingEventHandler; //$$ import com.replaymod.recording.handler.RecordingEventHandler;
import net.minecraft.client.Minecraft; //$$ import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.PlayerControllerMP; //$$ import net.minecraft.client.multiplayer.PlayerControllerMP;
import net.minecraft.util.math.BlockPos; //$$ import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World; //$$ import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin; //$$ import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; //$$ import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At; //$$ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect; //$$ import org.spongepowered.asm.mixin.injection.Redirect;
//$$
import static com.replaymod.core.versions.MCVer.*; //$$ import static com.replaymod.core.versions.MCVer.*;
//$$
@Mixin(PlayerControllerMP.class) //$$ @Mixin(PlayerControllerMP.class)
public abstract class MixinPlayerControllerMP implements RecordingEventHandler.RecordingEventSender { //$$ public abstract class MixinPlayerControllerMP implements RecordingEventHandler.RecordingEventSender {
//$$
@Shadow //$$ @Shadow
private Minecraft mc; //$$ private Minecraft mc;
//$$
// Redirects the call to playEvent without the initial player argument to the method with that argument //$$ // Redirects the call to playEvent without the initial player argument to the method with that argument
// The new method will then play it and (if applicable) record it. (See MixinWorldClient) //$$ // The new method will then play it and (if applicable) record it. (See MixinWorldClient)
// This is necessary for the block break event (particles and sound) to be recorded. Otherwise it looks like the //$$ // This is necessary for the block break event (particles and sound) to be recorded. Otherwise it looks like the
// event was emitted because of a packet (player will be null) and not as it actually was (by the player). //$$ // event was emitted because of a packet (player will be null) and not as it actually was (by the player).
@Redirect(method = "onPlayerDestroyBlock", at = @At(value = "INVOKE", //$$ @Redirect(method = "onPlayerDestroyBlock", at = @At(value = "INVOKE",
target = "Lnet/minecraft/world/World;playEvent(ILnet/minecraft/util/math/BlockPos;I)V")) //$$ target = "Lnet/minecraft/world/World;playEvent(ILnet/minecraft/util/math/BlockPos;I)V"))
public void replayModRecording_playEvent_fixed(World world, int type, BlockPos pos, int data) { //$$ public void replayModRecording_playEvent_fixed(World world, int type, BlockPos pos, int data) {
world.playEvent(player(mc), type, pos, data); //$$ world.playEvent(player(mc), type, pos, data);
} //$$ }
} //$$ }
//#endif //#endif

View File

@@ -1,12 +1,17 @@
package com.replaymod.recording.mixin; package com.replaymod.recording.mixin;
import com.replaymod.recording.handler.RecordingEventHandler; import com.replaymod.recording.handler.RecordingEventHandler;
import net.minecraft.client.renderer.RenderGlobal;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
//#if MC>=11300
import net.minecraft.client.renderer.WorldRenderer;
//#else
//$$ import net.minecraft.client.renderer.RenderGlobal;
//#endif
//#if MC>=10904 //#if MC>=10904
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
//#else //#else
@@ -15,7 +20,11 @@ import net.minecraft.util.math.BlockPos;
//#endif //#endif
//#endif //#endif
@Mixin(RenderGlobal.class) //#if MC>=11300
@Mixin(WorldRenderer.class)
//#else
//$$ @Mixin(RenderGlobal.class)
//#endif
public abstract class MixinRenderGlobal implements RecordingEventHandler.RecordingEventSender { public abstract class MixinRenderGlobal implements RecordingEventHandler.RecordingEventSender {
private RecordingEventHandler recordingEventHandler; private RecordingEventHandler recordingEventHandler;

View File

@@ -10,7 +10,6 @@ import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvent; import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraft.world.WorldProvider;
import net.minecraft.world.storage.ISaveHandler; import net.minecraft.world.storage.ISaveHandler;
import net.minecraft.world.storage.WorldInfo; import net.minecraft.world.storage.WorldInfo;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
@@ -19,6 +18,13 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
//#if MC>=11300
import net.minecraft.world.dimension.Dimension;
import net.minecraft.world.storage.WorldSavedDataStorage;
//#else
//$$ import net.minecraft.world.WorldProvider;
//#endif
import static com.replaymod.core.versions.MCVer.*; import static com.replaymod.core.versions.MCVer.*;
@Mixin(WorldClient.class) @Mixin(WorldClient.class)
@@ -26,8 +32,22 @@ public abstract class MixinWorldClient extends World implements RecordingEventHa
@Shadow @Shadow
private Minecraft mc; private Minecraft mc;
protected MixinWorldClient(ISaveHandler saveHandlerIn, WorldInfo info, WorldProvider providerIn, Profiler profilerIn, boolean client) { protected MixinWorldClient(ISaveHandler saveHandlerIn,
super(saveHandlerIn, info, providerIn, profilerIn, client); //#if MC>=11300
WorldSavedDataStorage mapStorage,
//#endif
WorldInfo info,
//#if MC>=11300
Dimension providerIn,
//#else
//$$ WorldProvider providerIn,
//#endif
Profiler profilerIn, boolean client) {
super(saveHandlerIn,
//#if MC>=11300
mapStorage,
//#endif
info, providerIn, profilerIn, client);
} }
private RecordingEventHandler replayModRecording_getRecordingEventHandler() { private RecordingEventHandler replayModRecording_getRecordingEventHandler() {

View File

@@ -1,9 +1,16 @@
package com.replaymod.recording.packet; package com.replaymod.recording.packet;
import com.replaymod.core.ReplayMod;
import com.replaymod.core.utils.Restrictions; import com.replaymod.core.utils.Restrictions;
import com.replaymod.editor.gui.MarkerProcessor;
import com.replaymod.recording.ReplayModRecording;
import com.replaymod.recording.Setting;
import com.replaymod.recording.handler.ConnectionEventHandler;
import com.replaymod.replaystudio.data.Marker; import com.replaymod.replaystudio.data.Marker;
import com.replaymod.replaystudio.replay.ReplayFile; import com.replaymod.replaystudio.replay.ReplayFile;
import com.replaymod.replaystudio.replay.ReplayMetaData; import com.replaymod.replaystudio.replay.ReplayMetaData;
import de.johni0702.minecraft.gui.element.GuiLabel;
import de.johni0702.minecraft.gui.utils.Colors;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
@@ -17,6 +24,10 @@ import net.minecraft.network.play.server.*;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
//#if MC>=11300
import net.minecraft.network.login.server.SPacketLoginSuccess;
//#endif
//#if MC>=10904 //#if MC>=10904
import net.minecraft.network.datasync.EntityDataManager; import net.minecraft.network.datasync.EntityDataManager;
import net.minecraft.util.text.TextComponentString; import net.minecraft.util.text.TextComponentString;
@@ -29,13 +40,16 @@ import net.minecraft.util.text.TextComponentString;
//#if MC>=10800 //#if MC>=10800
import net.minecraft.network.EnumPacketDirection; import net.minecraft.network.EnumPacketDirection;
import net.minecraftforge.fml.common.network.internal.FMLProxyPacket; //#if MC<11300
//$$ import net.minecraftforge.fml.common.network.internal.FMLProxyPacket;
//#endif
//#else //#else
//$$ import cpw.mods.fml.common.network.internal.FMLProxyPacket; //$$ import cpw.mods.fml.common.network.internal.FMLProxyPacket;
//#endif //#endif
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Path;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
@@ -49,9 +63,11 @@ import static com.replaymod.core.versions.MCVer.*;
public class PacketListener extends ChannelInboundHandlerAdapter { public class PacketListener extends ChannelInboundHandlerAdapter {
private static final Minecraft mc = Minecraft.getMinecraft(); private static final Minecraft mc = getMinecraft();
private static final Logger logger = LogManager.getLogger(); private static final Logger logger = LogManager.getLogger();
private final ReplayMod core;
private final Path outputPath;
private final ReplayFile replayFile; private final ReplayFile replayFile;
private final ResourcePackRecorder resourcePackRecorder; private final ResourcePackRecorder resourcePackRecorder;
@@ -67,6 +83,11 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
private long lastSentPacket; private long lastSentPacket;
private long timePassedWhilePaused; private long timePassedWhilePaused;
private volatile boolean serverWasPaused; private volatile boolean serverWasPaused;
//#if MC>=11300
private EnumConnectionState connectionState = EnumConnectionState.LOGIN;
//#else
//$$ private EnumConnectionState connectionState = EnumConnectionState.PLAY;
//#endif
/** /**
* Used to keep track of the last metadata save job submitted to the save service and * Used to keep track of the last metadata save job submitted to the save service and
@@ -74,7 +95,9 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
*/ */
private final AtomicInteger lastSaveMetaDataId = new AtomicInteger(); private final AtomicInteger lastSaveMetaDataId = new AtomicInteger();
public PacketListener(ReplayFile replayFile, ReplayMetaData metaData) throws IOException { public PacketListener(ReplayMod core, Path outputPath, ReplayFile replayFile, ReplayMetaData metaData) throws IOException {
this.core = core;
this.outputPath = outputPath;
this.replayFile = replayFile; this.replayFile = replayFile;
this.metaData = metaData; this.metaData = metaData;
this.resourcePackRecorder = new ResourcePackRecorder(replayFile); this.resourcePackRecorder = new ResourcePackRecorder(replayFile);
@@ -147,6 +170,12 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
}); });
//#if MC>=11300
if (packet instanceof SPacketLoginSuccess) {
connectionState = EnumConnectionState.PLAY;
}
//#endif
} catch(Exception e) { } catch(Exception e) {
logger.error("Writing packet:", e); logger.error("Writing packet:", e);
} }
@@ -157,21 +186,39 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
metaData.setDuration((int) lastSentPacket); metaData.setDuration((int) lastSentPacket);
saveMetaData(); saveMetaData();
saveService.shutdown(); core.runLater(() -> {
try { ConnectionEventHandler connectionEventHandler = ReplayModRecording.instance.getConnectionEventHandler();
saveService.awaitTermination(10, TimeUnit.SECONDS); if (connectionEventHandler.getPacketListener() == this) {
} catch (InterruptedException e) { connectionEventHandler.reset();
logger.error("Waiting for save service termination:", e);
}
synchronized (replayFile) {
try {
replayFile.save();
replayFile.close();
} catch (IOException e) {
logger.error("Saving replay file:", e);
} }
} });
GuiLabel savingLabel = new GuiLabel().setI18nText("replaymod.gui.replaysaving.title").setColor(Colors.BLACK);
new Thread(() -> {
core.runLater(() -> core.getBackgroundProcesses().addProcess(savingLabel));
saveService.shutdown();
try {
saveService.awaitTermination(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
logger.error("Waiting for save service termination:", e);
}
synchronized (replayFile) {
try {
replayFile.save();
replayFile.close();
if (core.getSettingsRegistry().get(Setting.AUTO_POST_PROCESS)) {
MarkerProcessor.apply(outputPath, progress -> {});
}
} catch (IOException e) {
logger.error("Saving replay file:", e);
}
}
core.runLater(() -> core.getBackgroundProcesses().removeProcess(savingLabel));
}).start();
} }
@Override @Override
@@ -229,16 +276,18 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
//#endif //#endif
//#endif //#endif
if (packet instanceof FMLProxyPacket) { //#if MC<11300
// This packet requires special handling //$$ if (packet instanceof FMLProxyPacket) {
//$$ // This packet requires special handling
//#if MC>=10800 //#if MC>=10800
((FMLProxyPacket) packet).toS3FPackets().forEach(this::save); //$$ ((FMLProxyPacket) packet).toS3FPackets().forEach(this::save);
//#else //#else
//$$ save(((FMLProxyPacket) packet).toS3FPacket()); //$$ save(((FMLProxyPacket) packet).toS3FPacket());
//#endif //#endif
super.channelRead(ctx, msg); //$$ super.channelRead(ctx, msg);
return; //$$ return;
} //$$ }
//#endif
save(packet); save(packet);
@@ -337,9 +386,9 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
//#endif //#endif
//#if MC>=10800 //#if MC>=10800
Integer packetId = EnumConnectionState.PLAY.getPacketId(EnumPacketDirection.CLIENTBOUND, packet); Integer packetId = connectionState.getPacketId(EnumPacketDirection.CLIENTBOUND, packet);
//#else //#else
//$$ Integer packetId = (Integer) EnumConnectionState.PLAY.func_150755_b().inverse().get(packet.getClass()); //$$ Integer packetId = (Integer) connectionState.func_150755_b().inverse().get(packet.getClass());
//#endif //#endif
if (packetId == null) { if (packetId == null) {
throw new IOException("Unknown packet type:" + packet.getClass()); throw new IOException("Unknown packet type:" + packet.getClass());
@@ -357,11 +406,15 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
return array; return array;
} }
public void addMarker() { public void addMarker(String name) {
Entity view = getRenderViewEntity(Minecraft.getMinecraft()); addMarker(name, (int) getCurrentDuration());
int timestamp = (int) (System.currentTimeMillis() - startTime); }
public void addMarker(String name, int timestamp) {
Entity view = getRenderViewEntity(mc);
Marker marker = new Marker(); Marker marker = new Marker();
marker.setName(name);
marker.setTime(timestamp); marker.setTime(timestamp);
marker.setX(view.posX); marker.setX(view.posX);
marker.setY(view.posY); marker.setY(view.posY);
@@ -382,6 +435,10 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
}); });
} }
public long getCurrentDuration() {
return lastSentPacket;
}
public void setServerWasPaused() { public void setServerWasPaused() {
this.serverWasPaused = true; this.serverWasPaused = true;
} }

View File

@@ -4,16 +4,22 @@ import com.google.common.hash.Hashing;
import com.google.common.io.Files; import com.google.common.io.Files;
import com.replaymod.replaystudio.replay.ReplayFile; import com.replaymod.replaystudio.replay.ReplayFile;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiScreenWorking;
import net.minecraft.client.gui.GuiYesNo; import net.minecraft.client.gui.GuiYesNo;
import net.minecraft.client.gui.GuiYesNoCallback; import net.minecraft.client.gui.GuiYesNoCallback;
import net.minecraft.client.multiplayer.ServerData; import net.minecraft.client.multiplayer.ServerData;
import net.minecraft.client.resources.I18n; import net.minecraft.client.resources.I18n;
import net.minecraft.client.resources.ResourcePackRepository;
import net.minecraft.util.HttpUtil;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
//#if MC>=11300
import de.johni0702.minecraft.gui.utils.Consumer;
import net.minecraft.client.resources.DownloadingPackFinder;
//#else
//$$ import net.minecraft.client.gui.GuiScreenWorking;
//$$ import net.minecraft.client.resources.ResourcePackRepository;
//$$ import net.minecraft.util.HttpUtil;
//#endif
//#if MC>=10904 //#if MC>=10904
import net.minecraft.network.play.client.CPacketResourcePackStatus; import net.minecraft.network.play.client.CPacketResourcePackStatus;
import net.minecraft.network.play.client.CPacketResourcePackStatus.Action; import net.minecraft.network.play.client.CPacketResourcePackStatus.Action;
@@ -33,11 +39,11 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import net.minecraft.client.network.NetHandlerPlayClient; import net.minecraft.client.network.NetHandlerPlayClient;
import net.minecraft.network.NetworkManager; import net.minecraft.network.NetworkManager;
import org.apache.commons.io.FileUtils; //#if MC<11300
//$$ import org.apache.commons.io.FileUtils;
//#endif
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import static com.replaymod.core.versions.MCVer.*;
//#else //#else
//$$ import net.minecraft.client.multiplayer.ServerData.ServerResourceMode; //$$ import net.minecraft.client.multiplayer.ServerData.ServerResourceMode;
//$$ import net.minecraft.client.multiplayer.ServerList; //$$ import net.minecraft.client.multiplayer.ServerList;
@@ -52,12 +58,14 @@ import java.io.OutputStream;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import static com.replaymod.core.versions.MCVer.*;
/** /**
* Records resource packs and handles incoming resource pack packets during recording. * Records resource packs and handles incoming resource pack packets during recording.
*/ */
public class ResourcePackRecorder { public class ResourcePackRecorder {
private static final Logger logger = LogManager.getLogger(); private static final Logger logger = LogManager.getLogger();
private static final Minecraft mc = Minecraft.getMinecraft(); private static final Minecraft mc = getMinecraft();
private final ReplayFile replayFile; private final ReplayFile replayFile;
@@ -131,12 +139,12 @@ public class ResourcePackRecorder {
if (url.startsWith("level://")) { if (url.startsWith("level://")) {
String levelName = url.substring("level://".length()); String levelName = url.substring("level://".length());
File savesDir = new File(mc.mcDataDir, "saves"); File savesDir = new File(mcDataDir(mc), "saves");
final File levelDir = new File(savesDir, levelName); final File levelDir = new File(savesDir, levelName);
if (levelDir.isFile()) { if (levelDir.isFile()) {
netManager.sendPacket(makeStatusPacket(hash, Action.ACCEPTED)); netManager.sendPacket(makeStatusPacket(hash, Action.ACCEPTED));
Futures.addCallback(setServerResourcePack(mc.getResourcePackRepository(), levelDir), new FutureCallback<Object>() { Futures.addCallback(setServerResourcePack(levelDir), new FutureCallback<Object>() {
@Override @Override
public void onSuccess(Object result) { public void onSuccess(Object result) {
recordResourcePack(levelDir, requestId); recordResourcePack(levelDir, requestId);
@@ -163,7 +171,11 @@ public class ResourcePackRecorder {
//noinspection Convert2Lambda //noinspection Convert2Lambda
mc.addScheduledTask(() -> mc.displayGuiScreen(new GuiYesNo(new GuiYesNoCallback() { mc.addScheduledTask(() -> mc.displayGuiScreen(new GuiYesNo(new GuiYesNoCallback() {
@Override @Override
public void confirmClicked(boolean result, int id) { //#if MC>=11300
public void confirmResult(boolean result, int id) {
//#else
//$$ public void confirmClicked(boolean result, int id) {
//#endif
if (serverData != null) { if (serverData != null) {
serverData.setResourceMode(result ? ServerData.ServerResourceMode.ENABLED : ServerData.ServerResourceMode.DISABLED); serverData.setResourceMode(result ? ServerData.ServerResourceMode.ENABLED : ServerData.ServerResourceMode.DISABLED);
} }
@@ -202,100 +214,112 @@ public class ResourcePackRecorder {
}); });
} }
//#if MC>=11300
private ListenableFuture downloadResourcePack(final int requestId, String url, String hash) { private ListenableFuture downloadResourcePack(final int requestId, String url, String hash) {
final ResourcePackRepository repo = mc.mcResourcePackRepository; DownloadingPackFinder packFinder = mc.getPackFinder();
String fileName; ((IDownloadingPackFinder) packFinder).setRequestCallback(file -> recordResourcePack(file, requestId));
if (hash.matches("^[a-f0-9]{40}$")) { return packFinder.downloadResourcePack(url, hash);
fileName = hash; }
} else {
fileName = url.substring(url.lastIndexOf("/") + 1);
if (fileName.contains("?")) { public interface IDownloadingPackFinder {
fileName = fileName.substring(0, fileName.indexOf("?")); void setRequestCallback(Consumer<File> callback);
} }
//#else
if (!fileName.endsWith(".zip")) { //$$ private ListenableFuture downloadResourcePack(final int requestId, String url, String hash) {
return Futures.immediateFailedFuture(new IllegalArgumentException("Invalid filename; must end in .zip")); //$$ final ResourcePackRepository repo = mc.mcResourcePackRepository;
} //$$ String fileName;
//$$ if (hash.matches("^[a-f0-9]{40}$")) {
fileName = "legacy_" + fileName.replaceAll("\\W", ""); //$$ fileName = hash;
} //$$ } else {
//$$ fileName = url.substring(url.lastIndexOf("/") + 1);
final File file = new File(repo.dirServerResourcepacks, fileName); //$$
//$$ if (fileName.contains("?")) {
//$$ fileName = fileName.substring(0, fileName.indexOf("?"));
//$$ }
//$$
//$$ if (!fileName.endsWith(".zip")) {
//$$ return Futures.immediateFailedFuture(new IllegalArgumentException("Invalid filename; must end in .zip"));
//$$ }
//$$
//$$ fileName = "legacy_" + fileName.replaceAll("\\W", "");
//$$ }
//$$
//$$ final File file = new File(repo.dirServerResourcepacks, fileName);
//#if MC>=10809 //#if MC>=10809
repo.lock.lock(); //$$ repo.lock.lock();
//#else //#else
//$$ repo.field_177321_h.lock(); //$$ repo.field_177321_h.lock();
//#endif //#endif
try { //$$ try {
//#if MC>=10809 //#if MC>=10809
repo.clearResourcePack(); //$$ repo.clearResourcePack();
//#else //#else
//$$ repo.func_148529_f(); //$$ repo.func_148529_f();
//#endif //#endif
//$$
if (file.exists() && hash.length() == 40) { //$$ if (file.exists() && hash.length() == 40) {
try { //$$ try {
String fileHash = Hashing.sha1().hashBytes(Files.toByteArray(file)).toString(); //$$ String fileHash = Hashing.sha1().hashBytes(Files.toByteArray(file)).toString();
if (fileHash.equals(hash)) { //$$ if (fileHash.equals(hash)) {
recordResourcePack(file, requestId); //$$ recordResourcePack(file, requestId);
return setServerResourcePack(repo, file); //$$ return setServerResourcePack(file);
} //$$ }
//$$
logger.warn("File " + file + " had wrong hash (expected " + hash + ", found " + fileHash + "). Deleting it."); //$$ logger.warn("File " + file + " had wrong hash (expected " + hash + ", found " + fileHash + "). Deleting it.");
FileUtils.deleteQuietly(file); //$$ FileUtils.deleteQuietly(file);
} catch (IOException ioexception) { //$$ } catch (IOException ioexception) {
logger.warn("File " + file + " couldn\'t be hashed. Deleting it.", ioexception); //$$ logger.warn("File " + file + " couldn\'t be hashed. Deleting it.", ioexception);
FileUtils.deleteQuietly(file); //$$ FileUtils.deleteQuietly(file);
} //$$ }
} //$$ }
//$$
final GuiScreenWorking guiScreen = new GuiScreenWorking(); //$$ final GuiScreenWorking guiScreen = new GuiScreenWorking();
final Minecraft mc = Minecraft.getMinecraft(); //$$ final Minecraft mc = Minecraft.getMinecraft();
//$$
Futures.getUnchecked(mc.addScheduledTask(() -> mc.displayGuiScreen(guiScreen))); //$$ Futures.getUnchecked(mc.addScheduledTask(() -> mc.displayGuiScreen(guiScreen)));
//$$
//#if MC>=10809 //#if MC>=10809
//#if MC>=11002 //#if MC>=11002
//#if MC>=11100 //#if MC>=11100
Map<String, String> sessionInfo = ResourcePackRepository.getDownloadHeaders(); //$$ Map<String, String> sessionInfo = ResourcePackRepository.getDownloadHeaders();
//#else //#else
//$$ Map<String, String> sessionInfo = ResourcePackRepository.func_190115_a(); //$$ Map<String, String> sessionInfo = ResourcePackRepository.func_190115_a();
//#endif //#endif
//#else //#else
//$$ Map<String, String> sessionInfo = Minecraft.getSessionInfo(); //$$ Map<String, String> sessionInfo = Minecraft.getSessionInfo();
//#endif //#endif
repo.downloadingPacks = HttpUtil.downloadResourcePack(file, url, sessionInfo, 50 * 1024 * 1024, guiScreen, mc.getProxy()); //$$ repo.downloadingPacks = HttpUtil.downloadResourcePack(file, url, sessionInfo, 50 * 1024 * 1024, guiScreen, mc.getProxy());
Futures.addCallback(repo.downloadingPacks, new FutureCallback<Object>() { //$$ Futures.addCallback(repo.downloadingPacks, new FutureCallback<Object>() {
//#else //#else
//$$ Map sessionInfo = Minecraft.getSessionInfo(); //$$ Map sessionInfo = Minecraft.getSessionInfo();
//$$ repo.field_177322_i = HttpUtil.func_180192_a(file, url, sessionInfo, 50 * 1024 * 1024, guiScreen, mc.getProxy()); //$$ repo.field_177322_i = HttpUtil.func_180192_a(file, url, sessionInfo, 50 * 1024 * 1024, guiScreen, mc.getProxy());
//$$ Futures.addCallback(repo.field_177322_i, new FutureCallback() { //$$ Futures.addCallback(repo.field_177322_i, new FutureCallback() {
//#endif //#endif
@Override //$$ @Override
public void onSuccess(Object value) { //$$ public void onSuccess(Object value) {
recordResourcePack(file, requestId); //$$ recordResourcePack(file, requestId);
setServerResourcePack(repo, file); //$$ setServerResourcePack(file);
} //$$ }
//$$
@Override //$$ @Override
public void onFailure(@Nonnull Throwable throwable) { //$$ public void onFailure(@Nonnull Throwable throwable) {
throwable.printStackTrace(); //$$ throwable.printStackTrace();
} //$$ }
}); //$$ });
//#if MC>=10809 //#if MC>=10809
return repo.downloadingPacks; //$$ return repo.downloadingPacks;
//#else //#else
//$$ return repo.field_177322_i; //$$ return repo.field_177322_i;
//#endif //#endif
} finally { //$$ } finally {
//#if MC>=10809 //#if MC>=10809
repo.lock.unlock(); //$$ repo.lock.unlock();
//#else //#else
//$$ repo.field_177321_h.unlock(); //$$ repo.field_177321_h.unlock();
//#endif //#endif
} //$$ }
} //$$ }
//#endif
//#else //#else
//$$ public synchronized S3FPacketCustomPayload handleResourcePack(S3FPacketCustomPayload packet) { //$$ public synchronized S3FPacketCustomPayload handleResourcePack(S3FPacketCustomPayload packet) {
//$$ final int requestId = nextRequestId++; //$$ final int requestId = nextRequestId++;

View File

@@ -1,12 +1,29 @@
package com.replaymod.render; package com.replaymod.render;
import com.replaymod.core.versions.MCVer;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableColor;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.Getter; import lombok.Getter;
import net.minecraft.client.resources.I18n; import net.minecraft.client.resources.I18n;
import org.lwjgl.util.ReadableColor; import net.minecraft.util.Util;
import java.io.File; import java.io.File;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Optional;
import static com.replaymod.render.ReplayModRender.LOGGER;
//#if MC>=11300
import org.apache.maven.artifact.versioning.ComparableVersion;
//#else
//#if MC>=10800
//$$ import net.minecraftforge.fml.common.versioning.ComparableVersion;
//#else
//$$ import cpw.mods.fml.common.versioning.ComparableVersion;
//#endif
//#endif
@Data @Data
public class RenderSettings { public class RenderSettings {
@@ -21,6 +38,14 @@ public class RenderSettings {
public String getDescription() { public String getDescription() {
return I18n.format("replaymod.gui.rendersettings.renderer." + name().toLowerCase() + ".description"); return I18n.format("replaymod.gui.rendersettings.renderer." + name().toLowerCase() + ".description");
} }
public boolean isSpherical() {
return this == EQUIRECTANGULAR || this == ODS;
}
public boolean hasFixedAspectRatio() {
return this == EQUIRECTANGULAR || this == ODS || this == CUBIC;
}
} }
public enum EncodingPreset { public enum EncodingPreset {
@@ -49,7 +74,7 @@ public class RenderSettings {
} }
public String getValue() { public String getValue() {
return "-y -f rawvideo -pix_fmt rgb24 -s %WIDTH%x%HEIGHT% -r %FPS% -i - %FILTERS%" + preset; return "-y -f rawvideo -pix_fmt bgra -s %WIDTH%x%HEIGHT% -r %FPS% -i - %FILTERS%" + preset;
} }
public String getFileExtension() { public String getFileExtension() {
@@ -94,7 +119,9 @@ public class RenderSettings {
private final boolean stabilizePitch; private final boolean stabilizePitch;
private final boolean stabilizeRoll; private final boolean stabilizeRoll;
private final ReadableColor chromaKeyingColor; private final ReadableColor chromaKeyingColor;
private final boolean inject360Metadata; private final int sphericalFovX;
private final int sphericalFovY;
private final boolean injectSphericalMetadata;
private final AntiAliasing antiAliasing; private final AntiAliasing antiAliasing;
private final String exportCommand; private final String exportCommand;
@@ -131,12 +158,66 @@ public class RenderSettings {
} }
public String getVideoFilters() { public String getVideoFilters() {
if (antiAliasing == AntiAliasing.NONE) { StringBuilder filters = new StringBuilder();
return "";
} else { if (antiAliasing != AntiAliasing.NONE) {
double factor = 1.0 / antiAliasing.getFactor(); double factor = 1.0 / antiAliasing.getFactor();
return String.format("-vf scale=iw*%1$s:ih*%1$s ", factor); filters.append(String.format("-filter:v scale=iw*%1$s:ih*%1$s ", factor));
} }
return filters.toString();
}
public String getExportCommandOrDefault() {
return exportCommand.isEmpty() ? findFFmpeg() : exportCommand;
}
private static String findFFmpeg() {
switch (Util.getOSType()) {
case WINDOWS:
// Allow windows users to unpack the ffmpeg archive into a sub-folder of their .minecraft folder
File inDotMinecraft = new File(MCVer.mcDataDir(MCVer.getMinecraft()), "ffmpeg/bin/ffmpeg.exe");
if (inDotMinecraft.exists()) {
LOGGER.debug("FFmpeg found in .minecraft/ffmpeg");
return inDotMinecraft.getAbsolutePath();
}
break;
case OSX:
// The PATH doesn't seem to be set as expected on OSX, therefore we check some common locations ourselves
for (String path : new String[]{"/usr/local/bin/ffmpeg", "/usr/bin/ffmpeg"}) {
File file = new File(path);
if (file.exists()) {
LOGGER.debug("Found FFmpeg at {}", path);
return path;
} else {
LOGGER.debug("FFmpeg not located at {}", path);
}
}
// Homebrew doesn't seem to reliably symlink its installed binaries either
File homebrewFolder = new File("/usr/local/Cellar/ffmpeg");
String[] homebrewVersions = homebrewFolder.list();
if (homebrewVersions != null) {
Optional<File> latestOpt = Arrays.stream(homebrewVersions)
.map(ComparableVersion::new) // Convert file name to comparable version
.sorted(Comparator.reverseOrder()) // Sort for latest version
.map(ComparableVersion::toString) // Convert back to file name
.map(v -> new File(new File(homebrewFolder, v), "bin/ffmpeg")) // Convert to binary files
.filter(File::exists) // Filter invalid installations (missing executable)
.findFirst(); // Take first one
if (latestOpt.isPresent()) {
File latest = latestOpt.get();
LOGGER.debug("Found {} versions of FFmpeg installed with homebrew, chose {}",
homebrewVersions.length, latest);
return latest.getAbsolutePath();
}
}
break;
case LINUX: // Linux users are entrusted to have their PATH configured correctly (most package manager do this)
case SOLARIS: // Never heard of anyone running this mod on Solaris having any problems
case UNKNOWN: // Unknown OS, just try to use "ffmpeg"
}
LOGGER.debug("Using default FFmpeg executable");
return "ffmpeg";
} }
} }

View File

@@ -1,65 +1,57 @@
package com.replaymod.render; package com.replaymod.render;
import com.replaymod.core.Module;
import com.replaymod.core.ReplayMod; import com.replaymod.core.ReplayMod;
import com.replaymod.render.utils.RenderJob; import com.replaymod.render.utils.RenderJob;
import com.replaymod.replay.events.ReplayCloseEvent; import com.replaymod.replay.events.ReplayCloseEvent;
import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReport;
import net.minecraft.util.ReportedException;
import net.minecraftforge.common.config.Configuration;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
//#if MC>=10800 //#if MC>=10800
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; //#if MC>=11300
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.eventbus.api.SubscribeEvent;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#endif
//#else //#else
//$$ import cpw.mods.fml.common.Mod; //$$ import cpw.mods.fml.common.Mod;
//$$ import cpw.mods.fml.common.event.FMLPreInitializationEvent;
//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent; //$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
//#endif //#endif
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static com.replaymod.core.versions.MCVer.*; import static com.replaymod.core.versions.MCVer.*;
@Mod(modid = ReplayModRender.MOD_ID, public class ReplayModRender implements Module {
version = "@MOD_VERSION@", { instance = this; }
acceptedMinecraftVersions = "@MC_VERSION@",
acceptableRemoteVersions = "*",
//#if MC>=10800
clientSideOnly = true,
//#endif
useMetadata = true)
public class ReplayModRender {
public static final String MOD_ID = "replaymod-render";
@Mod.Instance(MOD_ID)
public static ReplayModRender instance; public static ReplayModRender instance;
private ReplayMod core; private ReplayMod core;
public static Logger LOGGER; public static Logger LOGGER = LogManager.getLogger();
private Configuration configuration;
private final List<RenderJob> renderQueue = new ArrayList<>(); private final List<RenderJob> renderQueue = new ArrayList<>();
public ReplayModRender(ReplayMod core) {
this.core = core;
core.getSettingsRegistry().register(Setting.class);
}
public ReplayMod getCore() { public ReplayMod getCore() {
return core; return core;
} }
@Mod.EventHandler @Override
public void preInit(FMLPreInitializationEvent event) { public void initClient() {
LOGGER = event.getModLog();
core = ReplayMod.instance;
configuration = new Configuration(event.getSuggestedConfigurationFile());
FML_BUS.register(this); FML_BUS.register(this);
core.getSettingsRegistry().register(Setting.class);
} }
@SubscribeEvent @SubscribeEvent
@@ -69,17 +61,17 @@ public class ReplayModRender {
public File getVideoFolder() { public File getVideoFolder() {
String path = core.getSettingsRegistry().get(Setting.RENDER_PATH); String path = core.getSettingsRegistry().get(Setting.RENDER_PATH);
File folder = new File(path.startsWith("./") ? core.getMinecraft().mcDataDir : null, path); File folder = new File(path.startsWith("./") ? mcDataDir(core.getMinecraft()) : null, path);
try { try {
FileUtils.forceMkdir(folder); FileUtils.forceMkdir(folder);
} catch (IOException e) { } catch (IOException e) {
throw new ReportedException(CrashReport.makeCrashReport(e, "Cannot create video folder.")); throw newReportedException(CrashReport.makeCrashReport(e, "Cannot create video folder."));
} }
return folder; return folder;
} }
public Configuration getConfiguration() { public Path getRenderSettingsPath() {
return configuration; return mcDataDir(core.getMinecraft()).toPath().resolve("config/replaymod-rendersettings.json");
} }
public List<RenderJob> getRenderQueue() { public List<RenderJob> getRenderQueue() {

View File

@@ -1,19 +1,18 @@
package com.replaymod.render; package com.replaymod.render;
import com.replaymod.core.versions.MCVer;
import com.replaymod.render.frame.RGBFrame; import com.replaymod.render.frame.RGBFrame;
import com.replaymod.render.rendering.FrameConsumer; import com.replaymod.render.rendering.FrameConsumer;
import com.replaymod.render.rendering.VideoRenderer; import com.replaymod.render.rendering.VideoRenderer;
import com.replaymod.render.utils.ByteBufferPool; import com.replaymod.render.utils.ByteBufferPool;
import com.replaymod.render.utils.StreamPipe; import com.replaymod.render.utils.StreamPipe;
import net.minecraft.client.Minecraft; import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReport;
import net.minecraft.crash.CrashReportCategory; import net.minecraft.crash.CrashReportCategory;
import net.minecraft.util.Util;
import org.apache.commons.exec.CommandLine; import org.apache.commons.exec.CommandLine;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.TeeOutputStream; import org.apache.commons.io.output.TeeOutputStream;
import org.lwjgl.util.ReadableDimension;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
@@ -22,17 +21,8 @@ import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.channels.Channels; import java.nio.channels.Channels;
import java.nio.channels.WritableByteChannel; import java.nio.channels.WritableByteChannel;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Optional;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
//#if MC>=10800
import net.minecraftforge.fml.common.versioning.ComparableVersion;
//#else
//$$ import cpw.mods.fml.common.versioning.ComparableVersion;
//#endif
import static com.replaymod.render.ReplayModRender.LOGGER; import static com.replaymod.render.ReplayModRender.LOGGER;
import static org.apache.commons.lang3.Validate.isTrue; import static org.apache.commons.lang3.Validate.isTrue;
@@ -64,7 +54,7 @@ public class VideoWriter implements FrameConsumer<RGBFrame> {
.replace("%BITRATE%", String.valueOf(settings.getBitRate())) .replace("%BITRATE%", String.valueOf(settings.getBitRate()))
.replace("%FILTERS%", settings.getVideoFilters()); .replace("%FILTERS%", settings.getVideoFilters());
String executable = settings.getExportCommand().isEmpty() ? findFFmpeg() : settings.getExportCommand(); String executable = settings.getExportCommandOrDefault();
LOGGER.info("Starting {} with args: {}", executable, commandArgs); LOGGER.info("Starting {} with args: {}", executable, commandArgs);
String[] cmdline; String[] cmdline;
try { try {
@@ -78,7 +68,7 @@ public class VideoWriter implements FrameConsumer<RGBFrame> {
} catch (IOException e) { } catch (IOException e) {
throw new NoFFmpegException(e); throw new NoFFmpegException(e);
} }
File exportLogFile = new File(Minecraft.getMinecraft().mcDataDir, "export.log"); File exportLogFile = new File(MCVer.mcDataDir(MCVer.getMinecraft()), "export.log");
OutputStream exportLogOut = new TeeOutputStream(new FileOutputStream(exportLogFile), ffmpegLog); OutputStream exportLogOut = new TeeOutputStream(new FileOutputStream(exportLogFile), ffmpegLog);
new StreamPipe(process.getInputStream(), exportLogOut).start(); new StreamPipe(process.getInputStream(), exportLogOut).start();
new StreamPipe(process.getErrorStream(), exportLogOut).start(); new StreamPipe(process.getErrorStream(), exportLogOut).start();
@@ -86,54 +76,6 @@ public class VideoWriter implements FrameConsumer<RGBFrame> {
channel = Channels.newChannel(outputStream); channel = Channels.newChannel(outputStream);
} }
private String findFFmpeg() {
switch (Util.getOSType()) {
case WINDOWS:
// Allow windows users to unpack the ffmpeg archive into a sub-folder of their .minecraft folder
File inDotMinecraft = new File(Minecraft.getMinecraft().mcDataDir, "ffmpeg/bin/ffmpeg.exe");
if (inDotMinecraft.exists()) {
LOGGER.debug("FFmpeg found in .minecraft/ffmpeg");
return inDotMinecraft.getAbsolutePath();
}
break;
case OSX:
// The PATH doesn't seem to be set as expected on OSX, therefore we check some common locations ourselves
for (String path : new String[]{"/usr/local/bin/ffmpeg", "/usr/bin/ffmpeg"}) {
File file = new File(path);
if (file.exists()) {
LOGGER.debug("Found FFmpeg at {}", path);
return path;
} else {
LOGGER.debug("FFmpeg not located at {}", path);
}
}
// Homebrew doesn't seem to reliably symlink its installed binaries either
File homebrewFolder = new File("/usr/local/Cellar/ffmpeg");
String[] homebrewVersions = homebrewFolder.list();
if (homebrewVersions != null) {
Optional<File> latestOpt = Arrays.stream(homebrewVersions)
.map(ComparableVersion::new) // Convert file name to comparable version
.sorted(Comparator.reverseOrder()) // Sort for latest version
.map(ComparableVersion::toString) // Convert back to file name
.map(v -> new File(new File(homebrewFolder, v), "bin/ffmpeg")) // Convert to binary files
.filter(File::exists) // Filter invalid installations (missing executable)
.findFirst(); // Take first one
if (latestOpt.isPresent()) {
File latest = latestOpt.get();
LOGGER.debug("Found {} versions of FFmpeg installed with homebrew, chose {}",
homebrewVersions.length, latest);
return latest.getAbsolutePath();
}
}
break;
case LINUX: // Linux users are entrusted to have their PATH configured correctly (most package manager do this)
case SOLARIS: // Never heard of anyone running this mod on Solaris having any problems
case UNKNOWN: // Unknown OS, just try to use "ffmpeg"
}
LOGGER.debug("Using default FFmpeg executable");
return "ffmpeg";
}
@Override @Override
public void close() throws IOException { public void close() throws IOException {
IOUtils.closeQuietly(outputStream); IOUtils.closeQuietly(outputStream);
@@ -182,9 +124,9 @@ public class VideoWriter implements FrameConsumer<RGBFrame> {
} }
CrashReport report = CrashReport.makeCrashReport(t, "Exporting frame"); CrashReport report = CrashReport.makeCrashReport(t, "Exporting frame");
CrashReportCategory exportDetails = report.makeCategory("Export details"); CrashReportCategory exportDetails = report.makeCategory("Export details");
exportDetails.addCrashSection("Export command", settings.getExportCommand()); MCVer.addDetail(exportDetails, "Export command", settings::getExportCommand);
exportDetails.addCrashSection("Export args", commandArgs); MCVer.addDetail(exportDetails, "Export args", commandArgs::toString);
Minecraft.getMinecraft().crashed(report); MCVer.getMinecraft().crashed(report);
} }
} }

View File

@@ -5,10 +5,16 @@ import com.replaymod.render.blend.BlendMeshBuilder;
import com.replaymod.render.blend.Exporter; import com.replaymod.render.blend.Exporter;
import com.replaymod.render.blend.data.DMesh; import com.replaymod.render.blend.data.DMesh;
import com.replaymod.render.blend.data.DObject; import com.replaymod.render.blend.data.DObject;
import net.minecraft.client.model.ModelBox;
import net.minecraft.client.model.ModelRenderer;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
//#if MC>=11300
import net.minecraft.client.renderer.entity.model.ModelBox;
import net.minecraft.client.renderer.entity.model.ModelRenderer;
//#else
//$$ import net.minecraft.client.model.ModelBox;
//$$ import net.minecraft.client.model.ModelRenderer;
//#endif
import java.io.IOException; import java.io.IOException;
import static com.replaymod.core.versions.MCVer.*; import static com.replaymod.core.versions.MCVer.*;

View File

@@ -4,8 +4,8 @@ import com.replaymod.render.frame.OpenGlFrame;
import com.replaymod.render.rendering.Frame; import com.replaymod.render.rendering.Frame;
import com.replaymod.render.utils.ByteBufferPool; import com.replaymod.render.utils.ByteBufferPool;
import com.replaymod.render.utils.PixelBufferObject; import com.replaymod.render.utils.PixelBufferObject;
import net.minecraft.client.renderer.OpenGlHelper;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@@ -19,7 +19,7 @@ public abstract class MultiFramePboOpenGlFrameCapturer<F extends Frame, D extend
super(worldRenderer, renderInfo); super(worldRenderer, renderInfo);
data = type.getEnumConstants(); data = type.getEnumConstants();
int bufferSize = framePixels * 3 * data.length; int bufferSize = framePixels * 4 * data.length;
pbo = new PixelBufferObject(bufferSize, PixelBufferObject.Usage.READ); pbo = new PixelBufferObject(bufferSize, PixelBufferObject.Usage.READ);
otherPBO = new PixelBufferObject(bufferSize, PixelBufferObject.Usage.READ); otherPBO = new PixelBufferObject(bufferSize, PixelBufferObject.Usage.READ);
} }
@@ -47,7 +47,7 @@ public abstract class MultiFramePboOpenGlFrameCapturer<F extends Frame, D extend
ByteBuffer pboBuffer = pbo.mapReadOnly(); ByteBuffer pboBuffer = pbo.mapReadOnly();
OpenGlFrame[] frames = new OpenGlFrame[data.length]; OpenGlFrame[] frames = new OpenGlFrame[data.length];
int frameBufferSize = getFrameWidth() * getFrameHeight() * 3; int frameBufferSize = getFrameWidth() * getFrameHeight() * 4;
for (int i = 0; i < frames.length; i++) { for (int i = 0; i < frames.length; i++) {
ByteBuffer frameBuffer = ByteBufferPool.allocate(frameBufferSize); ByteBuffer frameBuffer = ByteBufferPool.allocate(frameBufferSize);
pboBuffer.limit(pboBuffer.position() + frameBufferSize); pboBuffer.limit(pboBuffer.position() + frameBufferSize);
@@ -79,17 +79,10 @@ public abstract class MultiFramePboOpenGlFrameCapturer<F extends Frame, D extend
protected OpenGlFrame captureFrame(int frameId, D captureData) { protected OpenGlFrame captureFrame(int frameId, D captureData) {
pbo.bind(); pbo.bind();
GL11.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 1); int offset = captureData.ordinal() * getFrameWidth() * getFrameHeight() * 4;
GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1); frameBuffer().bindFramebuffer(true);
GL11.glReadPixels(0, 0, getFrameWidth(), getFrameHeight(), GL12.GL_BGRA, GL11.GL_UNSIGNED_BYTE, offset);
int offset = captureData.ordinal() * getFrameWidth() * getFrameHeight() * 3; frameBuffer().unbindFramebuffer();
if (OpenGlHelper.isFramebufferEnabled()) {
frameBuffer().bindFramebufferTexture();
GL11.glGetTexImage(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, offset);
frameBuffer().unbindFramebufferTexture();
} else {
GL11.glReadPixels(0, 0, getFrameWidth(), getFrameHeight(), GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, offset);
}
pbo.unbind(); pbo.unbind();
return null; return null;

View File

@@ -6,14 +6,11 @@ import com.replaymod.render.frame.ODSOpenGlFrame;
import com.replaymod.render.frame.OpenGlFrame; import com.replaymod.render.frame.OpenGlFrame;
import com.replaymod.render.rendering.FrameCapturer; import com.replaymod.render.rendering.FrameCapturer;
import com.replaymod.render.shader.Program; import com.replaymod.render.shader.Program;
import net.minecraft.client.Minecraft; import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReport;
import net.minecraft.util.ReportedException;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import org.lwjgl.util.ReadableDimension;
//#if MC>=10800 //#if MC>=10800
import net.minecraft.client.renderer.GlStateManager;
import static net.minecraft.client.renderer.GlStateManager.*; import static net.minecraft.client.renderer.GlStateManager.*;
//#else //#else
//$$ import com.replaymod.render.hooks.GLStateTracker.BooleanState; //$$ import com.replaymod.render.hooks.GLStateTracker.BooleanState;
@@ -36,9 +33,7 @@ public class ODSFrameCapturer implements FrameCapturer<ODSOpenGlFrame> {
private final Program.Uniform leftEyeVariable; private final Program.Uniform leftEyeVariable;
private final BooleanState[] previousStates = new BooleanState[3]; private final BooleanState[] previousStates = new BooleanState[3];
private final BooleanState previousFogState; private BooleanState previousFogState;
private final Minecraft mc = Minecraft.getMinecraft();
public ODSFrameCapturer(WorldRenderer worldRenderer, final RenderInfo renderInfo, int frameSize) { public ODSFrameCapturer(WorldRenderer worldRenderer, final RenderInfo renderInfo, int frameSize) {
RenderInfo fakeInfo = new RenderInfo() { RenderInfo fakeInfo = new RenderInfo() {
@@ -62,9 +57,9 @@ public class ODSFrameCapturer implements FrameCapturer<ODSOpenGlFrame> {
@Override @Override
public float updateForNextFrame() { public float updateForNextFrame() {
if (call++ % 2 == 0) { if (call++ % 2 == 0) {
shaderProgram.stopUsing(); unbindProgram();
partialTicks = renderInfo.updateForNextFrame(); partialTicks = renderInfo.updateForNextFrame();
shaderProgram.use(); bindProgram();
} }
return partialTicks; return partialTicks;
} }
@@ -78,30 +73,43 @@ public class ODSFrameCapturer implements FrameCapturer<ODSOpenGlFrame> {
right = new CubicStereoFrameCapturer(worldRenderer, fakeInfo, frameSize); right = new CubicStereoFrameCapturer(worldRenderer, fakeInfo, frameSize);
try { try {
shaderProgram = new Program(vertexResource, fragmentResource); shaderProgram = new Program(vertexResource, fragmentResource);
shaderProgram.use();
leftEyeVariable = shaderProgram.getUniformVariable("leftEye"); leftEyeVariable = shaderProgram.getUniformVariable("leftEye");
directionVariable = shaderProgram.getUniformVariable("direction"); directionVariable = shaderProgram.getUniformVariable("direction");
setTexture("texture", 0);
setTexture("lightMap", 1);
linkState(0, "textureEnabled");
linkState(1, "lightMapEnabled");
linkState(2, "hurtTextureEnabled");
final Program.Uniform uniform = shaderProgram.getUniformVariable("fogEnabled");
previousFogState = fog();
uniform.set(previousFogState.currentState);
fog(new BooleanState(previousFogState.capability) {
@Override
public void setState(boolean state) {
super.setState(state);
uniform.set(state);
}
});
shaderProgram.stopUsing();
} catch (Exception e) { } catch (Exception e) {
throw new ReportedException(CrashReport.makeCrashReport(e, "Creating ODS shaders")); throw newReportedException(CrashReport.makeCrashReport(e, "Creating ODS shaders"));
} }
} }
private void bindProgram() {
shaderProgram.use();
setTexture("texture", 0);
setTexture("lightMap", 1);
linkState(0, "textureEnabled");
linkState(1, "lightMapEnabled");
linkState(2, "hurtTextureEnabled");
final Program.Uniform uniform = shaderProgram.getUniformVariable("fogEnabled");
previousFogState = fog();
uniform.set(previousFogState.currentState);
fog(new BooleanState(previousFogState.capability) {
{ currentState = previousFogState.currentState; }
@Override
public void setState(boolean state) {
super.setState(state);
uniform.set(state);
}
});
}
private void unbindProgram() {
for (int i = 0; i < previousStates.length; i++) {
previousStates[i].currentState = texture2DState(i).currentState;
texture2DState(i, previousStates[i]);
}
previousFogState.currentState = fog().currentState;
fog(previousFogState);
shaderProgram.stopUsing();
}
private void setTexture(String texture, int i) { private void setTexture(String texture, int i) {
shaderProgram.getUniformVariable(texture).set(i); shaderProgram.getUniformVariable(texture).set(i);
} }
@@ -111,6 +119,7 @@ public class ODSFrameCapturer implements FrameCapturer<ODSOpenGlFrame> {
previousStates[id] = texture2DState(id); previousStates[id] = texture2DState(id);
uniform.set(previousStates[id].currentState); uniform.set(previousStates[id].currentState);
texture2DState(id, new BooleanState(previousStates[id].capability) { texture2DState(id, new BooleanState(previousStates[id].capability) {
{ currentState = previousStates[id].currentState; }
@Override @Override
public void setState(boolean state) { public void setState(boolean state) {
super.setState(state); super.setState(state);
@@ -126,12 +135,12 @@ public class ODSFrameCapturer implements FrameCapturer<ODSOpenGlFrame> {
@Override @Override
public ODSOpenGlFrame process() { public ODSOpenGlFrame process() {
shaderProgram.use(); bindProgram();
leftEyeVariable.set(true); leftEyeVariable.set(true);
CubicOpenGlFrame leftFrame = left.process(); CubicOpenGlFrame leftFrame = left.process();
leftEyeVariable.set(false); leftEyeVariable.set(false);
CubicOpenGlFrame rightFrame = right.process(); CubicOpenGlFrame rightFrame = right.process();
shaderProgram.stopUsing(); unbindProgram();
if (leftFrame != null && rightFrame != null) { if (leftFrame != null && rightFrame != null) {
return new ODSOpenGlFrame(leftFrame, rightFrame); return new ODSOpenGlFrame(leftFrame, rightFrame);
@@ -144,10 +153,6 @@ public class ODSFrameCapturer implements FrameCapturer<ODSOpenGlFrame> {
left.close(); left.close();
right.close(); right.close();
shaderProgram.delete(); shaderProgram.delete();
for (int i = 0; i < previousStates.length; i++) {
texture2DState(i, previousStates[i]);
}
fog(previousFogState);
} }
private class CubicStereoFrameCapturer extends CubicPboOpenGlFrameCapturer { private class CubicStereoFrameCapturer extends CubicPboOpenGlFrameCapturer {

View File

@@ -1,16 +1,17 @@
package com.replaymod.render.capturer; package com.replaymod.render.capturer;
import com.replaymod.core.versions.MCVer;
import com.replaymod.render.frame.OpenGlFrame; import com.replaymod.render.frame.OpenGlFrame;
import com.replaymod.render.rendering.Frame; import com.replaymod.render.rendering.Frame;
import com.replaymod.render.rendering.FrameCapturer; import com.replaymod.render.rendering.FrameCapturer;
import com.replaymod.render.utils.ByteBufferPool; import com.replaymod.render.utils.ByteBufferPool;
import de.johni0702.minecraft.gui.utils.lwjgl.Dimension;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import de.johni0702.minecraft.gui.utils.lwjgl.WritableDimension;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.client.shader.Framebuffer; import net.minecraft.client.shader.Framebuffer;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
import org.lwjgl.util.Dimension; import org.lwjgl.opengl.GL12;
import org.lwjgl.util.ReadableDimension;
import org.lwjgl.util.WritableDimension;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@@ -30,7 +31,7 @@ public abstract class OpenGlFrameCapturer<F extends Frame, D extends CaptureData
protected int framesDone; protected int framesDone;
private Framebuffer frameBuffer; private Framebuffer frameBuffer;
private final Minecraft mc = Minecraft.getMinecraft(); private final Minecraft mc = MCVer.getMinecraft();
public OpenGlFrameCapturer(WorldRenderer worldRenderer, RenderInfo renderInfo) { public OpenGlFrameCapturer(WorldRenderer worldRenderer, RenderInfo renderInfo) {
this.worldRenderer = worldRenderer; this.worldRenderer = worldRenderer;
@@ -64,7 +65,7 @@ public abstract class OpenGlFrameCapturer<F extends Frame, D extends CaptureData
protected Framebuffer frameBuffer() { protected Framebuffer frameBuffer() {
if (frameBuffer == null) { if (frameBuffer == null) {
frameBuffer = Minecraft.getMinecraft().getFramebuffer(); frameBuffer = mc.getFramebuffer();
} }
return frameBuffer; return frameBuffer;
} }
@@ -96,30 +97,28 @@ public abstract class OpenGlFrameCapturer<F extends Frame, D extends CaptureData
} }
protected OpenGlFrame captureFrame(int frameId, D captureData) { protected OpenGlFrame captureFrame(int frameId, D captureData) {
GL11.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 1); ByteBuffer buffer = ByteBufferPool.allocate(getFrameWidth() * getFrameHeight() * 4);
GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1); frameBuffer().bindFramebuffer(true);
GL11.glReadPixels(0, 0, getFrameWidth(), getFrameHeight(), GL12.GL_BGRA, GL11.GL_UNSIGNED_BYTE, buffer);
ByteBuffer buffer = ByteBufferPool.allocate(getFrameWidth() * getFrameHeight() * 3); frameBuffer().unbindFramebuffer();
if (OpenGlHelper.isFramebufferEnabled()) {
frameBuffer().bindFramebufferTexture();
GL11.glGetTexImage(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, buffer);
frameBuffer().unbindFramebufferTexture();
} else {
GL11.glReadPixels(0, 0, getFrameWidth(), getFrameHeight(), GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, buffer);
}
buffer.rewind(); buffer.rewind();
return new OpenGlFrame(frameId, new Dimension(getFrameWidth(), getFrameHeight()), buffer); return new OpenGlFrame(frameId, new Dimension(getFrameWidth(), getFrameHeight()), buffer);
} }
protected void resize(int width, int height) { protected void resize(int width, int height) {
if (width != mc.displayWidth || height != mc.displayHeight) { //#if MC>=11300
setWindowSize(width, height); Framebuffer fb = mc.getFramebuffer();
if (fb.framebufferWidth != width || fb.framebufferHeight != height) {
fb.createFramebuffer(width, height);
} }
} mc.mainWindow.framebufferWidth = width;
mc.mainWindow.framebufferHeight = height;
private void setWindowSize(int width, int height) { //#else
mc.resize(width, height); //$$ if (width != mc.displayWidth || height != mc.displayHeight) {
//$$ mc.resize(width, height);
//$$ }
//#endif
} }
@Override @Override

Some files were not shown because too many files have changed in this diff Show More