Compare commits

..

26 Commits

Author SHA1 Message Date
Jonas Herzig
6c8c779983 Merge branch '1.8.9' into 1.9.4
ed56673 Merge branch '1.8' into 1.8.9
91573d9 Update jGui, ReplayStudio and Translations
d71358b Add confirmation dialog before overwriting path presets (fixes #65)
1a55983 Move addCallback method from integration test Utils to main Utils
18c5bcd Be more cooperative with other mods on the ingame menu (fixes #42)
c054fe8 Register keys for simplepathing only once (fixes #63)
f13297c Fix default interpolator handling in keyframe gui and after loading (fixes #64)
391f304 Show error popup if entity tracker fails to load
0e0eaaa Fix entity tracker being set even if it failed loading (fixes #50)
a34bbbc Fix NPE when spectating a player without a camera entity
60879fb Change local class to be an inner class because Srg2Source breaks with it
dfafbec Load translations from github repo, only reload language not all resource packs
8a6ec51 Add missing tooltips to player overview and various missing chat messages
5677fc5 Re-add warning to replay editor
748a91e Fix missing tooltips on fav/like/dislike buttons in replay center
ee24866 Fix upload replay button not being updated when name/tags change
dba085c Move translations into separate repo
3f0e3e7 Fix NPE when receiving Replay|Restrict messages (fixes #16 GH)
40e1d85 Fix crash when saving the last keyframe in the edit gui (fixes #61)
03aada1 Update Mixin to 0.6.8 (fixes #9 GH)
0c1dc65 Fix interpolator being lost when moving keyframe to the end (fixes #62)
fcbbbc9 Add integration test. Run with ./gradlew runIntegrationTest
fe6ded0 Fix movement of keyframe via GuiEditKeyframe not updating selected keyframe
f58fa8f Fix Login GUI not respecting other GUIs opened on startup
4fc3a31 Fix OpenEye being installed into working dir instead of mcDataDir
2017-05-31 20:49:04 +02:00
Jonas Herzig
aa16e42a4f Add workaround for setupCIWorkspace not being sufficient 2017-05-31 11:19:22 +02:00
Jonas Herzig
ed566732df Merge branch '1.8' into 1.8.9
91573d9 Update jGui, ReplayStudio and Translations
d71358b Add confirmation dialog before overwriting path presets (fixes #65)
1a55983 Move addCallback method from integration test Utils to main Utils
18c5bcd Be more cooperative with other mods on the ingame menu (fixes #42)
c054fe8 Register keys for simplepathing only once (fixes #63)
f13297c Fix default interpolator handling in keyframe gui and after loading (fixes #64)
391f304 Show error popup if entity tracker fails to load
0e0eaaa Fix entity tracker being set even if it failed loading (fixes #50)
a34bbbc Fix NPE when spectating a player without a camera entity
60879fb Change local class to be an inner class because Srg2Source breaks with it
dfafbec Load translations from github repo, only reload language not all resource packs
8a6ec51 Add missing tooltips to player overview and various missing chat messages
5677fc5 Re-add warning to replay editor
748a91e Fix missing tooltips on fav/like/dislike buttons in replay center
ee24866 Fix upload replay button not being updated when name/tags change
dba085c Move translations into separate repo
3f0e3e7 Fix NPE when receiving Replay|Restrict messages (fixes #16 GH)
40e1d85 Fix crash when saving the last keyframe in the edit gui (fixes #61)
03aada1 Update Mixin to 0.6.8 (fixes #9 GH)
0c1dc65 Fix interpolator being lost when moving keyframe to the end (fixes #62)
fcbbbc9 Add integration test. Run with ./gradlew runIntegrationTest
fe6ded0 Fix movement of keyframe via GuiEditKeyframe not updating selected keyframe
f58fa8f Fix Login GUI not respecting other GUIs opened on startup
4fc3a31 Fix OpenEye being installed into working dir instead of mcDataDir
2017-05-31 09:36:46 +02:00
johni0702
d9d57e283c Merge branch '1.8.9' into 1.9.4
24c58cb Merge branch '1.8' into 1.8.9
1ffeb01 Update jGui
c122d30 Merge branch '1.8-path-segments' into 1.8
27fd25a Fix crash when server responds with garbage to language query (fixes #51)
db6b0ff Fix / by 0 when two spectator keyframes are closer than 50ms (fixes #60)
d61d0ac Fix hand of invisible spectated player being visible (fixes #59)
f7460f9 Re-enable time path after path playback (fixes #55)
bdbffd5 Add Catmull-Rom spline interpolator to simplepathing
5cbfd28 Generate interpolator settings from InterpolatorType enum
6095aef Fix loading and storing of default interpolator with settings
3f52336 Revert "Fix AbstractMethodError caused by FG2.0 not supporting lambdas (fixes #58)"
c6293bc Update to FG2.2
c8126ed Merge '1.8' into 1.8.9
64898ce Fix NPE when saving recorded resource pack
70e3e54 Fix AbstractMethodError caused by FG2.0 not supporting lambdas (fixes #58)
3f2b3d6 Fix replacing source file in Replay Editor (fixes #57)
d2def94 Fix duration of replays corrupted because of JVM/OS crash
2c64030 Refactor simple pathing timeline logic into dedicated class with unit tests Fix logic for user-set interpolators
4deb374 Enable validation on focus change for all number input fields
5302bca Fix id and text of Replay Editor button in docs
057edcc Manually inject root cert for cross-signed LetsEncrypt cert in requests
cd16211 Register new Property to imply that the path segment following a keyframe has a user-set interpolator instead of the default Modified Keyframe GUI to allow for custom Path interpolator
2017-01-29 12:53:31 +01:00
johni0702
24c58cbbe7 Merge branch '1.8' into 1.8.9
1ffeb01 Update jGui
c122d30 Merge branch '1.8-path-segments' into 1.8
27fd25a Fix crash when server responds with garbage to language query (fixes #51)
db6b0ff Fix / by 0 when two spectator keyframes are closer than 50ms (fixes #60)
d61d0ac Fix hand of invisible spectated player being visible (fixes #59)
f7460f9 Re-enable time path after path playback (fixes #55)
bdbffd5 Add Catmull-Rom spline interpolator to simplepathing
5cbfd28 Generate interpolator settings from InterpolatorType enum
6095aef Fix loading and storing of default interpolator with settings
2c64030 Refactor simple pathing timeline logic into dedicated class with unit tests Fix logic for user-set interpolators
cd16211 Register new Property to imply that the path segment following a keyframe has a user-set interpolator instead of the default Modified Keyframe GUI to allow for custom Path interpolator
2017-01-29 11:25:30 +01:00
johni0702
3f52336852 Revert "Fix AbstractMethodError caused by FG2.0 not supporting lambdas (fixes #58)"
This reverts commit 70e3e54038.

As of c6293bc, this branch uses FG2.2 which fully supports lambdas.
2017-01-24 13:29:09 +01:00
johni0702
c6293bcbb0 Update to FG2.2 2017-01-24 13:27:57 +01:00
johni0702
c8126edbb2 Merge '1.8' into 1.8.9
64898ce Fix NPE when saving recorded resource pack
70e3e54 Fix AbstractMethodError caused by FG2.0 not supporting lambdas (fixes #58)
3f2b3d6 Fix replacing source file in Replay Editor (fixes #57)
d2def94 Fix duration of replays corrupted because of JVM/OS crash
4deb374 Enable validation on focus change for all number input fields
5302bca Fix id and text of Replay Editor button in docs
057edcc Manually inject root cert for cross-signed LetsEncrypt cert in requests
2017-01-24 13:25:53 +01:00
johni0702
4fdfa9ec87 Merge branch '1.8.9' into 1.9.4 2017-01-14 23:48:02 +01:00
johni0702
44d4a250eb Merge branch '1.8' into 1.9.4
d9b796d Mixin'd the Shader's mods ShadersRender class to only render hands if the Forge event isn't cancelled
6bbff3b Updated Mixin to 0.6.4-SNAPSHOT and set mixin target level
6f3ac71 Add CustomMainMenu compatibility info to docs
b0a1cdf Search for ffmpeg executable in common locations to simplify setup
d465891 Fixed GuiEditPositionKeyframe's width to fit on all screen sizes Centered all rows of GuiEditPositionKeyframe's VerticalLayout
1b099f9 Add render queue
03534c6 Update docs
4388d69 Only try to create the downloads folder if it does not yet exist
7ad19aa Add mod version and accepted MC version directly to the @Mod annotation
655bea0 Replace @Mod.Instance with direct static references
458dad6 Merge pull request #4 from ReplayMod/1.8-editor
eb5ccb2 Add Replay Editor (for now, trimming only)
6498d62 Fix server game data snapshot being restored in singleplayer (fixes #44)
2c898a8 Prevent rendering without sufficient keyframes (fixes #45)
5d520a5 Fix deserialization of chroma key color (fixes #43)
c681742 Use caching maven proxy for faster and more stable drone builds
fbf165e Added Anti-Aliasing to the Render Settings
2017-01-14 23:28:28 +01:00
johni0702
01acbc6f1f Update to 1.8.9 2017-01-14 22:52:53 +01:00
johni0702
72e1fe0d6c Merge branch '1.8' into 1.9.4
6163585 Update ReplayStudio to fix dependencies
2016-12-16 21:27:43 +01:00
johni0702
4a1cceaa47 Merge branch '1.8' into 1.9.4
fb39a06 Add drone ci config
030c71c Automatically disable and re-enable Optifine's Fast Render setting before rendering a video
1fef12d Include FML handshake in recording
e86c66d Move README.md, LICENSE.md and user docs from master branch to 1.8
2016-12-10 19:31:41 +01:00
johni0702
ca2bbca7a4 Merge branch '1.8' into 1.9.4
5b3284f Update jGui and ReplayStudio (fixes #33)
61d6fd4 Close replay file zip after recording (fixes #32)
00bcc9e Fix paths of files that are supposed to be in the mc data dir (fixes #40)
e6a789d Fix camera rotation when jumping in time (fixes #39)
50b53fc Add a way to register key bindings triggered every render tick (fixes #31)
a817c73 Prevent GUIs of other mods from opening during replay
d64ef8b Hide NPCs in the Player Overview GUI (fixes #29)
1f2c05e Fix spectating player entities past death and respawn (fixes #36)
ad2893b Fix vanilla bug caused by unremovable entities in the entityList of ClientWorld (fixes #29)
c934cb9 Fix spawn player packet being sent twice
6efbf91 Handle F1 properly while mouse is visible in the replay overlay (fixes #30)
9add2af Deselect keyframe when pressing "V" aka. sync timelines (fixes #27)
2016-11-29 22:32:03 +01:00
johni0702
75c0e7b77a Fix particles being visible after world change when jumping in time (fixes #35) 2016-11-13 20:03:57 +01:00
johni0702
e20dfa6d7f Merge branch '1.8' into 1.9.4
682fb4b Download OpenEye via http because of outdated java versions
0c48d6a Make ShaderReflection init fail fast, not printing out unneeded exceptions
2016-11-11 18:14:02 +01:00
johni0702
54790d33f9 Merge branch '1.8' into 1.9.4
0ba5b0a Merge branch '1.8-shaders' into 1.8
e5eb982 Add Optifine compatibility to rendering by disabling their separate chunk loading queue Add compatibility to shaders that utilize the Shadow Map by manually setting displayListEntitiesDirty to false Call Shaders.beginRender in an Event Handler to avoid calling all of EntityRenderer#renderWorld
3a28c5b Draw the GuiVideoRenderer on a separate framebuffer that has the actual size of the window instead of MC's framebuffer which may have a different size
636ce7b Add compatibility for GLSL Shaders
92eb11a Change video preview to be compatible with Optifine
2016-11-10 21:51:28 +01:00
johni0702
165861764c Merge branch '1.8' into 1.9.4
0db1b9e Fix opening a replay multiple times by quickly double clicking (fixes #25)
9ff767b Fix spectated player having the arm model of the camera player
d82cef3 Make sure video folder exists before rendering (fixes #24)
cc65b26 Replace custom update system with forge update checker
c8b71dc Add tooltips to ingame buttons
b240f8a Fix camera path always playing from the beginning regardless of ctrl key
4805e5e Fix crash when setting two keyframes to the same time via the edit keyframe gui
18f8303 Fix crash when moving the last keyframe
fcd4c07 Add error popup when trying to play path with reversed time keyframes Update jGui
9c605ba Update jGui Fix handling of ESC key in popups (fixes #17)
721fdc4 Allow moving the keyframe timeline cursor by dragging (fixes #18)
1851e97 Allow deletion of selected keyframes by pressing the DELETE key (fixes #19)
d547098 Fix markers not being removed
b2c7faa Fix marker not being deselected when clicking anywhere else
4e6b387 Update jGui Fix popups being drawn outside of the actually visible element (fixes #21)
21bbeb2 Fix bitrate field always being active regardless of selected preset (fixes #20)
b5a6d8b Fix camera player head being visible while in third person
dd0537b Center star between resolution fields in render settings gui
e35bfca Increase spacing between rows in the render settings gui
6d0cdfb Update jGui (fixes #15)
2016-11-10 21:02:55 +01:00
johni0702
5014edafe0 Remove javascript comments from mcmod.info 2016-11-10 20:32:24 +01:00
johni0702
21ac84e990 Merge branch '1.8' into 1.9.4
a804d6b Properly register replay restrictions plugin channel
45191da Add GUI that offers the user to install OpenEye
9de63ce Add gradle tasks for automating the release process
2a7542a Update jGui
2016-10-17 13:05:25 +02:00
johni0702
8e6eec879f Fix name tag hiding during redering (fixes #10) 2016-09-24 17:11:25 +02:00
johni0702
5100f36b2c Merge branch '1.8' into 1.9.4
c93a3e3 Open Replay on double click (fixes #9)
8bb5d11 Update ReplayStudio
f3142a7 Pause replay after path playback (fixes #5)
3abaa1b Cleanup deleted corrupted replays after two days (fixes #1)
4b47f2c Add compatibility checking to ReplayViewer and ReplayCenter (fixes #7)
ada460c Deselect keyframe when left clicking empty space on timeline (fixes #6)
ad62060 Apply render settings only when their checkbox is enabled (fixes #3)
82246a5 Deselect Keyframe when timeline is changed (fixes #2)
2016-09-24 11:14:30 +02:00
johni0702
7ef6a6d166 Fix several sounds and the block break effect not being recorded (fixes #8) 2016-09-24 11:04:15 +02:00
johni0702
446b9be384 Revert ReplayCenter compatibility checking from "Update to Minecraft 1.9.4"
This reverts part of commit a80a20c37a.
2016-09-19 15:29:42 +02:00
johni0702
d1ed67f1a0 One EventBus to rule them all 2016-09-17 22:01:10 +02:00
johni0702
a80a20c37a Update to Minecraft 1.9.4 2016-09-17 22:00:04 +02:00
143 changed files with 2049 additions and 2411 deletions

View File

@@ -7,7 +7,6 @@ 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
- curl --fail --create-dirs -o $HOME/.m2/settings.xml https://maven.johni0702.de/config/maven - curl --fail --create-dirs -o $HOME/.m2/settings.xml https://maven.johni0702.de/config/maven
- ./gradlew setupCIWorkspace
- ./gradlew :build - ./gradlew :build
archive: archive:
image: plugins/s3 image: plugins/s3

View File

@@ -1 +1 @@
eyJhbGciOiJIUzI1NiJ9.cGlwZWxpbmU6CiAgY2xvbmU6CiAgICBpbWFnZTogcGx1Z2lucy9naXQKICAgIHRhZ3M6IHRydWUKICBidWlsZDoKICAgIGltYWdlOiBtYXZlbjozLjMuOS1qZGstOAogICAgY29tbWFuZHM6CiAgICAgIC0gY3VybCAtLWZhaWwgLS1jcmVhdGUtZGlycyAtbyAkSE9NRS8uZ3JhZGxlL2luaXQuZC9taXJyb3IuZ3JhZGxlIGh0dHBzOi8vbWF2ZW4uam9obmkwNzAyLmRlL2NvbmZpZy9ncmFkbGUKICAgICAgLSBjdXJsIC0tZmFpbCAtLWNyZWF0ZS1kaXJzIC1vICRIT01FLy5tMi9zZXR0aW5ncy54bWwgaHR0cHM6Ly9tYXZlbi5qb2huaTA3MDIuZGUvY29uZmlnL21hdmVuCiAgICAgIC0gLi9ncmFkbGV3IHNldHVwQ0lXb3Jrc3BhY2UKICAgICAgLSAuL2dyYWRsZXcgOmJ1aWxkCiAgYXJjaGl2ZToKICAgIGltYWdlOiBwbHVnaW5zL3MzCiAgICBhY2w6IHB1YmxpYy1yZWFkCiAgICByZWdpb246ICJ1cy1lYXN0LTEiCiAgICBidWNrZXQ6ICJyZXBsYXltb2QiCiAgICBwYXRoX3N0eWxlOiB0cnVlCiAgICBzb3VyY2U6IGJ1aWxkL2xpYnMvKgogICAgc3RyaXBfcHJlZml4OiBidWlsZC9saWJzLwogICAgdGFyZ2V0OiAvCg.riE6lhwfdNUhI7LO2SxEKFOjzjaX40ne7Ae9gyP75KE eyJhbGciOiJIUzI1NiJ9.cGlwZWxpbmU6CiAgY2xvbmU6CiAgICBpbWFnZTogcGx1Z2lucy9naXQKICAgIHRhZ3M6IHRydWUKICBidWlsZDoKICAgIGltYWdlOiBtYXZlbjozLjMuOS1qZGstOAogICAgY29tbWFuZHM6CiAgICAgIC0gY3VybCAtLWZhaWwgLS1jcmVhdGUtZGlycyAtbyAkSE9NRS8uZ3JhZGxlL2luaXQuZC9taXJyb3IuZ3JhZGxlIGh0dHBzOi8vbWF2ZW4uam9obmkwNzAyLmRlL2NvbmZpZy9ncmFkbGUKICAgICAgLSBjdXJsIC0tZmFpbCAtLWNyZWF0ZS1kaXJzIC1vICRIT01FLy5tMi9zZXR0aW5ncy54bWwgaHR0cHM6Ly9tYXZlbi5qb2huaTA3MDIuZGUvY29uZmlnL21hdmVuCiAgICAgIC0gLi9ncmFkbGV3IDpidWlsZAogIGFyY2hpdmU6CiAgICBpbWFnZTogcGx1Z2lucy9zMwogICAgYWNsOiBwdWJsaWMtcmVhZAogICAgcmVnaW9uOiAidXMtZWFzdC0xIgogICAgYnVja2V0OiAicmVwbGF5bW9kIgogICAgcGF0aF9zdHlsZTogdHJ1ZQogICAgc291cmNlOiBidWlsZC9saWJzLyoKICAgIHN0cmlwX3ByZWZpeDogYnVpbGQvbGlicy8KICAgIHRhcmdldDogLwo.Z6rpaLplJKQ4pzB8AjCnplgci42qEu7L4LeS0fGm4SY

4
.gitmodules vendored
View File

@@ -2,6 +2,10 @@
path = jGui path = jGui
url = https://github.com/ReplayMod/jGui url = https://github.com/ReplayMod/jGui
[submodule "ReplayStudio"]
path = ReplayStudio
url = https://github.com/ReplayMod/ReplayStudio
[submodule "src/main/resources/assets/replaymod/lang"] [submodule "src/main/resources/assets/replaymod/lang"]
path = src/main/resources/assets/replaymod/lang path = src/main/resources/assets/replaymod/lang
url = https://github.com/ReplayMod/Translations url = https://github.com/ReplayMod/Translations

1
ReplayStudio Submodule

Submodule ReplayStudio added at c283a44257

View File

@@ -7,17 +7,23 @@ buildscript {
name = "forge" name = "forge"
url = "http://files.minecraftforge.net/maven" url = "http://files.minecraftforge.net/maven"
} }
maven {
name = "sponge"
url = "https://repo.spongepowered.org/maven"
}
maven { maven {
name = "sonatype" name = "sonatype"
url = "https://oss.sonatype.org/content/repositories/snapshots/" url = "https://oss.sonatype.org/content/repositories/snapshots/"
} }
} }
dependencies { dependencies {
classpath 'net.minecraftforge.gradle:ForgeGradle:1.2-SNAPSHOT' classpath 'net.minecraftforge.gradle:ForgeGradle:2.2-SNAPSHOT'
classpath 'org.spongepowered:mixingradle:0.4-SNAPSHOT'
} }
} }
apply plugin: 'forge' apply plugin: 'net.minecraftforge.gradle.forge'
apply plugin: 'org.spongepowered.mixin'
sourceCompatibility = 1.8 sourceCompatibility = 1.8
targetCompatibility = 1.8 targetCompatibility = 1.8
@@ -26,22 +32,13 @@ version = gitDescribe()
group= "com.replaymod" group= "com.replaymod"
archivesBaseName = "replaymod" archivesBaseName = "replaymod"
ext {
mixinConfigs = [
'compat.shaders', 'extras.playeroverview', 'recording', 'render', 'replay'
].collect {"mixins.${it}.replaymod.json"}
mixinSrg = new File(project.buildDir, 'tmp/mixins/mixins.srg')
mixinRefMap = new File(project.buildDir, 'tmp/mixins/mixins.replaymod.refmap.json')
}
minecraft { minecraft {
version = '1.7.10-10.13.4.1558-1.7.10' coreMod = 'com.replaymod.core.LoadingPlugin'
version = '1.9.4-12.17.0.1976'
runDir = "eclipse" runDir = "eclipse"
mappings = "stable_12" mappings = "snapshot_20160518"
replace '@MOD_VERSION@', project.version replace '@MOD_VERSION@', project.version
// Includes intentional whitespace to stop Forge from declaring the mod to be compatible with replace '@MC_VERSION@', project.minecraft.version
// a newer srg-compatible MC version (that may be using a different protocol version)
replace '@MC_VERSION@', "[ ${project.minecraft.version} ]"
} }
repositories { repositories {
@@ -50,7 +47,7 @@ repositories {
url = "http://repo.spongepowered.org/maven/" url = "http://repo.spongepowered.org/maven/"
} }
maven { maven {
url 'https://jitpack.io' url 'https://repo.spacehq.org/content/repositories/snapshots/'
} }
} }
@@ -72,22 +69,24 @@ dependencies {
shade 'org.aspectj:aspectjrt:1.8.2' shade 'org.aspectj:aspectjrt:1.8.2'
shade 'com.github.replaymod:ReplayStudio:181140c' compile project(':ReplayStudio')
testCompile 'junit:junit:4.11' testCompile 'junit:junit:4.11'
} }
tasks['idea'].dependsOn ':ReplayStudio:preshadowJar'
jar { jar {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE duplicatesStrategy = DuplicatesStrategy.EXCLUDE
dependsOn configurations.compile dependsOn configurations.compile
dependsOn configurations.shade dependsOn configurations.shade
dependsOn ':ReplayStudio:shadowJar'
from project.mixinRefMap
def shade = {files( def shade = {files(
configurations.compile.findAll {it.name.startsWith 'mixin-'} configurations.compile.findAll {it.name.startsWith 'mixin-'}
+ configurations.shade + configurations.shade
+ getTasks().getByPath(':ReplayStudio:shadowJar').outputs.files
)} )}
def noticeDir = file("$buildDir/NOTICE") def noticeDir = file("$buildDir/NOTICE")
@@ -116,8 +115,7 @@ jar {
manifest { manifest {
attributes 'TweakClass': 'org.spongepowered.asm.launch.MixinTweaker', attributes 'TweakClass': 'org.spongepowered.asm.launch.MixinTweaker',
'TweakOrder': '0', 'TweakOrder': '0',
'MixinConfigs': project.mixinConfigs.join(','), 'FMLCorePluginContainsFMLMod': 'true',
'FMLCorePlugin': 'com.replaymod.core.LoadingPlugin',
'FMLAT': 'replaymod_at.cfg' 'FMLAT': 'replaymod_at.cfg'
} }
} }
@@ -157,8 +155,6 @@ processResources
sourceSets { sourceSets {
main { main {
// Required so resources are loaded correctly in dev env
output.resourcesDir = output.classesDir
// Directly include jGui into main build // Directly include jGui into main build
// This could also be done by including it as a gradle subproject, however // This could also be done by including it as a gradle subproject, however
// doing so caused classpath issues on some IntelliJ versions. // doing so caused classpath issues on some IntelliJ versions.
@@ -168,6 +164,7 @@ sourceSets {
resources { resources {
srcDir 'jGui/src/main/resources' srcDir 'jGui/src/main/resources'
} }
refMap = "mixins.replaymod.refmap.json"
} }
integrationTest { integrationTest {
compileClasspath += main.runtimeClasspath + main.output compileClasspath += main.runtimeClasspath + main.output
@@ -183,25 +180,61 @@ task copySrg(type: Copy, dependsOn: 'genSrgs') {
into 'build' into 'build'
} }
reobf.doFirst {
if (project.mixinSrg.exists()) {
addExtraSrgFile project.mixinSrg
}
}
compileJava.dependsOn copySrg
compileJava {
options.compilerArgs += [
"-AoutSrgFile=${project.mixinSrg.canonicalPath}",
"-AoutRefMapFile=${project.mixinRefMap.canonicalPath}",
"-AreobfSrgFile=${project.file('build/mcp-srg.srg').canonicalPath}"
]
}
setupDecompWorkspace.dependsOn copySrg setupDecompWorkspace.dependsOn copySrg
setupDevWorkspace.dependsOn copySrg setupDevWorkspace.dependsOn copySrg
project.tasks.idea.dependsOn copySrg project.tasks.idea.dependsOn copySrg
import org.objectweb.asm.*
import java.util.zip.ZipEntry
import java.util.zip.ZipFile
import java.util.zip.ZipOutputStream
import static org.objectweb.asm.Opcodes.ASM5
// MC binaries were complied with a java version that produces invalid class files under certain circumstances
// This causes setupCIWorkspace to be insufficient for compiling.
// Related JDK bug: https://bugs.openjdk.java.net/browse/JDK-8066725
// As a workaround, to use setupCIWorkspace on Drone, we modify the bin jar in-place and remove all parameter annotations.
// WARNING: This piece of code ignores any and all gradle conventions and will probably fail horribly when run outside
// of a single-use environment (e.g. Drone). Use setupDecompWorkspace for normal use.
tasks.deobfMcMCP.doLast {
println "Applying RuntimeInvisibleParameterAnnotations workaround..."
File jar = getOutJar()
File tmp = new File((File) getTemporaryDir(), "workaround.jar")
tmp.withOutputStream {
new ZipOutputStream(it).withStream { dst ->
new ZipFile(jar).withCloseable { src ->
src.entries().each {
if (it.name.startsWith("net/minecraft/") && it.name.endsWith(".class")) {
def cw = new ClassWriter(0)
def cv = new ClassVisitor(ASM5, cw) {
@Override
MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
return new MethodVisitor(ASM5, cv.visitMethod(access, name, desc, signature, exceptions)) {
@Override
AnnotationVisitor visitParameterAnnotation(int parameter, String pdesc, boolean visible) {
return null // Strip all parameter annotations
}
}
}
}
new ClassReader(src.getInputStream(it)).accept(cv, 0)
dst.putNextEntry(new ZipEntry(it.name))
dst.write(cw.toByteArray())
} else {
dst.putNextEntry(it)
dst.write(src.getInputStream(it).bytes)
}
}
}
}
}
jar.delete()
tmp.renameTo(jar)
}
task runIntegrationTest(type: JavaExec, dependsOn: ["makeStart", "jar"]) { task runIntegrationTest(type: JavaExec, dependsOn: ["makeStart", "jar"]) {
main = 'GradleStart' main = 'GradleStart'
standardOutput = System.out standardOutput = System.out

2
jGui

Submodule jGui updated: 042e1c6e50...e2bd90628e

View File

@@ -0,0 +1 @@
include 'ReplayStudio'

View File

@@ -3,9 +3,6 @@ package com.replaymod.core;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
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 cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.TickEvent;
import de.johni0702.minecraft.gui.container.AbstractGuiOverlay; import de.johni0702.minecraft.gui.container.AbstractGuiOverlay;
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;
@@ -19,6 +16,9 @@ import de.johni0702.minecraft.gui.utils.Consumer;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.settings.KeyBinding; import net.minecraft.client.settings.KeyBinding;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
import org.lwjgl.input.Keyboard; import org.lwjgl.input.Keyboard;
import java.lang.reflect.Field; import java.lang.reflect.Field;
@@ -50,10 +50,8 @@ public abstract class AbstractTask implements Task {
public ListenableFuture<Void> execute() { public ListenableFuture<Void> execute() {
future = SettableFuture.create(); future = SettableFuture.create();
FMLCommonHandler.instance().bus().register(this);
MinecraftForge.EVENT_BUS.register(this); MinecraftForge.EVENT_BUS.register(this);
addCallback(future, success -> { addCallback(future, success -> {
FMLCommonHandler.instance().bus().unregister(this);
MinecraftForge.EVENT_BUS.unregister(this); MinecraftForge.EVENT_BUS.unregister(this);
}, error -> {}); }, error -> {});
@@ -84,26 +82,15 @@ public abstract class AbstractTask implements Task {
private void expectGuiClosed0(int timeout, Runnable onClosed) { private void expectGuiClosed0(int timeout, Runnable onClosed) {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
FMLCommonHandler.instance().bus().register(new GuiEventHandler(timeout, stackTrace, onClosed)); class EventHandler {
}
public class GuiEventHandler {
final net.minecraft.client.gui.GuiScreen currentScreen = mc.currentScreen; final net.minecraft.client.gui.GuiScreen currentScreen = mc.currentScreen;
private final int timeout;
private final StackTraceElement[] stackTrace;
private final Runnable onClosed;
int framesPassed; int framesPassed;
public GuiEventHandler(int timeout, StackTraceElement[] stackTrace, Runnable onClosed) {
this.timeout = timeout;
this.stackTrace = stackTrace;
this.onClosed = onClosed;
}
@SubscribeEvent @SubscribeEvent
public void onGuiOpen(TickEvent.RenderTickEvent event) { public void onGuiOpen(TickEvent.RenderTickEvent event) {
if (event.phase != TickEvent.Phase.START) return; if (event.phase != TickEvent.Phase.START) return;
if (currentScreen != mc.currentScreen) { if (currentScreen != mc.currentScreen) {
FMLCommonHandler.instance().bus().unregister(this); MinecraftForge.EVENT_BUS.unregister(this);
onClosed.run(); onClosed.run();
} else { } else {
if (framesPassed < timeout) { if (framesPassed < timeout) {
@@ -117,6 +104,8 @@ public abstract class AbstractTask implements Task {
} }
} }
} }
MinecraftForge.EVENT_BUS.register(new EventHandler());
}
public void expectPopupClosed(Runnable onClosed) { public void expectPopupClosed(Runnable onClosed) {
expectPopupClosed0(10, onClosed); expectPopupClosed0(10, onClosed);
@@ -128,30 +117,18 @@ public abstract class AbstractTask implements Task {
private void expectPopupClosed0(int timeout, Runnable onClosed) { private void expectPopupClosed0(int timeout, Runnable onClosed) {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
FMLCommonHandler.instance().bus().register(new PopupEventHandler(timeout, stackTrace, onClosed)); AbstractGuiPopup popup = getPopup(mc.currentScreen);
} if (popup == null) {
public class PopupEventHandler { throw new IllegalStateException("No popup found.");
private final int timeout; }
private final StackTraceElement[] stackTrace; class EventHandler {
private final Runnable onClosed;
private final AbstractGuiPopup popup;
int framesPassed; int framesPassed;
public PopupEventHandler(int timeout, StackTraceElement[] stackTrace, Runnable onClosed) {
this.timeout = timeout;
this.stackTrace = stackTrace;
this.onClosed = onClosed;
this.popup = getPopup(mc.currentScreen);
if (this.popup == null) {
throw new IllegalStateException("No popup found.");
}
}
@SubscribeEvent @SubscribeEvent
public void onGuiOpen(TickEvent.RenderTickEvent event) { public void onGuiOpen(TickEvent.RenderTickEvent event) {
if (event.phase != TickEvent.Phase.START) return; if (event.phase != TickEvent.Phase.START) return;
if (getPopup(mc.currentScreen) != popup) { if (getPopup(mc.currentScreen) != popup) {
FMLCommonHandler.instance().bus().unregister(this); MinecraftForge.EVENT_BUS.unregister(this);
onClosed.run(); onClosed.run();
} else { } else {
if (framesPassed < timeout) { if (framesPassed < timeout) {
@@ -164,6 +141,8 @@ public abstract class AbstractTask implements Task {
} }
} }
} }
MinecraftForge.EVENT_BUS.register(new EventHandler());
}
private AbstractGuiPopup getPopup(net.minecraft.client.gui.GuiScreen minecraft) { private AbstractGuiPopup getPopup(net.minecraft.client.gui.GuiScreen minecraft) {
GuiContainer<?> container = GuiOverlay.from(minecraft); GuiContainer<?> container = GuiOverlay.from(minecraft);
@@ -184,21 +163,10 @@ public abstract class AbstractTask implements Task {
public <T> void expectGui(Class<T> guiClass, Consumer<T> onOpen) { public <T> void expectGui(Class<T> guiClass, Consumer<T> onOpen) {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
FMLCommonHandler.instance().bus().register(new ExpectGuiEventHandler<T>(guiClass, stackTrace, onOpen)); class EventHandler {
}
public class ExpectGuiEventHandler<T> {
private final Class<T> guiClass;
private final StackTraceElement[] stackTrace;
private final Consumer<T> onOpen;
net.minecraft.client.gui.GuiScreen currentScreen; net.minecraft.client.gui.GuiScreen currentScreen;
int framesPassed; int framesPassed;
public ExpectGuiEventHandler(Class<T> guiClass, StackTraceElement[] stackTrace, Consumer<T> onOpen) {
this.guiClass = guiClass;
this.stackTrace = stackTrace;
this.onOpen = onOpen;
}
@SubscribeEvent @SubscribeEvent
public void onGuiOpen(TickEvent.RenderTickEvent event) { public void onGuiOpen(TickEvent.RenderTickEvent event) {
if (event.phase != TickEvent.Phase.START) return; if (event.phase != TickEvent.Phase.START) return;
@@ -211,7 +179,7 @@ public abstract class AbstractTask implements Task {
return; return;
} }
FMLCommonHandler.instance().bus().unregister(this); MinecraftForge.EVENT_BUS.unregister(this);
Object foundGui = null; Object foundGui = null;
if (AbstractGuiScreen.class.isAssignableFrom(guiClass)) { if (AbstractGuiScreen.class.isAssignableFrom(guiClass)) {
@@ -240,15 +208,16 @@ public abstract class AbstractTask implements Task {
return; return;
} }
} }
future.setException(new UnexpectedGuiException(guiClass, stackTrace, class UnexpectedGuiException extends Exception {
foundGui == null ? currentScreen : foundGui)); UnexpectedGuiException(Object foundGui) {
super("Expected instance of " + guiClass + " but found " + foundGui);
setStackTrace(Arrays.copyOfRange(stackTrace, 2, stackTrace.length));
}
}
future.setException(new UnexpectedGuiException(foundGui == null ? currentScreen : foundGui));
} }
} }
class UnexpectedGuiException extends Exception { MinecraftForge.EVENT_BUS.register(new EventHandler());
UnexpectedGuiException(Class<?> guiClass, StackTraceElement[] stackTrace, Object foundGui) {
super("Expected instance of " + guiClass + " but found " + foundGui);
setStackTrace(Arrays.copyOfRange(stackTrace, 2, stackTrace.length));
}
} }
private void clickNow(int x, int y) { private void clickNow(int x, int y) {

View File

@@ -10,10 +10,10 @@ import com.replaymod.recording.ExitSPWorld;
import com.replaymod.replay.ExitReplay; import com.replaymod.replay.ExitReplay;
import com.replaymod.replay.LoadReplay; import com.replaymod.replay.LoadReplay;
import com.replaymod.replay.OpenReplayViewer; import com.replaymod.replay.OpenReplayViewer;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import static com.replaymod.core.AbstractTask.mc; import static com.replaymod.core.AbstractTask.mc;

View File

@@ -2,7 +2,7 @@ package com.replaymod.extra;
import com.replaymod.core.AbstractTask; import com.replaymod.core.AbstractTask;
import com.replaymod.extras.OpenEyeExtra; import com.replaymod.extras.OpenEyeExtra;
import cpw.mods.fml.common.Loader; import net.minecraftforge.fml.common.Loader;
import java.io.File; import java.io.File;
import java.nio.file.NoSuchFileException; import java.nio.file.NoSuchFileException;

View File

@@ -1,33 +1,32 @@
package com.replaymod.recording; package com.replaymod.recording;
import com.replaymod.core.AbstractTask; import com.replaymod.core.AbstractTask;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import net.minecraft.client.gui.GuiCreateWorld; import net.minecraft.client.gui.GuiCreateWorld;
import net.minecraft.client.gui.GuiMainMenu; import net.minecraft.client.gui.GuiMainMenu;
import net.minecraft.client.gui.GuiSelectWorld; import net.minecraft.client.gui.GuiWorldSelection;
import net.minecraftforge.client.event.RenderGameOverlayEvent; import net.minecraftforge.client.event.RenderGameOverlayEvent;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
public class CreateSPWorld extends AbstractTask { public class CreateSPWorld extends AbstractTask {
@Override @Override
protected void init() { protected void init() {
expectGui(GuiMainMenu.class, mainMenu -> { expectGui(GuiMainMenu.class, mainMenu -> {
click("Singleplayer"); click("Singleplayer");
expectGui(GuiSelectWorld.class, selectWorld -> { expectGui(GuiWorldSelection.class, selectWorld -> {
click("Create New World"); click("Create New World");
expectGui(GuiCreateWorld.class, createWorld -> { expectGui(GuiCreateWorld.class, createWorld -> {
click("Create New World"); click("Create New World");
class EventHandler {
@SubscribeEvent
public void onRenderIngame(RenderGameOverlayEvent.Pre event) {
MinecraftForge.EVENT_BUS.unregister(this);
runLater(() -> future.set(null));
}
}
MinecraftForge.EVENT_BUS.register(new EventHandler()); MinecraftForge.EVENT_BUS.register(new EventHandler());
}); });
}); });
}); });
} }
public class EventHandler {
@SubscribeEvent
public void onRenderIngame(RenderGameOverlayEvent.Pre event) {
MinecraftForge.EVENT_BUS.unregister(this);
runLater(() -> future.set(null));
}
}
} }

View File

@@ -2,10 +2,10 @@ package com.replaymod.replay;
import com.replaymod.core.AbstractTask; import com.replaymod.core.AbstractTask;
import com.replaymod.replay.gui.screen.GuiReplayViewer; import com.replaymod.replay.gui.screen.GuiReplayViewer;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import de.johni0702.minecraft.gui.function.Clickable; import de.johni0702.minecraft.gui.function.Clickable;
import net.minecraftforge.client.event.RenderGameOverlayEvent; import net.minecraftforge.client.event.RenderGameOverlayEvent;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import org.lwjgl.util.Point; import org.lwjgl.util.Point;
import org.lwjgl.util.ReadableDimension; import org.lwjgl.util.ReadableDimension;
@@ -19,15 +19,14 @@ public class LoadReplay extends AbstractTask {
// Load first replay // Load first replay
click(replayViewer.loadButton); click(replayViewer.loadButton);
class EventHandler {
@SubscribeEvent
public void onRenderIngame(RenderGameOverlayEvent.Pre event) {
MinecraftForge.EVENT_BUS.unregister(this);
runLater(() -> future.set(null));
}
}
MinecraftForge.EVENT_BUS.register(new EventHandler()); MinecraftForge.EVENT_BUS.register(new EventHandler());
})); }));
} }
public class EventHandler {
@SubscribeEvent
public void onRenderIngame(RenderGameOverlayEvent.Pre event) {
MinecraftForge.EVENT_BUS.unregister(this);
runLater(() -> future.set(null));
}
}
} }

View File

@@ -2,8 +2,8 @@ package com.replaymod.replay;
import com.replaymod.core.AbstractTask; import com.replaymod.core.AbstractTask;
import com.replaymod.extras.playeroverview.PlayerOverviewGui; import com.replaymod.extras.playeroverview.PlayerOverviewGui;
import cpw.mods.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.TickEvent; import net.minecraftforge.fml.common.gameevent.TickEvent;
import org.lwjgl.input.Keyboard; import org.lwjgl.input.Keyboard;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
@@ -34,7 +34,7 @@ public class SpectatePlayer extends AbstractTask {
future.setException(new TimeoutException("Camera hasn't stopped spectating.")); future.setException(new TimeoutException("Camera hasn't stopped spectating."));
return; return;
} }
if (mc.renderViewEntity == mc.thePlayer) { if (mc.getRenderViewEntity() == mc.thePlayer) {
future.set(null); future.set(null);
} }
} }

View File

@@ -1,27 +1,24 @@
package com.replaymod.compat; package com.replaymod.compat;
import com.replaymod.compat.bettersprinting.DisableBetterSprinting;
import com.replaymod.compat.optifine.DisableFastRender; import com.replaymod.compat.optifine.DisableFastRender;
import com.replaymod.compat.oranges17animations.HideInvisibleEntities; import com.replaymod.compat.shaders.ShaderBeginRender;
import cpw.mods.fml.common.FMLCommonHandler; import net.minecraftforge.common.MinecraftForge;
import cpw.mods.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import cpw.mods.fml.common.event.FMLInitializationEvent; import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.eventhandler.EventBus; import net.minecraftforge.fml.common.eventhandler.EventBus;
@Mod(modid = ReplayModCompat.MOD_ID, @Mod(modid = ReplayModCompat.MOD_ID,
version = "@MOD_VERSION@", version = "@MOD_VERSION@",
acceptedMinecraftVersions = "@MC_VERSION@", acceptedMinecraftVersions = "@MC_VERSION@",
acceptableRemoteVersions = "*",
useMetadata = true) useMetadata = true)
public class ReplayModCompat { public class ReplayModCompat {
public static final String MOD_ID = "replaymod-compat"; public static final String MOD_ID = "replaymod-compat";
@Mod.EventHandler @Mod.EventHandler
public void init(FMLInitializationEvent event) { public void init(FMLInitializationEvent event) {
EventBus bus = FMLCommonHandler.instance().bus(); EventBus bus = MinecraftForge.EVENT_BUS;
bus.register(new ShaderBeginRender());
bus.register(new DisableFastRender()); bus.register(new DisableFastRender());
bus.register(new HideInvisibleEntities());
DisableBetterSprinting.register();
} }
} }

View File

@@ -1,97 +0,0 @@
package com.replaymod.compat.bettersprinting;
import com.replaymod.replay.ReplayModReplay;
import com.replaymod.replay.events.ReplayChatMessageEvent;
import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.ModContainer;
import cpw.mods.fml.common.eventhandler.EventPriority;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.versioning.DefaultArtifactVersion;
import cpw.mods.fml.common.versioning.Restriction;
import cpw.mods.fml.common.versioning.VersionRange;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.PlayerControllerMP;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.world.IWorldAccess;
import net.minecraftforge.client.event.GuiOpenEvent;
import net.minecraftforge.common.MinecraftForge;
import java.util.Collections;
/**
* Old Better Sprinting versions replace the vanilla player with their own, overridden instance (replacing the camera entity).
*
* See: https://github.com/chylex/Better-Sprinting/blob/1.8/src/main/java/chylex/bettersprinting/client/player/impl/LogicImplOverride.java
*/
public class DisableBetterSprinting {
private static final VersionRange OLD_VERSION = VersionRange.newRange(null,
Collections.singletonList(new Restriction(null, false, new DefaultArtifactVersion("2.0.0"), false)));
private static final String LOGIC_CLASS_NAME = "chylex.bettersprinting.client.player.impl.LogicImplOverride";
private static final String CONTROLLER_OVERRIDE_CLASS_NAME = LOGIC_CLASS_NAME + ".PlayerControllerMPOverride";
public static void register() {
Loader.instance().getModList().stream()
.filter(mod -> mod.getModId().equalsIgnoreCase("bettersprinting"))
.findFirst()
.map(ModContainer::getProcessedVersion).filter(OLD_VERSION::containsVersion)
.ifPresent($_ -> MinecraftForge.EVENT_BUS.register(new DisableBetterSprinting()));
}
private DisableBetterSprinting() {}
private final Minecraft mc = Minecraft.getMinecraft();
private PlayerControllerMP originalController;
private BetterSprintingWorldAccess worldAccessHook = new BetterSprintingWorldAccess();
@SubscribeEvent(priority = EventPriority.HIGH)
public void beforeGuiOpenEvent(GuiOpenEvent event) {
if (ReplayModReplay.instance.getReplayHandler() != null && mc.theWorld != null) {
// During replay, get ready to revert BetterSprinting's overwritten playerController
originalController = mc.playerController;
mc.theWorld.addWorldAccess(worldAccessHook);
}
}
@SubscribeEvent(priority = EventPriority.LOW)
public void afterGuiOpenEvent(GuiOpenEvent event) {
if (ReplayModReplay.instance.getReplayHandler() != null && mc.theWorld != null) {
mc.theWorld.removeWorldAccess(worldAccessHook);
}
}
@SubscribeEvent
public void onReplayChatMessage(ReplayChatMessageEvent event) {
// Suppress this message if it's the Better Sprinting warning message
for (StackTraceElement elem : Thread.currentThread().getStackTrace()) {
if (LOGIC_CLASS_NAME.equals(elem.getClassName())) {
event.setCanceled(true);
return;
}
}
}
private class BetterSprintingWorldAccess implements IWorldAccess {
@Override
public void onEntityDestroy(Entity entityIn) {
if (mc.playerController != null && mc.playerController.getClass().getName().equals(CONTROLLER_OVERRIDE_CLASS_NAME)) {
// Someone has secretly swapped out the player controller and is about to substitute their own player entity.
// This is the right time to destroy their plan.
mc.playerController = originalController;
}
}
@Override public void markBlockForUpdate(int x, int y, int z) {}
@Override public void markBlockForRenderUpdate(int p_147588_1_, int p_147588_2_, int p_147588_3_) {}
@Override public void markBlockRangeForRenderUpdate(int x1, int y1, int z1, int x2, int y2, int z2) {}
@Override public void playSound(String soundName, double x, double y, double z, float volume, float pitch) {}
@Override public void playSoundToNearExcept(EntityPlayer except, String soundName, double x, double y, double z, float volume, float pitch) {}
@Override public void spawnParticle(String p_180442_1_, double p_180442_2_, double p_180442_3_, double p_180442_5_, double p_180442_7_, double p_180442_9_, double p_180442_11_) {}
@Override public void onEntityCreate(Entity p_72703_1_) {}
@Override public void playRecord(String recordName, int x, int y, int z) {}
@Override public void broadcastSound(int p_180440_1_, int x, int y, int z, int p_180440_3_) {}
@Override public void playAuxSFX(EntityPlayer p_72706_1_, int p_72706_2_, int p_72706_3_, int p_72706_4_, int p_72706_5_, int p_72706_6_) {}
@Override public void destroyBlockPartially(int p_147587_1_, int p_147587_2_, int p_147587_3_, int p_147587_4_, int p_147587_5_) {}
@Override public void onStaticEntitiesChanged() {}
}
}

View File

@@ -1,11 +1,10 @@
package com.replaymod.compat.optifine; package com.replaymod.compat.optifine;
import com.replaymod.render.events.ReplayRenderEvent; import com.replaymod.render.events.ReplayRenderEvent;
import cpw.mods.fml.client.FMLClientHandler;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraftforge.fml.client.FMLClientHandler;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
// TODO 1.7.10: Is this still necessary (probably, but needs checking)?
public class DisableFastRender { public class DisableFastRender {
private final Minecraft mc = Minecraft.getMinecraft(); private final Minecraft mc = Minecraft.getMinecraft();

View File

@@ -1,28 +0,0 @@
package com.replaymod.compat.oranges17animations;
import com.replaymod.replay.camera.CameraEntity;
import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.eventhandler.EventPriority;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import net.minecraft.client.Minecraft;
import net.minecraftforge.client.event.RenderLivingEvent;
/**
* Orange seems to have copied vast parts of the RendererLivingEntity into their ArmorAnimation class which cancels the RenderLivingEvent.Pre and calls its own code instead.
* This breaks our mixin which assures that, even though the camera is in spectator mode, it cannot see invisible entities.
*
* To fix this issue, we simply cancel the RenderLivingEvent.Pre before it gets to ArmorAnimation if the entity is invisible.
*/
public class HideInvisibleEntities {
private final Minecraft mc = Minecraft.getMinecraft();
private final boolean modLoaded = Loader.isModLoaded("animations");
@SubscribeEvent(priority = EventPriority.HIGH)
public void preRenderLiving(RenderLivingEvent.Pre event) {
if (modLoaded) {
if (mc.thePlayer instanceof CameraEntity && event.entity.isInvisible()) {
event.setCanceled(true);
}
}
}
}

View File

@@ -0,0 +1,39 @@
package com.replaymod.compat.shaders;
import com.replaymod.render.hooks.EntityRendererHandler;
import net.minecraft.client.Minecraft;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
import java.lang.reflect.InvocationTargetException;
public class ShaderBeginRender {
private final Minecraft mc = Minecraft.getMinecraft();
/**
* Invokes Shaders#beginRender when rendering a video,
* as this would usually get called by EntityRenderer#renderWorld,
* which we're not calling during rendering.
*/
@SubscribeEvent
public void onRenderTickStart(TickEvent.RenderTickEvent event) {
if (event.phase != TickEvent.Phase.START) return;
if (ShaderReflection.shaders_beginRender == null) return;
if (ShaderReflection.config_isShaders == null) return;
try {
// check if video is being rendered
if (((EntityRendererHandler.IEntityRenderer) mc.entityRenderer).replayModRender_getHandler() == null)
return;
// check if Shaders are enabled
if (!(boolean) (ShaderReflection.config_isShaders.invoke(null))) return;
ShaderReflection.shaders_beginRender.invoke(null, mc, mc.timer.elapsedPartialTicks, 0);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,53 @@
package com.replaymod.compat.shaders;
import net.minecraft.client.Minecraft;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ShaderReflection {
// Shaders.frameTimeCounter
public static Field shaders_frameTimeCounter;
// Shaders.isShadowPass
public static Field shaders_isShadowPass;
// Shaders.beginRender()
public static Method shaders_beginRender;
// RenderGlobal.chunksToUpdateForced (Optifine only)
public static Field renderGlobal_chunksToUpdateForced;
// Config.isShaders() (Optifine only)
public static Method config_isShaders;
static {
try {
shaders_frameTimeCounter = Class.forName("shadersmod.client.Shaders")
.getDeclaredField("frameTimeCounter");
shaders_frameTimeCounter.setAccessible(true);
shaders_isShadowPass = Class.forName("shadersmod.client.Shaders")
.getDeclaredField("isShadowPass");
shaders_isShadowPass.setAccessible(true);
shaders_beginRender = Class.forName("shadersmod.client.Shaders")
.getDeclaredMethod("beginRender", Minecraft.class, float.class, long.class);
shaders_beginRender.setAccessible(true);
renderGlobal_chunksToUpdateForced = Class.forName("net.minecraft.client.renderer.RenderGlobal")
.getDeclaredField("chunksToUpdateForced");
renderGlobal_chunksToUpdateForced.setAccessible(true);
config_isShaders = Class.forName("Config")
.getDeclaredMethod("isShaders");
config_isShaders.setAccessible(true);
} catch (ClassNotFoundException ignore) {
// no shaders mod installed
} catch (NoSuchMethodException | NoSuchFieldException e) {
// the method wasn't found. Has it been renamed?
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,28 @@
package com.replaymod.compat.shaders.mixin;
import com.replaymod.compat.shaders.ShaderReflection;
import com.replaymod.replay.ReplayHandler;
import com.replaymod.replay.ReplayModReplay;
import net.minecraft.client.renderer.EntityRenderer;
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.callback.CallbackInfo;
@Mixin(EntityRenderer.class)
public abstract class MixinShaderEntityRenderer {
@Inject(method = "renderWorldPass", at = @At("HEAD"))
private void replayModCompat_updateShaderFrameTimeCounter(CallbackInfo ignore) {
if (ReplayModReplay.instance.getReplayHandler() == null) return;
if (ShaderReflection.shaders_frameTimeCounter == null) return;
ReplayHandler replayHandler = ReplayModReplay.instance.getReplayHandler();
float timestamp = replayHandler.getReplaySender().currentTimeStamp() / 1000f % 3600f;
try {
ShaderReflection.shaders_frameTimeCounter.set(null, timestamp);
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,28 @@
package com.replaymod.compat.shaders.mixin;
import com.replaymod.render.hooks.EntityRendererHandler;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.chunk.RenderChunk;
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.callback.CallbackInfoReturnable;
@Mixin(RenderChunk.class)
public abstract class MixinShaderRenderChunk {
private final Minecraft mc = Minecraft.getMinecraft();
/**
* Changes the RenderChunk#isPlayerUpdate method that Optifine adds
* to always return true while rendering so no chunks are being added
* to a separate rendering queue
*/
@Inject(method = "isPlayerUpdate", at = @At("HEAD"), cancellable = true)
private void replayModCompat_disableIsPlayerUpdate(CallbackInfoReturnable<Boolean> ci) {
if (((EntityRendererHandler.IEntityRenderer) mc.entityRenderer).replayModRender_getHandler() == null) return;
ci.setReturnValue(true);
}
}

View File

@@ -0,0 +1,40 @@
package com.replaymod.compat.shaders.mixin;
import com.replaymod.compat.shaders.ShaderReflection;
import com.replaymod.render.hooks.EntityRendererHandler;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.RenderGlobal;
import net.minecraft.client.renderer.culling.ICamera;
import net.minecraft.entity.Entity;
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;
@Mixin(RenderGlobal.class)
public abstract class MixinShaderRenderGlobal {
private final Minecraft mc = Minecraft.getMinecraft();
@Shadow
public boolean displayListEntitiesDirty;
@Inject(method = "setupTerrain", at = @At("HEAD"), cancellable = true)
public void replayModCompat_setupTerrain(Entity viewEntity, double partialTicks, ICamera camera,
int frameCount, boolean playerSpectator, CallbackInfo ci) {
if (((EntityRendererHandler.IEntityRenderer) mc.entityRenderer).replayModRender_getHandler() == null) return;
if (ShaderReflection.shaders_isShadowPass == null) return;
// when called by the shadow pass, displayListEntitiesDirty can't be set to false, as no chunk updates
// are being processed. As it's being set to true by ChunkLoadingRenderGlobal#updateChunks, we have to
// set it to false manually to exit the loop imposed by MixinRenderGlobal#replayModRender_setupTerrain.
try {
if ((boolean) ShaderReflection.shaders_isShadowPass.get(null) == true) {
displayListEntitiesDirty = false;
}
} catch (IllegalAccessException ignore) {}
}
}

View File

@@ -1,50 +0,0 @@
package com.replaymod.compat.shaders.mixin;
import com.replaymod.render.hooks.EntityRendererHandler;
import com.replaymod.replay.ReplayHandler;
import com.replaymod.replay.ReplayModReplay;
import net.minecraft.client.Minecraft;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Pseudo;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Pseudo
@Mixin(targets = "shadersmod/client/Shaders", remap = false)
public abstract class MixinShaders {
@Shadow static Minecraft mc;
@Shadow static long systemTime;
@Shadow static long lastSystemTime;
@Shadow static long diffSystemTime;
@Shadow static int frameCounter;
@Shadow static float frameTime;
@Shadow static float frameTimeCounter;
@Redirect(method = "beginRender", at = @At(value = "INVOKE", target = "Ljava/lang/System;currentTimeMillis()J"))
private static long replayModCompat_currentTimeMillis() {
ReplayHandler replayHandler = ReplayModReplay.instance.getReplayHandler();
if (replayHandler != null) {
systemTime = replayHandler.getReplaySender().currentTimeStamp();
// We need to manipulate all the other previous-frame-time-based variables too
lastSystemTime = 0; // Draw all frames as if they were the first ones
// diffSystemTime will be set to 0 by Shaders
// frameTime will be set to 0 by Shaders
frameTimeCounter = systemTime / 1000f; // will be %= 3600f by Shaders
// Set frameCounter only if rendering is in progress
EntityRendererHandler entityRendererHandler =
((EntityRendererHandler.IEntityRenderer) mc.entityRenderer).replayModRender_getHandler();
if (entityRendererHandler != null) {
frameCounter = entityRendererHandler.getRenderInfo().getFramesDone();
}
return systemTime;
}
return System.currentTimeMillis();
}
}

View File

@@ -1,94 +0,0 @@
package com.replaymod.core;
import net.minecraft.launchwrapper.IClassTransformer;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.glu.GLU;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
/**
* Insert glGetError checks after all calls to any method in any GL*, ARB* and EXT* classes
*/
public class GLErrorTransformer implements IClassTransformer {
private static final String GL_CLASS = "org.lwjgl.opengl.GL";
private static final String GL = GL_CLASS.replace('.', '/');
private static final String ARB_CLASS = "org.lwjgl.opengl.ARB";
private static final String ARB = ARB_CLASS.replace('.', '/');
private static final String EXT_CLASS = "org.lwjgl.opengl.EXT";
private static final String EXT = EXT_CLASS.replace('.', '/');
private static final String GLErrorTransformer_CLASS = GLErrorTransformer.class.getName();
private static final String GLErrorTransformer = GLErrorTransformer_CLASS.replace('.', '/');
@Override
public byte[] transform(String name, String transformedName, byte[] basicClass) {
// Ignore (anonymous) inner classes of this transformer
if (name.startsWith(GLErrorTransformer_CLASS)) return basicClass;
ClassReader reader = new ClassReader(basicClass);
ClassWriter writer = new ClassWriter(reader, 0);
reader.accept(new ClassVisitor(Opcodes.ASM5, writer) {
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
return new MethodVisitor(Opcodes.ASM5, mv) {
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
super.visitMethodInsn(opcode, owner, name, desc, itf);
if (owner.startsWith(GL) || owner.startsWith(ARB) || owner.startsWith(EXT)) {
visitLdcInsn(owner.replace('/', '.'));
visitLdcInsn(name);
super.visitMethodInsn(Opcodes.INVOKESTATIC, GLErrorTransformer, "glErrorCheck", "(Ljava/lang/String;Ljava/lang/String;)V", false);
}
}
@Override
public void visitMaxs(int maxStack, int maxLocals) {
super.visitMaxs(maxStack + 2, maxLocals);
}
};
}
}, 0);
return writer.toByteArray();
}
private static boolean inGlBegin;
@SuppressWarnings("unused") // Called via ASM
public static void glErrorCheck(String cls, String method) {
if (method.equals("glBegin") && cls.equals(GL_CLASS + "11")) {
if (inGlBegin) {
// glBegin within glBegin
throw new GLError(GL11.GL_INVALID_OPERATION, cls, method);
}
inGlBegin = true;
return;
}
if (inGlBegin && method.equals("glEnd") && cls.equals(GL_CLASS + "11")) {
inGlBegin = false;
}
if (inGlBegin) {
return; // Cannot call glGetError in between of glBegin and glEnd
}
int err = GL11.glGetError();
if (err != 0) {
GLError e = new GLError(err, cls, method);
if ("true".equals(System.getProperty("replaymod.glerrors.throw", "true"))) {
throw e;
} else {
e.printStackTrace();
}
}
}
public static class GLError extends RuntimeException {
private GLError(int err, String cls, String method) {
super(GLU.gluErrorString(err) + " (" + err + ")");
StackTraceElement[] stack = getStackTrace();
stack[0] = new StackTraceElement(cls, method, stack[0].getFileName(), stack[0].getLineNumber());
setStackTrace(stack);
}
}
}

View File

@@ -1,105 +0,0 @@
package com.replaymod.core;
import net.minecraft.launchwrapper.IClassTransformer;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import java.util.HashSet;
import java.util.Set;
/**
* Redirect all calls to GL11.glEnable and GL11.glDisable to the GLStateTracker (excluding that class itself).
*/
public class GLStateTrackerTransformer implements IClassTransformer {
private static final String GL11_CLASS = "org.lwjgl.opengl.GL11";
private static final String GL11 = GL11_CLASS.replace('.', '/');
private static final String glEnable = "glEnable";
private static final String glDisable = "glDisable";
private static final String GLStateTracker_CLASS = "com.replaymod.render.hooks.GLStateTracker";
private static final String GLStateTracker = GLStateTracker_CLASS.replace('.', '/');
private static final String hook_glEnable = "hook_glEnable";
private static final String hook_glDisable = "hook_glDisable";
@Override
public byte[] transform(String name, String transformedName, byte[] basicClass) {
// Ignore (anonymous) inner classes of this transformer
if (name.startsWith(GLStateTrackerTransformer.class.getName())) return basicClass;
// Ignore the state tracker itself
if (name.equals(GLStateTracker_CLASS)) return basicClass;
ClassReader reader = new ClassReader(basicClass);
Set<Method> eligibleMethods = findEligibleMethods(reader);
if (eligibleMethods.isEmpty()) {
return basicClass;
}
ClassWriter writer = new ClassWriter(reader, 0);
reader.accept(new ClassVisitor(Opcodes.ASM5, writer) {
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
if (!eligibleMethods.contains(new Method(name, desc))) {
return mv;
}
return new MethodVisitor(Opcodes.ASM5, mv) {
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
if (owner.equals(GL11)) {
if (name.equals(glEnable)) {
owner = GLStateTracker;
name = hook_glEnable;
} else if (name.equals(glDisable)) {
owner = GLStateTracker;
name = hook_glDisable;
}
}
super.visitMethodInsn(opcode, owner, name, desc, itf);
}
};
}
}, 0);
return writer.toByteArray();
}
private Set<Method> findEligibleMethods(ClassReader reader) {
Set<Method> eligibleMethods = new HashSet<>();
reader.accept(new ClassVisitor(Opcodes.ASM5) {
@Override
public MethodVisitor visitMethod(int access, String methodName, String methodDesc, String signature, String[] exceptions) {
return new MethodVisitor(Opcodes.ASM5) {
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
if (owner.equals(GL11) && (name.equals(glEnable) || name.equals(glDisable))) {
eligibleMethods.add(new Method(methodName, methodDesc));
}
}
};
}
}, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
return eligibleMethods;
}
private static class Method {
private final String name, desc;
private Method(String name, String desc) {
this.name = name;
this.desc = desc;
}
@Override
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) return false;
Method method = (Method) o;
return name.equals(method.name) && desc.equals(method.desc);
}
@Override
public int hashCode() {
return name.hashCode() ^ desc.hashCode();
}
}
}

View File

@@ -2,21 +2,20 @@ package com.replaymod.core;
import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import cpw.mods.fml.client.registry.ClientRegistry;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.InputEvent;
import cpw.mods.fml.common.gameevent.TickEvent;
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 net.minecraft.util.ReportedException;
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.TickEvent;
import org.lwjgl.input.Keyboard; import org.lwjgl.input.Keyboard;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.Callable;
public class KeyBindingRegistry { public class KeyBindingRegistry {
private Map<String, KeyBinding> keyBindings = new HashMap<String, KeyBinding>(); private Map<String, KeyBinding> keyBindings = new HashMap<String, KeyBinding>();
@@ -64,7 +63,7 @@ public class KeyBindingRegistry {
public void handleRepeatedKeyBindings() { public void handleRepeatedKeyBindings() {
for (Map.Entry<KeyBinding, Collection<Runnable>> entry : repeatedKeyBindingHandlers.asMap().entrySet()) { for (Map.Entry<KeyBinding, Collection<Runnable>> entry : repeatedKeyBindingHandlers.asMap().entrySet()) {
if (entry.getKey().getIsKeyPressed()) { if (entry.getKey().isKeyDown()) {
invokeKeyBindingHandlers(entry.getKey(), entry.getValue()); invokeKeyBindingHandlers(entry.getKey(), entry.getValue());
} }
} }
@@ -86,7 +85,7 @@ public class KeyBindingRegistry {
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); category.addCrashSection("Key Binding", keyBinding);
category.addCrashSectionCallable("Handler", runnable::toString); category.setDetail("Handler", runnable::toString);
throw new ReportedException(crashReport); throw new ReportedException(crashReport);
} }
} }
@@ -101,12 +100,7 @@ public class KeyBindingRegistry {
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); category.addCrashSection("Key Code", keyCode);
category.addCrashSectionCallable("Handler", new Callable() { category.setDetail("Handler", runnable::toString);
@Override
public Object call() throws Exception {
return runnable;
}
});
throw new ReportedException(crashReport); throw new ReportedException(crashReport);
} }
} }

View File

@@ -1,7 +1,7 @@
package com.replaymod.core; package com.replaymod.core;
import cpw.mods.fml.relauncher.CoreModManager; import net.minecraftforge.fml.relauncher.CoreModManager;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin; import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.spongepowered.asm.launch.MixinBootstrap; import org.spongepowered.asm.launch.MixinBootstrap;
import org.spongepowered.asm.mixin.Mixins; import org.spongepowered.asm.mixin.Mixins;
@@ -10,8 +10,6 @@ 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.ArrayList;
import java.util.List;
import java.util.Map; import java.util.Map;
public class LoadingPlugin implements IFMLLoadingPlugin { public class LoadingPlugin implements IFMLLoadingPlugin {
@@ -22,7 +20,6 @@ public class LoadingPlugin implements IFMLLoadingPlugin {
Mixins.addConfiguration("mixins.render.replaymod.json"); Mixins.addConfiguration("mixins.render.replaymod.json");
Mixins.addConfiguration("mixins.replay.replaymod.json"); Mixins.addConfiguration("mixins.replay.replaymod.json");
Mixins.addConfiguration("mixins.compat.shaders.replaymod.json"); Mixins.addConfiguration("mixins.compat.shaders.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) {
@@ -32,8 +29,7 @@ public class LoadingPlugin implements IFMLLoadingPlugin {
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?
CoreModManager.getLoadedCoremods().remove(file.getName()); CoreModManager.getIgnoredMods().remove(file.getName());
CoreModManager.getReparseableCoremods().add(file.getName());
} }
} catch (URISyntaxException e) { } catch (URISyntaxException e) {
e.printStackTrace(); e.printStackTrace();
@@ -46,12 +42,8 @@ public class LoadingPlugin implements IFMLLoadingPlugin {
@Override @Override
public String[] getASMTransformerClass() { public String[] getASMTransformerClass() {
List<String> transformers = new ArrayList<>(); return new String[]{
if ("true".equals(System.getProperty("replaymod.glerrors", "false"))) { };
transformers.add(GLErrorTransformer.class.getName());
}
transformers.add(GLStateTrackerTransformer.class.getName());
return transformers.stream().toArray(String[]::new);
} }
@Override @Override

View File

@@ -6,47 +6,37 @@ 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.utils.OpenGLUtils;
import com.replaymod.render.utils.SoundHandler;
import com.replaymod.replay.InputReplayTimer;
import com.replaymod.replaystudio.util.I18n; import com.replaymod.replaystudio.util.I18n;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.EventHandler;
import cpw.mods.fml.common.Mod.Instance;
import cpw.mods.fml.common.ModContainer;
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.EventBus;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.TickEvent;
import de.johni0702.minecraft.gui.container.GuiScreen; import de.johni0702.minecraft.gui.container.GuiScreen;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.settings.GameSettings;
import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReport;
import net.minecraft.crash.CrashReportCategory; import net.minecraft.crash.CrashReportCategory;
import net.minecraft.util.ChatComponentText;
import net.minecraft.util.ChatComponentTranslation;
import net.minecraft.util.ChatStyle;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.IChatComponent;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraft.util.text.*;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.config.Configuration; import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.fml.client.FMLClientHandler;
import net.minecraftforge.fml.common.Loader;
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 org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayDeque;
import java.util.List; import java.util.List;
import java.util.Queue;
@Mod(modid = ReplayMod.MOD_ID, @Mod(modid = ReplayMod.MOD_ID,
useMetadata = true, useMetadata = true,
version = "@MOD_VERSION@", version = "@MOD_VERSION@",
acceptedMinecraftVersions = "@MC_VERSION@", acceptedMinecraftVersions = "@MC_VERSION@",
acceptableRemoteVersions = "*", updateJSON = "https://raw.githubusercontent.com/ReplayMod/ReplayMod/master/versions.json",
guiFactory = "com.replaymod.core.gui.GuiFactory") guiFactory = "com.replaymod.core.gui.GuiFactory")
public class ReplayMod { public class ReplayMod {
@@ -59,10 +49,10 @@ public class ReplayMod {
private static String parseMinecraftVersion() { private static String parseMinecraftVersion() {
CrashReport crashReport = new CrashReport("", new Throwable()); CrashReport crashReport = new CrashReport("", new Throwable());
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
List<CrashReportCategory.Entry> list = crashReport.getCategory().field_85077_c; List<CrashReportCategory.Entry> list = crashReport.getCategory().children;
for (CrashReportCategory.Entry entry : list) { for (CrashReportCategory.Entry entry : list) {
if ("Minecraft Version".equals(entry.func_85089_a())) { if ("Minecraft Version".equals(entry.getKey())) {
return entry.func_85090_b(); return entry.getValue();
} }
} }
return "Unknown"; return "Unknown";
@@ -77,8 +67,6 @@ public class ReplayMod {
@Deprecated @Deprecated
public static Configuration config; public static Configuration config;
@Deprecated
public static SoundHandler soundHandler = new SoundHandler();
private final KeyBindingRegistry keyBindingRegistry = new KeyBindingRegistry(); private final KeyBindingRegistry keyBindingRegistry = new KeyBindingRegistry();
private final SettingsRegistry settingsRegistry = new SettingsRegistry(); private final SettingsRegistry settingsRegistry = new SettingsRegistry();
@@ -121,8 +109,7 @@ public class ReplayMod {
new MainMenuHandler().register(); new MainMenuHandler().register();
FMLCommonHandler.instance().bus().register(this); MinecraftForge.EVENT_BUS.register(keyBindingRegistry);
FMLCommonHandler.instance().bus().register(keyBindingRegistry);
getKeyBindingRegistry().registerKeyBinding("replaymod.input.settings", 0, () -> { getKeyBindingRegistry().registerKeyBinding("replaymod.input.settings", 0, () -> {
new GuiReplaySettings(null, settingsRegistry).display(); new GuiReplaySettings(null, settingsRegistry).display();
@@ -133,155 +120,10 @@ public class ReplayMod {
public void postInit(FMLPostInitializationEvent event) throws IOException { public void postInit(FMLPostInitializationEvent event) throws IOException {
settingsRegistry.save(); // Save default values to disk settingsRegistry.save(); // Save default values to disk
/* TODO 1.7.10: MC crashes when setting render distance to > 16
if(!FMLClientHandler.instance().hasOptifine()) if(!FMLClientHandler.instance().hasOptifine())
GameSettings.Options.RENDER_DISTANCE.setValueMax(64f); GameSettings.Options.RENDER_DISTANCE.setValueMax(64f);
*/
if (System.getProperty("replaymod.render.file") != null) { testIfMoeshAndExitMinecraft();
final File file = new File(System.getProperty("replaymod.render.file"));
if (!file.exists()) {
throw new IOException("File \"" + file.getPath() + "\" not found.");
}
final String path = System.getProperty("replaymod.render.path");
String type = System.getProperty("replaymod.render.type");
String bitrate = System.getProperty("replaymod.render.bitrate");
String fps = System.getProperty("replaymod.render.fps");
String waitForChunks = System.getProperty("replaymod.render.waitforchunks");
String linearMovement = System.getProperty("replaymod.render.linearmovement");
String skyColor = System.getProperty("replaymod.render.skycolor");
String width = System.getProperty("replaymod.render.width");
String height = System.getProperty("replaymod.render.height");
String exportCommand = System.getProperty("replaymod.render.exportcommand");
String exportCommandArgs = System.getProperty("replaymod.render.exportcommandargs");
/*
final RenderOptions options = new RenderOptions();
if (bitrate != null) {
options.setBitrate(bitrate);
}
if (fps != null) {
options.setFps(Integer.parseInt(fps));
}
if (waitForChunks != null) {
options.setWaitForChunks(Boolean.parseBoolean(waitForChunks));
}
if (linearMovement != null) {
options.setLinearMovement(Boolean.parseBoolean(linearMovement));
}
if (skyColor != null) {
if (skyColor.startsWith("0x")) {
options.setSkyColor(Integer.parseInt(skyColor.substring(2), 16));
} else {
options.setSkyColor(Integer.parseInt(skyColor));
}
}
if (width != null) {
options.setWidth(Integer.parseInt(width));
}
if (height != null) {
options.setHeight(Integer.parseInt(height));
}
if (exportCommand != null) {
options.setExportCommand(exportCommand);
}
if (exportCommandArgs != null) {
options.setExportCommandArgs(exportCommandArgs);
} else {
options.setExportCommandArgs(EncodingPreset.MP4DEFAULT.getCommandLineArgs());
}
options.setOutputFile(new File(String.valueOf(System.currentTimeMillis())));
Pipelines.Preset pipelinePreset = Pipelines.Preset.DEFAULT;
if (type != null) {
String[] parts = type.split(":");
type = parts[0].toUpperCase();
if ("NORMAL".equals(type) || "DEFAULT".equals(type)) {
pipelinePreset = Pipelines.Preset.DEFAULT;
} else if ("STEREO".equals(type) || "STEREOSCOPIC".equals(type)) {
pipelinePreset = Pipelines.Preset.STEREOSCOPIC;
} else if ("CUBIC".equals(type)) {
if (parts.length < 2) {
throw new IllegalArgumentException("Cubic renderer requires boolean for whether it's stable.");
}
pipelinePreset = Pipelines.Preset.CUBIC;
} else if ("EQUIRECTANGULAR".equals(type)) {
if (parts.length < 2) {
throw new IllegalArgumentException("Equirectangular renderer requires boolean for whether it's stable.");
}
pipelinePreset = Pipelines.Preset.EQUIRECTANGULAR;
} else if ("ODS".equals(type)) {
if (parts.length < 2) {
throw new IllegalArgumentException("ODS renderer requires boolean for whether it's stable.");
}
pipelinePreset = Pipelines.Preset.ODS;
} else {
throw new IllegalArgumentException("Unknown type: " + parts[0]);
}
}
options.setMode(pipelinePreset);
*/
@SuppressWarnings("unchecked")
Queue<ListenableFutureTask> tasks = mc.scheduledTasks;
synchronized (mc.scheduledTasks) {
tasks.add(ListenableFutureTask.create(new Runnable() {
@Override
public void run() {
String renderDistance = System.getProperty("replaymod.render.mc.renderdistance");
String clouds = System.getProperty("replaymod.render.mc.clouds");
if (renderDistance != null) {
mc.gameSettings.renderDistanceChunks = Integer.parseInt(renderDistance);
}
if (clouds != null) {
mc.gameSettings.clouds = Boolean.parseBoolean(clouds);
}
System.out.println("Loading replay...");
// TODO
// try {
// ReplayHandler.startReplay(file, false);
// } catch (Throwable t) {
// t.printStackTrace();
// FMLCommonHandler.instance().exitJava(1, false);
// }
//
// int index = 0;
// if (path != null) {
// for (KeyframeSet set : ReplayHandler.getKeyframeRepository()) {
// if (set.getName().equals(path)) {
// break;
// }
// index++;
// }
// if (index >= ReplayHandler.getKeyframeRepository().length) {
// throw new IllegalArgumentException("No path named \"" + path + "\".");
// }
// } else if (ReplayHandler.getKeyframeRepository().length == 0) {
// throw new IllegalArgumentException("Replay file has no paths defined.");
// }
// ReplayHandler.useKeyframePresetFromRepository(index);
//
// System.out.println("Rendering started...");
// try {
// ReplayProcess.startReplayProcess(options, true);
// } catch (Throwable t) {
// t.printStackTrace();
// FMLCommonHandler.instance().exitJava(1, false);
// }
// if (mc.hasCrashed) {
// System.out.println(mc.crashReporter.getCompleteReport());
// }
// System.out.println("Rendering done. Shutting down...");
// mc.shutdown();
}
}, null));
}
testIfMoeshAndExitMinecraft();
}
runLater(() -> { runLater(() -> {
// Cleanup deleted corrupted replays // Cleanup deleted corrupted replays
@@ -317,53 +159,9 @@ public class ReplayMod {
}); });
} }
/**
* Set when the currently running code has been scheduled by runLater.
* If this is the case, subsequent calls to runLater have to be delayed until all scheduled tasks have been
* processed, otherwise a livelock may occur.
*/
private boolean inRunLater = false;
// 1.7.10: Cannot use MC's because it is processed only during ticks (so not at all when replay is paused)
private final Queue<ListenableFutureTask<?>> scheduledTasks = new ArrayDeque<>();
public void runLater(Runnable runnable) { public void runLater(Runnable runnable) {
if (mc.isCallingFromMinecraftThread() && inRunLater) { synchronized (mc.scheduledTasks) {
EventBus bus = FMLCommonHandler.instance().bus(); mc.scheduledTasks.add(ListenableFutureTask.create(runnable, null));
bus.register(new RunLaterHelper(runnable));
return;
}
synchronized (scheduledTasks) {
scheduledTasks.add(ListenableFutureTask.create(() -> {
inRunLater = true;
try {
runnable.run();
} finally {
inRunLater = false;
}
}, null));
}
}
// in 1.7.10 apparently events can't be delivered to anonymous classes
@RequiredArgsConstructor
public class RunLaterHelper {
private final Runnable runnable;
@SubscribeEvent
public void onRenderTick(TickEvent.RenderTickEvent event) {
if (event.phase == TickEvent.Phase.START) {
runLater(runnable);
FMLCommonHandler.instance().bus().unregister(this);
}
}
}
@SubscribeEvent
public void runScheduledTasks(InputReplayTimer.RunScheduledTasks event) {
synchronized (scheduledTasks) {
while (!scheduledTasks.isEmpty()) {
scheduledTasks.poll().run();
}
} }
} }
@@ -392,13 +190,13 @@ public class ReplayMod {
private void printToChat(boolean warning, String message, Object... args) { private void printToChat(boolean warning, String message, Object... args) {
if (getSettingsRegistry().get(Setting.NOTIFICATIONS)) { if (getSettingsRegistry().get(Setting.NOTIFICATIONS)) {
// Some nostalgia: "§8[§6Replay Mod§8]§r Your message goes here" // Some nostalgia: "§8[§6Replay Mod§8]§r Your message goes here"
ChatStyle coloredDarkGray = new ChatStyle().setColor(EnumChatFormatting.DARK_GRAY); Style coloredDarkGray = new Style().setColor(TextFormatting.DARK_GRAY);
ChatStyle coloredGold = new ChatStyle().setColor(EnumChatFormatting.GOLD); Style coloredGold = new Style().setColor(TextFormatting.GOLD);
IChatComponent text = new ChatComponentText("[").setChatStyle(coloredDarkGray) ITextComponent text = new TextComponentString("[").setStyle(coloredDarkGray)
.appendSibling(new ChatComponentTranslation("replaymod.title").setChatStyle(coloredGold)) .appendSibling(new TextComponentTranslation("replaymod.title").setStyle(coloredGold))
.appendSibling(new ChatComponentText("] ")) .appendSibling(new TextComponentString("] "))
.appendSibling(new ChatComponentTranslation(message, args).setChatStyle(new ChatStyle() .appendSibling(new TextComponentTranslation(message, args).setStyle(new Style()
.setColor(warning ? EnumChatFormatting.RED : EnumChatFormatting.DARK_GREEN))); .setColor(warning ? TextFormatting.RED : TextFormatting.DARK_GREEN)));
// Send message to chat GUI // Send message to chat GUI
// The ingame GUI is initialized at startup, therefore this is possible before the client is connected // The ingame GUI is initialized at startup, therefore this is possible before the client is connected
mc.ingameGUI.getChatGUI().printChatMessage(text); mc.ingameGUI.getChatGUI().printChatMessage(text);

View File

@@ -1,8 +1,8 @@
package com.replaymod.core; package com.replaymod.core;
import com.replaymod.core.events.SettingsChangedEvent; import com.replaymod.core.events.SettingsChangedEvent;
import cpw.mods.fml.common.FMLCommonHandler;
import net.minecraft.client.resources.I18n; import net.minecraft.client.resources.I18n;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.config.Configuration; import net.minecraftforge.common.config.Configuration;
import java.lang.reflect.Field; import java.lang.reflect.Field;
@@ -83,7 +83,7 @@ public class SettingsRegistry {
throw new IllegalArgumentException("Default type " + key.getDefault().getClass() + " not supported."); throw new IllegalArgumentException("Default type " + key.getDefault().getClass() + " not supported.");
} }
settings.put(key, value); settings.put(key, value);
FMLCommonHandler.instance().bus().post(new SettingsChangedEvent(this, key)); MinecraftForge.EVENT_BUS.post(new SettingsChangedEvent(this, key));
} }
public void save() { public void save() {

View File

@@ -1,9 +1,9 @@
package com.replaymod.core.events; package com.replaymod.core.events;
import com.replaymod.core.SettingsRegistry; import com.replaymod.core.SettingsRegistry;
import cpw.mods.fml.common.eventhandler.Event;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import net.minecraftforge.fml.common.eventhandler.Event;
@RequiredArgsConstructor @RequiredArgsConstructor
public class SettingsChangedEvent extends Event { public class SettingsChangedEvent extends Event {

View File

@@ -1,9 +1,9 @@
package com.replaymod.core.gui; package com.replaymod.core.gui;
import com.replaymod.core.ReplayMod; import com.replaymod.core.ReplayMod;
import cpw.mods.fml.client.IModGuiFactory;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.gui.GuiScreen;
import net.minecraftforge.fml.client.IModGuiFactory;
import java.util.Set; import java.util.Set;

View File

@@ -1,12 +1,10 @@
package com.replaymod.core.handler; package com.replaymod.core.handler;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
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.minecraftforge.client.event.GuiScreenEvent; import net.minecraftforge.client.event.GuiScreenEvent;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import java.util.List;
/** /**
* Moves certain buttons on the main menu upwards so we can inject our own. * Moves certain buttons on the main menu upwards so we can inject our own.
@@ -18,14 +16,12 @@ public class MainMenuHandler {
@SubscribeEvent @SubscribeEvent
public void onInit(GuiScreenEvent.InitGuiEvent.Post event) { public void onInit(GuiScreenEvent.InitGuiEvent.Post event) {
if (event.gui instanceof GuiMainMenu) { if (event.getGui() instanceof GuiMainMenu) {
@SuppressWarnings("unchecked") for (GuiButton button : event.getButtonList()) {
List<GuiButton> buttonList = event.buttonList;
for (GuiButton button : buttonList) {
// Buttons that aren't in a rectangle directly above our space don't need moving // Buttons that aren't in a rectangle directly above our space don't need moving
if (button.xPosition + button.width < event.gui.width / 2 - 100 if (button.xPosition + button.width < event.getGui().width / 2 - 100
|| button.xPosition > event.gui.width / 2 + 100 || button.xPosition > event.getGui().width / 2 + 100
|| button.yPosition > event.gui.height / 4 + 10 + 4 * 24) continue; || button.yPosition > event.getGui().height / 4 + 10 + 4 * 24) continue;
// Move button up to make space for two rows of buttons // Move button up to make space for two 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
button.yPosition -= 2 * 24 - 10; button.yPosition -= 2 * 24 - 10;

View File

@@ -1,9 +1,10 @@
package com.replaymod.core.utils; package com.replaymod.core.utils;
import com.replaymod.replaystudio.data.ModInfo; import com.replaymod.replaystudio.data.ModInfo;
import cpw.mods.fml.common.Loader; import net.minecraft.util.ResourceLocation;
import cpw.mods.fml.common.ModContainer; import net.minecraftforge.fml.common.Loader;
import cpw.mods.fml.common.registry.GameData; import net.minecraftforge.fml.common.ModContainer;
import net.minecraftforge.fml.common.registry.GameData;
import java.util.*; import java.util.*;
import java.util.function.Function; import java.util.function.Function;
@@ -16,19 +17,14 @@ public class ModCompat {
Map<String, ModContainer> ignoreCaseMap = Loader.instance().getModList().stream() Map<String, ModContainer> ignoreCaseMap = Loader.instance().getModList().stream()
.collect(Collectors.toMap(m -> m.getModId().toLowerCase(), Function.identity())); .collect(Collectors.toMap(m -> m.getModId().toLowerCase(), Function.identity()));
return Stream.concat( return Stream.concat(
((Set<String>) GameData.getBlockRegistry().getKeys()).stream(), ((Set<ResourceLocation>) GameData.getBlockRegistry().getKeys()).stream(),
((Set<String>) GameData.getItemRegistry().getKeys()).stream() ((Set<ResourceLocation>) GameData.getItemRegistry().getKeys()).stream()
).map(ModCompat::getResourceDomain).filter(s -> !s.equals("minecraft")).distinct() ).map(ResourceLocation::getResourceDomain).filter(s -> !s.equals("minecraft")).distinct()
.map(String::toLowerCase).map(ignoreCaseMap::get).filter(mod -> mod != null) .map(String::toLowerCase).map(ignoreCaseMap::get).filter(mod -> mod != null)
.map(mod -> new ModInfo(mod.getModId(), mod.getName(), mod.getVersion())) .map(mod -> new ModInfo(mod.getModId(), mod.getName(), mod.getVersion()))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
private static String getResourceDomain(String name) {
if (!name.contains(":")) return null; // Still using old names without namespace, can't do anything, ignore
return name.split(":", 2)[0];
}
public static final class ModInfoDifference { public static final class ModInfoDifference {
private final Set<ModInfo> missing = new HashSet<>(); private final Set<ModInfo> missing = new HashSet<>();
private final Map<ModInfo, String> differing = new HashMap<>(); private final Map<ModInfo, String> differing = new HashMap<>();

View File

@@ -1,6 +1,9 @@
package com.replaymod.core.utils; package com.replaymod.core.utils;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.VertexBuffer;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import org.lwjgl.BufferUtils; import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
@@ -26,21 +29,22 @@ public class OpenGLUtils {
} }
public static void drawRotatedRectWithCustomSizedTexture(int x, int y, float rotation, float u, float v, int width, int height, float textureWidth, float textureHeight) { public static void drawRotatedRectWithCustomSizedTexture(int x, int y, float rotation, float u, float v, int width, int height, float textureWidth, float textureHeight) {
GL11.glPushMatrix(); GlStateManager.pushMatrix();
float f4 = 1.0F / textureWidth; float f4 = 1.0F / textureWidth;
float f5 = 1.0F / textureHeight; float f5 = 1.0F / textureHeight;
Tessellator tessellator = Tessellator.instance; Tessellator tessellator = Tessellator.getInstance();
GL11.glTranslatef(x+(width/2), y+(width/2), 0); VertexBuffer vertexBuffer = tessellator.getBuffer();
GL11.glRotatef(rotation, 0, 0, 1); GlStateManager.translate(x+(width/2), y+(width/2), 0);
tessellator.startDrawingQuads(); GlStateManager.rotate(rotation, 0, 0, 1);
tessellator.addVertexWithUV(-width / 2, height / 2, 0.0D, (double) (u * f4), (double) ((v + (float) height) * f5)); vertexBuffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
tessellator.addVertexWithUV(width/2, height/2, 0.0D, (double)((u + (float)width) * f4), (double)((v + (float)height) * f5)); vertexBuffer.pos(-width / 2, height / 2, 0.0D).tex((double) (u * f4), (double) ((v + (float) height) * f5)).endVertex();
tessellator.addVertexWithUV(width/2, -height/2, 0.0D, (double)((u + (float)width) * f4), (double)(v * f5)); vertexBuffer.pos(width/2, height/2, 0.0D).tex((double)((u + (float)width) * f4), (double)((v + (float)height) * f5)).endVertex();
tessellator.addVertexWithUV(-width/2, -height/2, 0.0D, (double)(u * f4), (double)(v * f5)); vertexBuffer.pos(width/2, -height/2, 0.0D).tex((double)((u + (float)width) * f4), (double)(v * f5)).endVertex();
vertexBuffer.pos(-width/2, -height/2, 0.0D).tex((double)(u * f4), (double)(v * f5)).endVertex();
tessellator.draw(); tessellator.draw();
GL11.glPopMatrix(); GlStateManager.popMatrix();
} }
} }

View File

@@ -1,10 +1,7 @@
package com.replaymod.core.utils; package com.replaymod.core.utils;
import io.netty.buffer.Unpooled;
import net.minecraft.network.PacketBuffer; import net.minecraft.network.PacketBuffer;
import net.minecraft.network.play.server.S3FPacketCustomPayload; import net.minecraft.network.play.server.SPacketCustomPayload;
import java.io.IOException;
/** /**
* Restrictions set by the server, * Restrictions set by the server,
@@ -17,8 +14,8 @@ public class Restrictions {
private boolean onlyFirstPerson; private boolean onlyFirstPerson;
private boolean onlyRecordingPlayer; private boolean onlyRecordingPlayer;
public String handle(S3FPacketCustomPayload packet) throws IOException { public String handle(SPacketCustomPayload packet) {
PacketBuffer buffer = new PacketBuffer(Unpooled.wrappedBuffer(packet.func_149168_d())); PacketBuffer buffer = packet.getBufferData();
while (buffer.isReadable()) { while (buffer.isReadable()) {
String name = buffer.readStringFromBuffer(64); String name = buffer.readStringFromBuffer(64);
boolean active = buffer.readBoolean(); boolean active = buffer.readBoolean();

View File

@@ -1,6 +1,5 @@
package com.replaymod.core.utils; package com.replaymod.core.utils;
import com.google.common.net.PercentEscaper;
import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
@@ -17,15 +16,11 @@ 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 lombok.SneakyThrows;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.AbstractClientPlayer;
import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.network.NetworkPlayerInfo;
import net.minecraft.client.resources.DefaultPlayerSkin;
import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReport;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import org.apache.commons.io.Charsets;
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.input.Keyboard;
import org.lwjgl.util.Dimension; import org.lwjgl.util.Dimension;
@@ -41,8 +36,6 @@ import java.awt.image.BufferedImage;
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.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.security.KeyManagementException; import java.security.KeyManagementException;
import java.security.KeyStore; import java.security.KeyStore;
import java.security.KeyStoreException; import java.security.KeyStoreException;
@@ -144,28 +137,15 @@ public class Utils {
return mail.matches("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$"); return mail.matches("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$");
} }
private static final PercentEscaper REPLAY_NAME_ENCODER = new PercentEscaper(".-_ ", false);
public static String replayNameToFileName(String replayName) {
return REPLAY_NAME_ENCODER.escape(replayName) + ".mcpr";
}
@SneakyThrows(UnsupportedEncodingException.class)
public static String fileNameToReplayName(String fileName) {
String baseName = FilenameUtils.getBaseName(fileName);
try {
return URLDecoder.decode(baseName, Charsets.UTF_8.name());
} catch (IllegalArgumentException e) {
return baseName;
}
}
public static ResourceLocation getResourceLocationForPlayerUUID(UUID uuid) { public static ResourceLocation getResourceLocationForPlayerUUID(UUID uuid) {
EntityPlayer player = Minecraft.getMinecraft().theWorld.getPlayerEntityByUUID(uuid); NetworkPlayerInfo info = getMinecraft().getConnection().getPlayerInfo(uuid);
if (player == null || !(player instanceof AbstractClientPlayer)) { ResourceLocation skinLocation;
return AbstractClientPlayer.locationStevePng; if (info != null && info.hasLocationSkin()) {
skinLocation = info.getLocationSkin();
} else {
skinLocation = DefaultPlayerSkin.getDefaultSkin(uuid);
} }
return ((AbstractClientPlayer) player).getLocationSkin(); return skinLocation;
} }
public static boolean isCtrlDown() { public static boolean isCtrlDown() {

View File

@@ -27,7 +27,7 @@ public class WrappedTimer extends Timer {
to.elapsedPartialTicks = from.elapsedPartialTicks; to.elapsedPartialTicks = from.elapsedPartialTicks;
to.lastSyncSysClock = from.lastSyncSysClock; to.lastSyncSysClock = from.lastSyncSysClock;
to.lastSyncHRClock = from.lastSyncHRClock; to.lastSyncHRClock = from.lastSyncHRClock;
to.field_74285_i = from.field_74285_i; to.counter = from.counter;
to.timeSyncAdjustment = from.timeSyncAdjustment; to.timeSyncAdjustment = from.timeSyncAdjustment;
} }
} }

View File

@@ -3,15 +3,14 @@ package com.replaymod.editor;
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 cpw.mods.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import cpw.mods.fml.common.event.FMLInitializationEvent; import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent; import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
@Mod(modid = ReplayModEditor.MOD_ID, @Mod(modid = ReplayModEditor.MOD_ID,
version = "@MOD_VERSION@", version = "@MOD_VERSION@",
acceptedMinecraftVersions = "@MC_VERSION@", acceptedMinecraftVersions = "@MC_VERSION@",
acceptableRemoteVersions = "*",
useMetadata = true) useMetadata = true)
public class ReplayModEditor { public class ReplayModEditor {
public static final String MOD_ID = "replaymod-editor"; public static final String MOD_ID = "replaymod-editor";

View File

@@ -36,6 +36,7 @@ import de.johni0702.minecraft.gui.popup.GuiYesNoPopup;
import de.johni0702.minecraft.gui.utils.Colors; import de.johni0702.minecraft.gui.utils.Colors;
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.io.FilenameUtils;
import org.lwjgl.util.ReadableDimension; import org.lwjgl.util.ReadableDimension;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -124,7 +125,7 @@ public class GuiReplayEditor extends GuiScreen {
} }
public void save(File inputFile, PacketStream.FilterInfo...filters) { public void save(File inputFile, PacketStream.FilterInfo...filters) {
save(Utils.fileNameToReplayName(inputFile.getName()), (outputFile) -> { save(FilenameUtils.getBaseName(inputFile.getName()), (outputFile) -> {
Studio studio = new ReplayStudio(); Studio studio = new ReplayStudio();
File tmpDir = null; File tmpDir = null;
try { try {
@@ -219,7 +220,7 @@ public class GuiReplayEditor extends GuiScreen {
Utils.error(LOGGER, GuiReplayEditor.this, CrashReport.makeCrashReport(e, "Getting replay folder"), null); Utils.error(LOGGER, GuiReplayEditor.this, CrashReport.makeCrashReport(e, "Getting replay folder"), null);
return; return;
} }
File targetFile = new File(replayFolder, Utils.replayNameToFileName(name)); File targetFile = new File(replayFolder, name + ".mcpr");
if (targetFile.exists()) { if (targetFile.exists()) {
LOGGER.trace("Selected file already exists, asking for confirmation"); LOGGER.trace("Selected file already exists, asking for confirmation");
Futures.addCallback(GuiYesNoPopup.open(GuiReplayEditor.this, Futures.addCallback(GuiYesNoPopup.open(GuiReplayEditor.this,

View File

@@ -20,6 +20,7 @@ import de.johni0702.minecraft.gui.layout.HorizontalLayout;
import de.johni0702.minecraft.gui.layout.VerticalLayout; import de.johni0702.minecraft.gui.layout.VerticalLayout;
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.FilenameUtils;
import org.apache.commons.io.IOCase; import org.apache.commons.io.IOCase;
import org.apache.commons.io.filefilter.SuffixFileFilter; import org.apache.commons.io.filefilter.SuffixFileFilter;
import org.lwjgl.util.Dimension; import org.lwjgl.util.Dimension;
@@ -51,7 +52,7 @@ public class GuiTrimPanel extends GuiPanel {
public final GuiDropdownMenu<File> inputReplays = new GuiDropdownMenu<File>(this) public final GuiDropdownMenu<File> inputReplays = new GuiDropdownMenu<File>(this)
.setMinSize(new Dimension(200, 20)).onSelection(i -> updateSelectedReplay()) .setMinSize(new Dimension(200, 20)).onSelection(i -> updateSelectedReplay())
.setToString(f -> f == NO_REPLAY ? "" : Utils.fileNameToReplayName(f.getName())); .setToString(f -> f == NO_REPLAY ? "" : FilenameUtils.getBaseName(f.getName()));
public final GuiNumberField startHour = newGuiNumberField(); public final GuiNumberField startHour = newGuiNumberField();
public final GuiNumberField startMin = newGuiNumberField(); public final GuiNumberField startMin = newGuiNumberField();

View File

@@ -2,16 +2,13 @@ package com.replaymod.editor.handler;
import com.replaymod.editor.ReplayModEditor; import com.replaymod.editor.ReplayModEditor;
import com.replaymod.editor.gui.GuiReplayEditor; import com.replaymod.editor.gui.GuiReplayEditor;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import de.johni0702.minecraft.gui.container.GuiScreen; import de.johni0702.minecraft.gui.container.GuiScreen;
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.resources.I18n; import net.minecraft.client.resources.I18n;
import net.minecraftforge.client.event.GuiScreenEvent; import net.minecraftforge.client.event.GuiScreenEvent;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import java.util.List;
public class GuiHandler { public class GuiHandler {
private static final int BUTTON_REPLAY_EDITOR = 17890237; private static final int BUTTON_REPLAY_EDITOR = 17890237;
@@ -23,31 +20,28 @@ public class GuiHandler {
} }
public void register() { public void register() {
FMLCommonHandler.instance().bus().register(this);
MinecraftForge.EVENT_BUS.register(this); MinecraftForge.EVENT_BUS.register(this);
} }
@SubscribeEvent @SubscribeEvent
public void injectIntoMainMenu(GuiScreenEvent.InitGuiEvent event) { public void injectIntoMainMenu(GuiScreenEvent.InitGuiEvent event) {
if (!(event.gui instanceof GuiMainMenu)) { if (!(event.getGui() instanceof GuiMainMenu)) {
return; return;
} }
@SuppressWarnings("unchecked") GuiButton button = new GuiButton(BUTTON_REPLAY_EDITOR, event.getGui().width / 2 + 2,
List<GuiButton> buttonList = event.buttonList; event.getGui().height / 4 + 10 + 3 * 24, I18n.format("replaymod.gui.replayeditor"));
GuiButton button = new GuiButton(BUTTON_REPLAY_EDITOR, event.gui.width / 2 + 2,
event.gui.height / 4 + 10 + 3 * 24, I18n.format("replaymod.gui.replayeditor"));
button.width = button.width / 2 - 2; button.width = button.width / 2 - 2;
buttonList.add(button); event.getButtonList().add(button);
} }
@SubscribeEvent @SubscribeEvent
public void onButton(GuiScreenEvent.ActionPerformedEvent.Pre event) { public void onButton(GuiScreenEvent.ActionPerformedEvent.Pre event) {
if(!event.button.enabled) return; if(!event.getButton().enabled) return;
if (event.gui instanceof GuiMainMenu) { if (event.getGui() instanceof GuiMainMenu) {
if (event.button.id == BUTTON_REPLAY_EDITOR) { if (event.getButton().id == BUTTON_REPLAY_EDITOR) {
new GuiReplayEditor(GuiScreen.wrap(event.gui), mod.getCore()).display(); new GuiReplayEditor(GuiScreen.wrap(event.getGui()), mod.getCore()).display();
} }
} }
} }

View File

@@ -5,13 +5,13 @@ import com.replaymod.replay.ReplayHandler;
import com.replaymod.replay.ReplayModReplay; import com.replaymod.replay.ReplayModReplay;
import com.replaymod.replay.events.ReplayOpenEvent; import com.replaymod.replay.events.ReplayOpenEvent;
import com.replaymod.replay.gui.overlay.GuiReplayOverlay; import com.replaymod.replay.gui.overlay.GuiReplayOverlay;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.TickEvent;
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 net.minecraft.client.settings.GameSettings;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
import org.lwjgl.input.Keyboard; import org.lwjgl.input.Keyboard;
public class FullBrightness implements Extra { public class FullBrightness implements Extra {
@@ -40,7 +40,7 @@ public class FullBrightness implements Extra {
} }
}); });
FMLCommonHandler.instance().bus().register(this); MinecraftForge.EVENT_BUS.register(this);
} }
@SubscribeEvent @SubscribeEvent

View File

@@ -4,8 +4,6 @@ import com.replaymod.core.KeyBindingRegistry;
import com.replaymod.core.ReplayMod; import com.replaymod.core.ReplayMod;
import com.replaymod.replay.events.ReplayOpenEvent; import com.replaymod.replay.events.ReplayOpenEvent;
import com.replaymod.replay.gui.overlay.GuiReplayOverlay; import com.replaymod.replay.gui.overlay.GuiReplayOverlay;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import de.johni0702.minecraft.gui.GuiRenderer; import de.johni0702.minecraft.gui.GuiRenderer;
import de.johni0702.minecraft.gui.RenderInfo; import de.johni0702.minecraft.gui.RenderInfo;
import de.johni0702.minecraft.gui.container.GuiContainer; import de.johni0702.minecraft.gui.container.GuiContainer;
@@ -19,6 +17,8 @@ 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 net.minecraft.client.resources.I18n; import net.minecraft.client.resources.I18n;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import org.lwjgl.input.Keyboard; import org.lwjgl.input.Keyboard;
import org.lwjgl.util.Dimension; import org.lwjgl.util.Dimension;
import org.lwjgl.util.ReadableDimension; import org.lwjgl.util.ReadableDimension;
@@ -35,7 +35,7 @@ public class HotkeyButtons implements Extra {
public void register(ReplayMod mod) throws Exception { public void register(ReplayMod mod) throws Exception {
this.mod = mod; this.mod = mod;
FMLCommonHandler.instance().bus().register(this); MinecraftForge.EVENT_BUS.register(this);
} }
@SubscribeEvent @SubscribeEvent

View File

@@ -5,7 +5,7 @@ import com.replaymod.core.ReplayMod;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.IResourcePack; import net.minecraft.client.resources.IResourcePack;
import net.minecraft.client.resources.data.IMetadataSection; import net.minecraft.client.resources.data.IMetadataSection;
import net.minecraft.client.resources.data.IMetadataSerializer; import net.minecraft.client.resources.data.MetadataSerializer;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
@@ -99,12 +99,12 @@ public class LocalizationExtra implements Extra {
} }
@Override @Override
public Set getResourceDomains() { public Set<String> getResourceDomains() {
return ImmutableSet.of("replaymod"); return ImmutableSet.of("replaymod");
} }
@Override @Override
public IMetadataSection getPackMetadata(IMetadataSerializer p_135058_1_, String p_135058_2_) throws IOException { public <T extends IMetadataSection> T getPackMetadata(MetadataSerializer metadataSerializer, String metadataSectionName) throws IOException {
return null; return null;
} }

View File

@@ -2,7 +2,6 @@ package com.replaymod.extras;
import com.replaymod.core.ReplayMod; import com.replaymod.core.ReplayMod;
import com.replaymod.core.Setting; import com.replaymod.core.Setting;
import cpw.mods.fml.common.Loader;
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;
@@ -15,6 +14,7 @@ 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 de.johni0702.minecraft.gui.utils.Colors; import de.johni0702.minecraft.gui.utils.Colors;
import net.minecraftforge.fml.common.Loader;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.HttpsURLConnection;

View File

@@ -4,21 +4,17 @@ import com.replaymod.core.ReplayMod;
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 cpw.mods.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import cpw.mods.fml.common.event.FMLInitializationEvent; import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent; import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Optional;
@Mod(modid = ReplayModExtras.MOD_ID, @Mod(modid = ReplayModExtras.MOD_ID,
version = "@MOD_VERSION@", version = "@MOD_VERSION@",
acceptedMinecraftVersions = "@MC_VERSION@", acceptedMinecraftVersions = "@MC_VERSION@",
acceptableRemoteVersions = "*",
useMetadata = true) useMetadata = true)
public class ReplayModExtras { public class ReplayModExtras {
public static final String MOD_ID = "replaymod-extras"; public static final String MOD_ID = "replaymod-extras";
@@ -36,8 +32,6 @@ public class ReplayModExtras {
OpenEyeExtra.class OpenEyeExtra.class
); );
private final Map<Class<? extends Extra>, Extra> instances = new HashMap<>();
public static Logger LOGGER; public static Logger LOGGER;
@Mod.EventHandler @Mod.EventHandler
@@ -51,14 +45,9 @@ public class ReplayModExtras {
try { try {
Extra extra = cls.newInstance(); Extra extra = cls.newInstance();
extra.register(ReplayMod.instance); extra.register(ReplayMod.instance);
instances.put(cls, extra);
} catch (Throwable t) { } catch (Throwable t) {
LOGGER.warn("Failed to load extra " + cls.getName() + ": ", t); LOGGER.warn("Failed to load extra " + cls.getName() + ": ", t);
} }
} }
} }
public <T extends Extra> Optional<T> get(Class<T> cls) {
return Optional.ofNullable(instances.get(cls)).map(cls::cast);
}
} }

View File

@@ -1,6 +1,7 @@
package com.replaymod.extras.playeroverview; package com.replaymod.extras.playeroverview;
import com.google.common.base.Optional; import com.google.common.base.Optional;
import com.google.common.base.Predicate;
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.extras.Extra; import com.replaymod.extras.Extra;
@@ -8,23 +9,17 @@ import com.replaymod.replay.ReplayModReplay;
import com.replaymod.replay.camera.CameraEntity; import com.replaymod.replay.camera.CameraEntity;
import com.replaymod.replay.events.ReplayCloseEvent; import com.replaymod.replay.events.ReplayCloseEvent;
import com.replaymod.replay.events.ReplayOpenEvent; import com.replaymod.replay.events.ReplayOpenEvent;
import cpw.mods.fml.common.FMLCommonHandler; import net.minecraft.client.renderer.entity.RenderManager;
import cpw.mods.fml.common.eventhandler.EventPriority; import net.minecraft.client.renderer.entity.RenderPlayer;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
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 net.minecraftforge.client.event.RenderPlayerEvent;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import org.lwjgl.input.Keyboard; import org.lwjgl.input.Keyboard;
import java.io.IOException; import java.io.IOException;
import java.util.HashSet; import java.util.*;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
public class PlayerOverview implements Extra { public class PlayerOverview implements Extra {
private ReplayModReplay module; private ReplayModReplay module;
@@ -41,10 +36,12 @@ public class PlayerOverview implements Extra {
public void run() { public void run() {
if (module.getReplayHandler() != null) { if (module.getReplayHandler() != null) {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
List<EntityPlayer> players = mod.getMinecraft().theWorld.playerEntities; List<EntityPlayer> players = mod.getMinecraft().theWorld.getPlayers(EntityPlayer.class, new Predicate() {
players = players.stream() @Override
.filter(it -> !(it instanceof CameraEntity)) // Exclude the camera entity public boolean apply(Object input) {
.collect(Collectors.toList()); return !(input instanceof CameraEntity); // Exclude the camera entity
}
});
if (!Utils.isCtrlDown()) { if (!Utils.isCtrlDown()) {
// Hide all players that have an UUID v2 (commonly used for NPCs) // Hide all players that have an UUID v2 (commonly used for NPCs)
Iterator<EntityPlayer> iter = players.iterator(); Iterator<EntityPlayer> iter = players.iterator();
@@ -60,8 +57,13 @@ public class PlayerOverview implements Extra {
} }
}); });
FMLCommonHandler.instance().bus().register(this);
MinecraftForge.EVENT_BUS.register(this); MinecraftForge.EVENT_BUS.register(this);
RenderManager renderManager = mod.getMinecraft().getRenderManager();
@SuppressWarnings("unchecked")
Map<String, RenderPlayer> skinMap = renderManager.skinMap;
skinMap.put("default", new PlayerRenderHook(this, renderManager, false));
skinMap.put("slim", new PlayerRenderHook(this, renderManager, true));
} }
public boolean isHidden(UUID uuid) { public boolean isHidden(UUID uuid) {
@@ -94,19 +96,12 @@ public class PlayerOverview implements Extra {
@SubscribeEvent @SubscribeEvent
public void oRenderHand(RenderHandEvent event) { public void oRenderHand(RenderHandEvent event) {
Entity view = module.getCore().getMinecraft().renderViewEntity; Entity view = module.getCore().getMinecraft().getRenderViewEntity();
if (view != null && isHidden(view.getUniqueID())) { if (view != null && isHidden(view.getUniqueID())) {
event.setCanceled(true); event.setCanceled(true);
} }
} }
@SubscribeEvent(priority = EventPriority.HIGHEST)
public void preRenderPlayer(RenderPlayerEvent.Pre event) {
if (isHidden(event.entityPlayer.getUniqueID())) {
event.setCanceled(true);
}
}
public boolean isSavingEnabled() { public boolean isSavingEnabled() {
return savingEnabled; return savingEnabled;
} }

View File

@@ -20,7 +20,9 @@ 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 net.minecraft.client.audio.PositionedSoundRecord;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.potion.Potion; import net.minecraft.entity.player.EnumPlayerModelParts;
import net.minecraft.init.MobEffects;
import net.minecraft.init.SoundEvents;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import org.lwjgl.util.Dimension; import org.lwjgl.util.Dimension;
import org.lwjgl.util.ReadableDimension; import org.lwjgl.util.ReadableDimension;
@@ -45,14 +47,14 @@ 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() {
getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.createPositionedSoundRecord(BUTTON_SOUND, 1.0F)); getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(SoundEvents.UI_BUTTON_CLICK, 1.0F));
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() {
getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.createPositionedSoundRecord(BUTTON_SOUND, 1.0F)); getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(SoundEvents.UI_BUTTON_CLICK, 1.0F));
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"));
@@ -95,10 +97,12 @@ public class PlayerOverviewGui extends GuiScreen implements Closeable {
public void draw(GuiRenderer renderer, ReadableDimension size, RenderInfo renderInfo) { public void draw(GuiRenderer renderer, ReadableDimension size, RenderInfo renderInfo) {
renderer.bindTexture(texture); renderer.bindTexture(texture);
renderer.drawTexturedRect(0, 0, 8, 8, 16, 16, 8, 8, 64, 64); renderer.drawTexturedRect(0, 0, 8, 8, 16, 16, 8, 8, 64, 64);
renderer.drawTexturedRect(0, 0, 40, 8, size.getWidth(), size.getHeight(), 8, 8, 64, 64); if (p.isWearing(EnumPlayerModelParts.HAT)) {
renderer.drawTexturedRect(0, 0, 40, 8, size.getWidth(), size.getHeight(), 8, 8, 64, 64);
}
} }
}.setSize(16, 16), }.setSize(16, 16),
new GuiLabel().setText(p.getDisplayName()).setColor(isSpectator(p) ? Colors.DKGREY : Colors.WHITE) new GuiLabel().setText(p.getName()).setColor(isSpectator(p) ? Colors.DKGREY : Colors.WHITE)
).onClick(new Runnable() { ).onClick(new Runnable() {
@Override @Override
public void run() { public void run() {
@@ -142,7 +146,7 @@ public class PlayerOverviewGui extends GuiScreen implements Closeable {
} }
private static boolean isSpectator(EntityPlayer e) { private static boolean isSpectator(EntityPlayer e) {
return e.isInvisible() && e.getActivePotionEffect(Potion.invisibility) == null; return e.isInvisible() && e.getActivePotionEffect(MobEffects.INVISIBILITY) == null;
} }
private static final class PlayerComparator implements Comparator<EntityPlayer> { private static final class PlayerComparator implements Comparator<EntityPlayer> {
@@ -150,7 +154,7 @@ 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;
return o1.getDisplayName().compareToIgnoreCase(o2.getDisplayName()); return o1.getName().compareToIgnoreCase(o2.getName());
} }
} }
} }

View File

@@ -0,0 +1,20 @@
package com.replaymod.extras.playeroverview;
import net.minecraft.client.entity.AbstractClientPlayer;
import net.minecraft.client.renderer.culling.ICamera;
import net.minecraft.client.renderer.entity.RenderManager;
import net.minecraft.client.renderer.entity.RenderPlayer;
public class PlayerRenderHook extends RenderPlayer {
private final PlayerOverview extra;
public PlayerRenderHook(PlayerOverview extra, RenderManager renderManager, boolean useSmallArms) {
super(renderManager, useSmallArms);
this.extra = extra;
}
@Override
public boolean shouldRender(AbstractClientPlayer entity, ICamera camera, double camX, double camY, double camZ) {
return !extra.isHidden(entity.getUniqueID()) && super.shouldRender(entity, camera, camX, camY, camZ);
}
}

View File

@@ -1,39 +0,0 @@
package com.replaymod.extras.playeroverview.mixin;
import com.replaymod.extras.ReplayModExtras;
import com.replaymod.extras.playeroverview.PlayerOverview;
import net.minecraft.client.model.ModelBase;
import net.minecraft.client.renderer.entity.RenderPlayer;
import net.minecraft.client.renderer.entity.RendererLivingEntity;
import net.minecraft.entity.Entity;
import org.spongepowered.asm.mixin.Mixin;
import java.util.UUID;
/**
* This mixin prevents players that are hidden in the PlayerOverview from being rendered.
*
* Cancelling the RenderPlayerEvent.Pre is insufficient because it affects neither the shadows nor the fire texture.
* See: https://github.com/MinecraftForge/MinecraftForge/issues/2987
*
* The previous solution was to overwrite the RenderPlayer instances which has been dropped in favor of this one
* because it is less compatible with other mods whereas this one should be fine as long as no other mod completely
* overwrites the methods we modify in this mixin.
* One example of the previous solution breaking is when used with VanillaEnhancements because it replaces the
* RenderManager with a new custom one which in turn will reset our registered RenderPlayer instances because
* it does so after we have already registered with the old RenderManager.
*/
@Mixin(value = RenderPlayer.class, priority = 1200)
public abstract class MixinRenderPlayer extends RendererLivingEntity {
public MixinRenderPlayer(ModelBase p_i1261_1_, float p_i1261_2_) {
super(p_i1261_1_, p_i1261_2_);
}
@Override
public void doRenderShadowAndFire(Entity entity, double d1, double d2, double d3, float f1, float f2) {
UUID uuid = entity.getUniqueID();
if (!ReplayModExtras.instance.get(PlayerOverview.class).map(po -> po.isHidden(uuid)).orElse(false)) {
super.doRenderShadowAndFire(entity, d1, d2, d3, f1, f2);
}
}
}

View File

@@ -25,7 +25,6 @@ public class LinuxUriScheme extends UriScheme {
"Icon=" + iconFile.getAbsolutePath().replace("\\", "\\\\").replace("\"", "\\\"") + "\n" + "Icon=" + iconFile.getAbsolutePath().replace("\\", "\\\\").replace("\"", "\\\"") + "\n" +
"Type=Application\n" + "Type=Application\n" +
"Terminal=false\n" + "Terminal=false\n" +
"NoDisplay=true\n" +
"MimeType=x-scheme-handler/replaymod;"; "MimeType=x-scheme-handler/replaymod;";
FileOutputStream out = new FileOutputStream(file); FileOutputStream out = new FileOutputStream(file);

View File

@@ -3,11 +3,11 @@ package com.replaymod.extras.youtube;
import com.replaymod.core.ReplayMod; import com.replaymod.core.ReplayMod;
import com.replaymod.extras.Extra; import com.replaymod.extras.Extra;
import com.replaymod.render.gui.GuiRenderingDone; import com.replaymod.render.gui.GuiRenderingDone;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import de.johni0702.minecraft.gui.container.GuiScreen; import de.johni0702.minecraft.gui.container.GuiScreen;
import de.johni0702.minecraft.gui.element.GuiButton; import de.johni0702.minecraft.gui.element.GuiButton;
import net.minecraftforge.client.event.GuiScreenEvent; import net.minecraftforge.client.event.GuiScreenEvent;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
public class YoutubeUpload implements Extra { public class YoutubeUpload implements Extra {
@Override @Override
@@ -17,8 +17,8 @@ public class YoutubeUpload implements Extra {
@SubscribeEvent @SubscribeEvent
public void onGuiOpen(GuiScreenEvent.InitGuiEvent.Post event) { public void onGuiOpen(GuiScreenEvent.InitGuiEvent.Post event) {
if (GuiScreen.from(event.gui) instanceof GuiRenderingDone) { if (GuiScreen.from(event.getGui()) instanceof GuiRenderingDone) {
GuiRenderingDone gui = (GuiRenderingDone) GuiScreen.from(event.gui); GuiRenderingDone gui = (GuiRenderingDone) GuiScreen.from(event.getGui());
// Check if there already is a youtube button // Check if there already is a youtube button
if (gui.actionsPanel.getChildren().stream().anyMatch(it -> it instanceof YoutubeButton)) { if (gui.actionsPanel.getChildren().stream().anyMatch(it -> it instanceof YoutubeButton)) {
return; // Button already added return; // Button already added

View File

@@ -36,8 +36,6 @@ import java.security.GeneralSecurityException;
import java.util.Collections; import java.util.Collections;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
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";
@@ -139,7 +137,7 @@ public class YoutubeUploader {
CommandLine commandLine = new CommandLine(settings.getExportCommand()); CommandLine commandLine = new CommandLine(settings.getExportCommand());
commandLine.addArguments(args); commandLine.addArguments(args);
LOGGER.info("Re-encoding for ODS with {} {}", settings.getExportCommand(), args); System.out.println("Re-encoding for ODS with " + settings.getExportCommand() + args);
Process process = new ProcessBuilder(commandLine.toStrings()).directory(videoFile.getParentFile()).start(); Process process = new ProcessBuilder(commandLine.toStrings()).directory(videoFile.getParentFile()).start();
final AtomicBoolean active = new AtomicBoolean(true); final AtomicBoolean active = new AtomicBoolean(true);
@@ -151,7 +149,7 @@ public class YoutubeUploader {
char c = (char) in.read(); char c = (char) in.read();
if (c == '\r') { if (c == '\r') {
String str = sb.toString(); String str = sb.toString();
LOGGER.debug("[FFmpeg] {}", str); System.out.println(str);
if (str.startsWith("frame=")) { if (str.startsWith("frame=")) {
str = str.substring(6).trim(); str = str.substring(6).trim();
str = str.substring(0, str.indexOf(' ')); str = str.substring(0, str.indexOf(' '));

View File

@@ -11,14 +11,14 @@ import com.replaymod.replay.events.ReplayCloseEvent;
import com.replaymod.replaystudio.replay.ReplayFile; 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 cpw.mods.fml.common.FMLCommonHandler;
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 de.johni0702.minecraft.gui.container.GuiScreen; import de.johni0702.minecraft.gui.container.GuiScreen;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.config.Configuration; import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.fml.common.Mod;
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.SubscribeEvent;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import java.io.File; import java.io.File;
@@ -29,7 +29,6 @@ import static net.minecraft.client.Minecraft.getMinecraft;
@Mod(modid = ReplayModOnline.MOD_ID, @Mod(modid = ReplayModOnline.MOD_ID,
version = "@MOD_VERSION@", version = "@MOD_VERSION@",
acceptedMinecraftVersions = "@MC_VERSION@", acceptedMinecraftVersions = "@MC_VERSION@",
acceptableRemoteVersions = "*",
useMetadata = true) useMetadata = true)
public class ReplayModOnline { public class ReplayModOnline {
public static final String MOD_ID = "replaymod-online"; public static final String MOD_ID = "replaymod-online";
@@ -41,7 +40,7 @@ public class ReplayModOnline {
private ReplayModReplay replayModule; private ReplayModReplay replayModule;
public static Logger LOGGER; private Logger logger;
private ApiClient apiClient; private ApiClient apiClient;
@@ -54,7 +53,7 @@ public class ReplayModOnline {
@Mod.EventHandler @Mod.EventHandler
public void preInit(FMLPreInitializationEvent event) { public void preInit(FMLPreInitializationEvent event) {
LOGGER = event.getModLog(); logger = event.getModLog();
core = ReplayMod.instance; core = ReplayMod.instance;
replayModule = ReplayModReplay.instance; replayModule = ReplayModReplay.instance;
@@ -70,12 +69,12 @@ public class ReplayModOnline {
public void init(FMLInitializationEvent event) { public void init(FMLInitializationEvent event) {
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());
} }
} }
new GuiHandler(this).register(); new GuiHandler(this).register();
FMLCommonHandler.instance().bus().register(this); MinecraftForge.EVENT_BUS.register(this);
} }
@Mod.EventHandler @Mod.EventHandler
@@ -83,10 +82,7 @@ public class ReplayModOnline {
// 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()) {
core.runLater(() -> { core.runLater(() -> new GuiLoginPrompt(apiClient, GuiScreen.wrap(getMinecraft().currentScreen), null, false).display());
GuiScreen parent = GuiScreen.wrap(getMinecraft().currentScreen);
new GuiLoginPrompt(apiClient, parent, parent, false).display();
});
} }
} }
} }
@@ -100,7 +96,7 @@ public class ReplayModOnline {
} }
public Logger getLogger() { public Logger getLogger() {
return LOGGER; return logger;
} }
public ApiClient getApiClient() { public ApiClient getApiClient() {

View File

@@ -2,7 +2,8 @@ package com.replaymod.online.api.replay.holders;
public enum MinecraftVersion { public enum MinecraftVersion {
MC_1_8("Minecraft 1.8", "1.8"); MC_1_8("Minecraft 1.8", "1.8"),
MC_1_9_4("Minecraft 1.9.4", "1.9.4");
private String niceName, apiName; private String niceName, apiName;

View File

@@ -37,6 +37,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 net.minecraftforge.fml.common.FMLLog;
import org.apache.commons.io.FilenameUtils;
import org.apache.logging.log4j.core.helpers.Strings; import org.apache.logging.log4j.core.helpers.Strings;
import org.lwjgl.util.Dimension; import org.lwjgl.util.Dimension;
import org.lwjgl.util.ReadableDimension; import org.lwjgl.util.ReadableDimension;
@@ -49,8 +51,6 @@ import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import static com.replaymod.online.ReplayModOnline.LOGGER;
public class GuiReplayCenter extends GuiScreen { public class GuiReplayCenter extends GuiScreen {
private final ReplayModOnline mod; private final ReplayModOnline mod;
private final ApiClient apiClient; private final ApiClient apiClient;
@@ -336,7 +336,7 @@ public class GuiReplayCenter extends GuiScreen {
} }
}); });
} catch (Exception e) { } catch (Exception e) {
LOGGER.error("Could not load Replay File {}", fileInfo.getId(), e); FMLLog.getLogger().error("Could not load Replay File " + fileInfo.getId(), e);
} }
} }
} }
@@ -417,7 +417,7 @@ public class GuiReplayCenter extends GuiScreen {
this.downloaded = downloaded; this.downloaded = downloaded;
ReplayMetaData metaData = fileInfo.getMetadata(); ReplayMetaData metaData = fileInfo.getMetadata();
name.setText(ChatFormatting.UNDERLINE + Utils.fileNameToReplayName(fileInfo.getName())); name.setText(ChatFormatting.UNDERLINE + FilenameUtils.getBaseName(fileInfo.getName()));
author.setI18nText("replaymod.gui.center.author", author.setI18nText("replaymod.gui.center.author",
"" + ChatFormatting.GRAY + ChatFormatting.ITALIC, fileInfo.getOwner()); "" + ChatFormatting.GRAY + ChatFormatting.ITALIC, fileInfo.getOwner());
if (Strings.isEmpty(metaData.getServerName())) { if (Strings.isEmpty(metaData.getServerName())) {

View File

@@ -2,7 +2,6 @@ package com.replaymod.online.gui;
import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.replaymod.core.utils.Utils;
import de.johni0702.minecraft.gui.container.GuiPanel; import de.johni0702.minecraft.gui.container.GuiPanel;
import de.johni0702.minecraft.gui.container.GuiScreen; import de.johni0702.minecraft.gui.container.GuiScreen;
import de.johni0702.minecraft.gui.element.GuiButton; import de.johni0702.minecraft.gui.element.GuiButton;
@@ -28,7 +27,7 @@ public class GuiSaveModifiedReplay extends GuiScreen {
@Override @Override
public void run() { public void run() {
String resultName = name.getText().trim().replace("[^a-zA-Z0-9\\.\\- ]", "_"); String resultName = name.getText().trim().replace("[^a-zA-Z0-9\\.\\- ]", "_");
final File resultFile = new File(file.getParentFile(), Utils.replayNameToFileName(resultName)); final File resultFile = new File(file.getParentFile(), resultName + ".mcpr");
if (resultFile.exists()) { if (resultFile.exists()) {
Futures.addCallback(GuiYesNoPopup.open(GuiSaveModifiedReplay.this, Futures.addCallback(GuiYesNoPopup.open(GuiSaveModifiedReplay.this,
new GuiLabel().setI18nText("replaymod.gui.replaymodified.warning1", resultName).setColor(Colors.BLACK), new GuiLabel().setI18nText("replaymod.gui.replaymodified.warning1", resultName).setColor(Colors.BLACK),

View File

@@ -38,6 +38,7 @@ import net.minecraft.client.settings.KeyBinding;
import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReport;
import net.minecraft.util.ReportedException; import net.minecraft.util.ReportedException;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -134,7 +135,7 @@ public class GuiUploadReplay extends GuiScreen {
} }
// Apply to gui // Apply to gui
name.setText(Utils.fileNameToReplayName(file.getName())); name.setText(FilenameUtils.getBaseName(file.getName()));
int secs = metaData.getDuration() / 1000; int secs = metaData.getDuration() / 1000;
durationLabel.setI18nText("replaymod.gui.upload.duration", secs / 60, secs % 60); durationLabel.setI18nText("replaymod.gui.upload.duration", secs / 60, secs % 60);
hideServerIP.setEnabled(!metaData.isSingleplayer()); hideServerIP.setEnabled(!metaData.isSingleplayer());

View File

@@ -5,8 +5,6 @@ import com.replaymod.online.gui.GuiLoginPrompt;
import com.replaymod.online.gui.GuiReplayCenter; import com.replaymod.online.gui.GuiReplayCenter;
import com.replaymod.online.gui.GuiUploadReplay; import com.replaymod.online.gui.GuiUploadReplay;
import com.replaymod.replay.gui.screen.GuiReplayViewer; import com.replaymod.replay.gui.screen.GuiReplayViewer;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import de.johni0702.minecraft.gui.container.AbstractGuiScreen; import de.johni0702.minecraft.gui.container.AbstractGuiScreen;
import de.johni0702.minecraft.gui.container.GuiPanel; import de.johni0702.minecraft.gui.container.GuiPanel;
import de.johni0702.minecraft.gui.container.GuiScreen; import de.johni0702.minecraft.gui.container.GuiScreen;
@@ -16,9 +14,9 @@ import net.minecraft.client.gui.GuiMainMenu;
import net.minecraft.client.resources.I18n; import net.minecraft.client.resources.I18n;
import net.minecraftforge.client.event.GuiScreenEvent; import net.minecraftforge.client.event.GuiScreenEvent;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import java.io.File; import java.io.File;
import java.util.List;
public class GuiHandler { public class GuiHandler {
private static final int BUTTON_REPLAY_CENTER = 17890236; private static final int BUTTON_REPLAY_CENTER = 17890236;
@@ -30,26 +28,23 @@ public class GuiHandler {
} }
public void register() { public void register() {
FMLCommonHandler.instance().bus().register(this);
MinecraftForge.EVENT_BUS.register(this); MinecraftForge.EVENT_BUS.register(this);
} }
@SubscribeEvent @SubscribeEvent
public void injectIntoMainMenu(GuiScreenEvent.InitGuiEvent event) { public void injectIntoMainMenu(GuiScreenEvent.InitGuiEvent event) {
if (!(event.gui instanceof GuiMainMenu)) { if (!(event.getGui() instanceof GuiMainMenu)) {
return; return;
} }
@SuppressWarnings("unchecked") GuiButton button = new GuiButton(BUTTON_REPLAY_CENTER, event.getGui().width / 2 - 100,
List<GuiButton> buttonList = event.buttonList; event.getGui().height / 4 + 10 + 4 * 24, I18n.format("replaymod.gui.replaycenter"));
GuiButton button = new GuiButton(BUTTON_REPLAY_CENTER, event.gui.width / 2 - 100, event.getButtonList().add(button);
event.gui.height / 4 + 10 + 4 * 24, I18n.format("replaymod.gui.replaycenter"));
buttonList.add(button);
} }
@SubscribeEvent @SubscribeEvent
public void injectIntoReplayViewer(GuiScreenEvent.InitGuiEvent.Post event) { public void injectIntoReplayViewer(GuiScreenEvent.InitGuiEvent.Post event) {
AbstractGuiScreen guiScreen = GuiScreen.from(event.gui); AbstractGuiScreen guiScreen = GuiScreen.from(event.getGui());
if (!(guiScreen instanceof GuiReplayViewer)) { if (!(guiScreen instanceof GuiReplayViewer)) {
return; return;
} }
@@ -75,15 +70,15 @@ public class GuiHandler {
@SubscribeEvent @SubscribeEvent
public void onButton(GuiScreenEvent.ActionPerformedEvent.Pre event) { public void onButton(GuiScreenEvent.ActionPerformedEvent.Pre event) {
if(!event.button.enabled) return; if(!event.getButton().enabled) return;
if (event.gui instanceof GuiMainMenu) { if (event.getGui() instanceof GuiMainMenu) {
if (event.button.id == BUTTON_REPLAY_CENTER) { if (event.getButton().id == BUTTON_REPLAY_CENTER) {
GuiReplayCenter replayCenter = new GuiReplayCenter(mod); GuiReplayCenter replayCenter = new GuiReplayCenter(mod);
if (mod.isLoggedIn()) { if (mod.isLoggedIn()) {
replayCenter.display(); replayCenter.display();
} else { } else {
new GuiLoginPrompt(mod.getApiClient(), GuiScreen.wrap(event.gui), replayCenter, true).display(); new GuiLoginPrompt(mod.getApiClient(), GuiScreen.wrap(event.getGui()), replayCenter, true).display();
} }
} }
} }

View File

@@ -10,9 +10,9 @@ import com.replaymod.replay.ReplayHandler;
import com.replaymod.replaystudio.pathing.path.Keyframe; import com.replaymod.replaystudio.pathing.path.Keyframe;
import com.replaymod.replaystudio.pathing.path.Path; import com.replaymod.replaystudio.pathing.path.Path;
import com.replaymod.replaystudio.pathing.path.Timeline; import com.replaymod.replaystudio.pathing.path.Timeline;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.Iterator; import java.util.Iterator;
@@ -65,7 +65,7 @@ public abstract class AbstractTimelinePlayer {
} }
replayHandler.getReplaySender().setSyncModeAndWait(); replayHandler.getReplaySender().setSyncModeAndWait();
FMLCommonHandler.instance().bus().register(this); MinecraftForge.EVENT_BUS.register(this);
lastTime = 0; lastTime = 0;
mc.timer = new ReplayTimer(mc.timer); mc.timer = new ReplayTimer(mc.timer);
mc.timer.timerSpeed = 1; mc.timer.timerSpeed = 1;
@@ -87,7 +87,7 @@ public abstract class AbstractTimelinePlayer {
mc.timer = ((ReplayTimer) mc.timer).getWrapped(); mc.timer = ((ReplayTimer) mc.timer).getWrapped();
replayHandler.getReplaySender().setReplaySpeed(0); replayHandler.getReplaySender().setReplaySpeed(0);
replayHandler.getReplaySender().setAsyncMode(true); replayHandler.getReplaySender().setAsyncMode(true);
FMLCommonHandler.instance().bus().unregister(this); MinecraftForge.EVENT_BUS.unregister(this);
return; return;
} }
long time = getTimePassed(); long time = getTimePassed();

View File

@@ -3,7 +3,6 @@ package com.replaymod.pathing.player;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import com.replaymod.replay.ReplayHandler; import com.replaymod.replay.ReplayHandler;
import com.replaymod.replaystudio.pathing.path.Timeline; import com.replaymod.replaystudio.pathing.path.Timeline;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
/** /**
* Timeline player using the system time. * Timeline player using the system time.
@@ -33,7 +32,6 @@ public class RealtimeTimelinePlayer extends AbstractTimelinePlayer {
} }
@Override @Override
@SubscribeEvent // 1.7.10 doesn't yet deal properly with inherited, overridden event handlers
public void onTick(ReplayTimer.UpdatedEvent event) { public void onTick(ReplayTimer.UpdatedEvent event) {
if (secondFrame) { if (secondFrame) {
secondFrame = false; secondFrame = false;

View File

@@ -1,9 +1,9 @@
package com.replaymod.pathing.player; package com.replaymod.pathing.player;
import com.replaymod.core.utils.WrappedTimer; import com.replaymod.core.utils.WrappedTimer;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.eventhandler.Event;
import net.minecraft.util.Timer; import net.minecraft.util.Timer;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.eventhandler.Event;
/** /**
* 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.
@@ -20,7 +20,7 @@ public class ReplayTimer extends WrappedTimer {
copy(this, state); // Save our current state copy(this, state); // Save our current state
super.updateTimer(); // Update current state super.updateTimer(); // Update current state
copy(state, this); // Restore our old state copy(state, this); // Restore our old state
FMLCommonHandler.instance().bus().post(new UpdatedEvent()); MinecraftForge.EVENT_BUS.post(new UpdatedEvent());
} }
public Timer getWrapped() { public Timer getWrapped() {

View File

@@ -8,7 +8,6 @@ import com.replaymod.replaystudio.pathing.property.PropertyPart;
import com.replaymod.replaystudio.pathing.property.PropertyParts; import com.replaymod.replaystudio.pathing.property.PropertyParts;
import lombok.NonNull; import lombok.NonNull;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.world.World; import net.minecraft.world.World;
import java.io.IOException; import java.io.IOException;
@@ -37,9 +36,7 @@ public class SpectatorProperty extends AbstractProperty<Integer> {
// Lookup entity by id, returns null if an entity with the id does not exists // Lookup entity by id, returns null if an entity with the id does not exists
Entity target = world.getEntityByID(value); Entity target = world.getEntityByID(value);
// Spectate entity, when called with null, returns to camera // Spectate entity, when called with null, returns to camera
if (target instanceof EntityLivingBase) { handler.spectateEntity(target);
handler.spectateEntity((EntityLivingBase) target);
}
} }
@Override @Override

View File

@@ -4,22 +4,21 @@ 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.packet.PacketListener; import com.replaymod.recording.packet.PacketListener;
import cpw.mods.fml.common.FMLCommonHandler;
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 io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import net.minecraftforge.common.MinecraftForge;
import net.minecraft.network.NetworkManager; import net.minecraft.network.NetworkManager;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.eventhandler.EventBus;
import net.minecraftforge.fml.common.network.NetworkRegistry;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.lwjgl.input.Keyboard; import org.lwjgl.input.Keyboard;
@Mod(modid = ReplayModRecording.MOD_ID, @Mod(modid = ReplayModRecording.MOD_ID,
version = "@MOD_VERSION@", version = "@MOD_VERSION@",
acceptedMinecraftVersions = "@MC_VERSION@", acceptedMinecraftVersions = "@MC_VERSION@",
acceptableRemoteVersions = "*",
useMetadata = true) useMetadata = true)
public class ReplayModRecording { public class ReplayModRecording {
public static final String MOD_ID = "replaymod-recording"; public static final String MOD_ID = "replaymod-recording";
@@ -54,7 +53,7 @@ public class ReplayModRecording {
@Mod.EventHandler @Mod.EventHandler
public void init(FMLInitializationEvent event) { public void init(FMLInitializationEvent event) {
EventBus bus = FMLCommonHandler.instance().bus(); EventBus bus = MinecraftForge.EVENT_BUS;
bus.register(connectionEventHandler = new ConnectionEventHandler(logger, core)); bus.register(connectionEventHandler = new ConnectionEventHandler(logger, core));
NetworkRegistry.INSTANCE.newChannel(Restrictions.PLUGIN_CHANNEL, new RestrictionsChannelHandler()); NetworkRegistry.INSTANCE.newChannel(Restrictions.PLUGIN_CHANNEL, new RestrictionsChannelHandler());

View File

@@ -2,14 +2,14 @@ package com.replaymod.recording.gui;
import com.replaymod.core.SettingsRegistry; import com.replaymod.core.SettingsRegistry;
import com.replaymod.recording.Setting; import com.replaymod.recording.Setting;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.Gui; import net.minecraft.client.gui.Gui;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.resources.I18n; import net.minecraft.client.resources.I18n;
import net.minecraftforge.client.event.RenderGameOverlayEvent; import net.minecraftforge.client.event.RenderGameOverlayEvent;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import org.lwjgl.opengl.GL11; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import static com.replaymod.core.ReplayMod.TEXTURE; import static com.replaymod.core.ReplayMod.TEXTURE;
import static com.replaymod.core.ReplayMod.TEXTURE_SIZE; import static com.replaymod.core.ReplayMod.TEXTURE_SIZE;
@@ -40,12 +40,13 @@ public class GuiRecordingOverlay {
*/ */
@SubscribeEvent @SubscribeEvent
public void renderRecordingIndicator(RenderGameOverlayEvent.Post event) { public void renderRecordingIndicator(RenderGameOverlayEvent.Post event) {
if (event.type != RenderGameOverlayEvent.ElementType.ALL) return; if (event.getType() != RenderGameOverlayEvent.ElementType.ALL) return;
if (settingsRegistry.get(Setting.INDICATOR)) { if (settingsRegistry.get(Setting.INDICATOR)) {
FontRenderer fontRenderer = mc.fontRendererObj; FontRenderer fontRenderer = mc.fontRendererObj;
fontRenderer.drawString(I18n.format("replaymod.gui.recording").toUpperCase(), 30, 18 - (fontRenderer.FONT_HEIGHT / 2), 0xffffffff); fontRenderer.drawString(I18n.format("replaymod.gui.recording").toUpperCase(), 30, 18 - (fontRenderer.FONT_HEIGHT / 2), 0xffffffff);
mc.renderEngine.bindTexture(TEXTURE); mc.renderEngine.bindTexture(TEXTURE);
GL11.glEnable(GL11.GL_ALPHA_TEST); GlStateManager.resetColor();
GlStateManager.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

@@ -2,7 +2,6 @@ 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.recording.Setting; import com.replaymod.recording.Setting;
import com.replaymod.recording.gui.GuiRecordingOverlay; import com.replaymod.recording.gui.GuiRecordingOverlay;
import com.replaymod.recording.packet.PacketListener; import com.replaymod.recording.packet.PacketListener;
@@ -10,10 +9,11 @@ import com.replaymod.replaystudio.replay.ReplayFile;
import com.replaymod.replaystudio.replay.ReplayMetaData; import com.replaymod.replaystudio.replay.ReplayMetaData;
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 cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.network.FMLNetworkEvent;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.network.NetworkManager; import net.minecraft.network.NetworkManager;
import net.minecraft.world.WorldType;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.network.FMLNetworkEvent.ClientDisconnectionFromServerEvent;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import java.io.File; import java.io.File;
@@ -46,6 +46,10 @@ public class ConnectionEventHandler {
try { try {
boolean local = networkManager.isLocalChannel(); boolean local = networkManager.isLocalChannel();
if (local) { if (local) {
if (mc.getIntegratedServer().getEntityWorld().getWorldType() == WorldType.DEBUG_WORLD) {
logger.info("Debug World recording is not supported.");
return;
}
if(!core.getSettingsRegistry().get(Setting.RECORD_SINGLEPLAYER)) { if(!core.getSettingsRegistry().get(Setting.RECORD_SINGLEPLAYER)) {
logger.info("Singleplayer Recording is disabled"); logger.info("Singleplayer Recording is disabled");
return; return;
@@ -70,7 +74,7 @@ public class ConnectionEventHandler {
File folder = core.getReplayFolder(); File folder = core.getReplayFolder();
String name = sdf.format(Calendar.getInstance().getTime()); String name = sdf.format(Calendar.getInstance().getTime());
File currentFile = new File(folder, Utils.replayNameToFileName(name)); File currentFile = new File(folder, name + ".mcpr");
ReplayFile replayFile = new ZipReplayFile(new ReplayStudio(), currentFile); ReplayFile replayFile = new ZipReplayFile(new ReplayStudio(), currentFile);
replayFile.writeModInfo(ModCompat.getInstalledNetworkMods()); replayFile.writeModInfo(ModCompat.getInstalledNetworkMods());
@@ -91,14 +95,14 @@ public class ConnectionEventHandler {
guiOverlay.register(); guiOverlay.register();
core.printInfoToChat("replaymod.chat.recordingstarted"); core.printInfoToChat("replaymod.chat.recordingstarted");
} catch(Throwable e) { } catch(Exception e) {
e.printStackTrace(); e.printStackTrace();
core.printWarningToChat("replaymod.chat.recordingfailed"); core.printWarningToChat("replaymod.chat.recordingfailed");
} }
} }
@SubscribeEvent @SubscribeEvent
public void onDisconnectedFromServerEvent(FMLNetworkEvent.ClientDisconnectionFromServerEvent event) { public void onDisconnectedFromServerEvent(ClientDisconnectionFromServerEvent event) {
if (packetListener != null) { if (packetListener != null) {
guiOverlay.unregister(); guiOverlay.unregister();
guiOverlay = null; guiOverlay = null;

View File

@@ -1,8 +1,8 @@
package com.replaymod.recording.handler; package com.replaymod.recording.handler;
import cpw.mods.fml.common.network.handshake.FMLHandshakeMessage;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.SimpleChannelInboundHandler;
import net.minecraftforge.fml.common.network.handshake.FMLHandshakeMessage;
/** /**
* Filters out all handshake packets that were sent for recording but must * Filters out all handshake packets that were sent for recording but must
@@ -17,7 +17,7 @@ import io.netty.channel.SimpleChannelInboundHandler;
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 (!(msg instanceof FMLHandshakeMessage.ModIdData)) { if (!(msg instanceof FMLHandshakeMessage.RegistryData)) {
// Pass on everything but RegistryData messages // Pass on everything but RegistryData messages
ctx.fireChannelRead(msg); ctx.fireChannelRead(msg);
} }

View File

@@ -1,34 +1,26 @@
package com.replaymod.recording.handler; package com.replaymod.recording.handler;
import com.replaymod.recording.packet.PacketListener; import com.replaymod.recording.packet.PacketListener;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.PlayerEvent;
import cpw.mods.fml.common.gameevent.TickEvent;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.EntityEquipmentSlot;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.network.Packet; import net.minecraft.network.Packet;
import net.minecraft.network.PacketBuffer; import net.minecraft.network.datasync.EntityDataManager;
import net.minecraft.network.play.server.S04PacketEntityEquipment; import net.minecraft.network.play.server.*;
import net.minecraft.network.play.server.S0APacketUseBed;
import net.minecraft.network.play.server.S0BPacketAnimation;
import net.minecraft.network.play.server.S0CPacketSpawnPlayer;
import net.minecraft.network.play.server.S0DPacketCollectItem;
import net.minecraft.network.play.server.S12PacketEntityVelocity;
import net.minecraft.network.play.server.S14PacketEntity.S17PacketEntityLookMove;
import net.minecraft.network.play.server.S18PacketEntityTeleport;
import net.minecraft.network.play.server.S19PacketEntityHeadLook;
import net.minecraft.network.play.server.S1BPacketEntityAttach;
import net.minecraft.network.play.server.S25PacketBlockBreakAnim;
import net.minecraft.server.integrated.IntegratedServer; import net.minecraft.server.integrated.IntegratedServer;
import net.minecraft.util.MathHelper; import net.minecraft.util.EnumHand;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.BlockPos;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.entity.minecart.MinecartInteractEvent; import net.minecraftforge.event.entity.minecart.MinecartInteractEvent;
import net.minecraftforge.event.entity.player.PlayerSleepInBedEvent; import net.minecraftforge.event.entity.player.PlayerSleepInBedEvent;
import net.minecraftforge.event.entity.player.PlayerUseItemEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.PlayerEvent.ItemPickupEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent.PlayerTickEvent;
import java.util.Objects; import java.util.Objects;
@@ -38,24 +30,24 @@ public class RecordingEventHandler {
private final PacketListener packetListener; private final PacketListener packetListener;
private Double lastX, lastY, lastZ; private Double lastX, lastY, lastZ;
private ItemStack[] playerItems = new ItemStack[5]; private ItemStack[] playerItems = new ItemStack[6];
private int ticksSinceLastCorrection; private int ticksSinceLastCorrection;
private boolean wasSleeping; private boolean wasSleeping;
private int lastRiding = -1; private int lastRiding = -1;
private Integer rotationYawHeadBefore; private Integer rotationYawHeadBefore;
private boolean wasHandActive;
private EnumHand lastActiveHand;
public RecordingEventHandler(PacketListener packetListener) { public RecordingEventHandler(PacketListener packetListener) {
this.packetListener = packetListener; this.packetListener = packetListener;
} }
public void register() { public void register() {
FMLCommonHandler.instance().bus().register(this);
MinecraftForge.EVENT_BUS.register(this); MinecraftForge.EVENT_BUS.register(this);
((RecordingEventSender) mc.renderGlobal).setRecordingEventHandler(this); ((RecordingEventSender) mc.renderGlobal).setRecordingEventHandler(this);
} }
public void unregister() { public void unregister() {
FMLCommonHandler.instance().bus().unregister(this);
MinecraftForge.EVENT_BUS.unregister(this); MinecraftForge.EVENT_BUS.unregister(this);
RecordingEventSender recordingEventSender = ((RecordingEventSender) mc.renderGlobal); RecordingEventSender recordingEventSender = ((RecordingEventSender) mc.renderGlobal);
if (recordingEventSender.getRecordingEventHandler() == this) { if (recordingEventSender.getRecordingEventHandler() == this) {
@@ -65,7 +57,7 @@ public class RecordingEventHandler {
public void onPlayerJoin() { public void onPlayerJoin() {
try { try {
packetListener.save(spawnPlayer(mc.thePlayer)); packetListener.save(new SPacketSpawnPlayer(mc.thePlayer));
} catch(Exception e1) { } catch(Exception e1) {
e1.printStackTrace(); e1.printStackTrace();
} }
@@ -73,18 +65,33 @@ public class RecordingEventHandler {
public void onPlayerRespawn() { public void onPlayerRespawn() {
try { try {
packetListener.save(spawnPlayer(mc.thePlayer)); packetListener.save(new SPacketSpawnPlayer(mc.thePlayer));
} catch(Exception e) { } catch(Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
private S0CPacketSpawnPlayer spawnPlayer(EntityPlayer player) { public void onClientSound(SoundEvent sound, SoundCategory category,
return new S0CPacketSpawnPlayer(player); double x, double y, double z, float volume, float pitch) {
try {
// Send to all other players in ServerWorldEventHandler#playSoundToAllNearExcept
packetListener.save(new SPacketSoundEffect(sound, category, x, y, z, volume, pitch));
} catch(Exception e) {
e.printStackTrace();
}
}
public void onClientEffect(int type, BlockPos pos, int data) {
try {
// Send to all other players in ServerWorldEventHandler#playEvent
packetListener.save(new SPacketEffect(type, pos, data, false));
} catch(Exception e) {
e.printStackTrace();
}
} }
@SubscribeEvent @SubscribeEvent
public void onPlayerTick(TickEvent.PlayerTickEvent e) { public void onPlayerTick(PlayerTickEvent e) {
try { try {
if(e.player != mc.thePlayer) return; if(e.player != mc.thePlayer) return;
@@ -92,7 +99,7 @@ public class RecordingEventHandler {
if(lastX == null || lastY == null || lastZ == null) { if(lastX == null || lastY == null || lastZ == null) {
force = true; force = true;
lastX = e.player.posX; lastX = e.player.posX;
lastY = e.player.boundingBox.minY; lastY = e.player.posY;
lastZ = e.player.posZ; lastZ = e.player.posZ;
} }
@@ -103,28 +110,23 @@ public class RecordingEventHandler {
} }
double dx = e.player.posX - lastX; double dx = e.player.posX - lastX;
double dy = e.player.boundingBox.minY - lastY; double dy = e.player.posY - lastY;
double dz = e.player.posZ - lastZ; double dz = e.player.posZ - lastZ;
lastX = e.player.posX; lastX = e.player.posX;
lastY = e.player.boundingBox.minY; lastY = e.player.posY;
lastZ = e.player.posZ; lastZ = e.player.posZ;
Packet packet; Packet packet;
if(force || Math.abs(dx) > 4.0 || Math.abs(dy) > 4.0 || Math.abs(dz) > 4.0) { if (force || Math.abs(dx) > 8.0 || Math.abs(dy) > 8.0 || Math.abs(dz) > 8.0) {
int x = MathHelper.floor_double(e.player.posX * 32.0D); packet = new SPacketEntityTeleport(e.player);
int y = MathHelper.floor_double(e.player.boundingBox.minY * 32.0D);
int z = MathHelper.floor_double(e.player.posZ * 32.0D);
byte yaw = (byte) ((int) (e.player.rotationYaw * 256.0F / 360.0F));
byte pitch = (byte) ((int) (e.player.rotationPitch * 256.0F / 360.0F));
packet = new S18PacketEntityTeleport(e.player.getEntityId(), x, y, z, yaw, pitch);
} else { } else {
byte newYaw = (byte) ((int) (e.player.rotationYaw * 256.0F / 360.0F)); byte newYaw = (byte) ((int) (e.player.rotationYaw * 256.0F / 360.0F));
byte newPitch = (byte) ((int) (e.player.rotationPitch * 256.0F / 360.0F)); byte newPitch = (byte) ((int) (e.player.rotationPitch * 256.0F / 360.0F));
packet = new S17PacketEntityLookMove(e.player.getEntityId(), packet = new SPacketEntity.S17PacketEntityLookMove(e.player.getEntityId(),
(byte) Math.round(dx * 32), (byte) Math.round(dy * 32), (byte) Math.round(dz * 32), (short) Math.round(dx * 4096), (short) Math.round(dy * 4096), (short) Math.round(dz * 4096),
newYaw, newPitch); newYaw, newPitch, e.player.onGround);
} }
packetListener.save(packet); packetListener.save(packet);
@@ -133,18 +135,16 @@ public class RecordingEventHandler {
int rotationYawHead = ((int)(e.player.rotationYawHead * 256.0F / 360.0F)); int rotationYawHead = ((int)(e.player.rotationYawHead * 256.0F / 360.0F));
if(!Objects.equals(rotationYawHead, rotationYawHeadBefore)) { if(!Objects.equals(rotationYawHead, rotationYawHeadBefore)) {
packetListener.save(new S19PacketEntityHeadLook(e.player, (byte) rotationYawHead)); packetListener.save(new SPacketEntityHeadLook(e.player, (byte) rotationYawHead));
rotationYawHeadBefore = rotationYawHead; rotationYawHeadBefore = rotationYawHead;
} }
S12PacketEntityVelocity vel = new S12PacketEntityVelocity(e.player.getEntityId(), e.player.motionX, e.player.motionY, e.player.motionZ); packetListener.save(new SPacketEntityVelocity(e.player.getEntityId(), e.player.motionX, e.player.motionY, e.player.motionZ));
packetListener.save(vel);
//Animation Packets //Animation Packets
//Swing Animation //Swing Animation
if(e.player.swingProgressInt == 1) { if (e.player.isSwingInProgress && e.player.swingProgressInt == -1) {
packetListener.save(new S0BPacketAnimation(e.player, 0)); packetListener.save(new SPacketAnimation(e.player, e.player.swingingHand == EnumHand.MAIN_HAND ? 0 : 3));
} }
/* /*
@@ -168,110 +168,113 @@ public class RecordingEventHandler {
*/ */
//Inventory Handling //Inventory Handling
if(playerItems[0] != mc.thePlayer.getHeldItem()) { if (playerItems[0] != mc.thePlayer.getHeldItem(EnumHand.MAIN_HAND)) {
playerItems[0] = mc.thePlayer.getHeldItem(); playerItems[0] = mc.thePlayer.getHeldItem(EnumHand.MAIN_HAND);
S04PacketEntityEquipment pee = new S04PacketEntityEquipment(e.player.getEntityId(), 0, playerItems[0]); packetListener.save(new SPacketEntityEquipment(e.player.getEntityId(), EntityEquipmentSlot.MAINHAND, playerItems[0]));
packetListener.save(pee);
} }
if(playerItems[1] != mc.thePlayer.inventory.armorInventory[0]) { if (playerItems[1] != mc.thePlayer.getHeldItem(EnumHand.OFF_HAND)) {
playerItems[1] = mc.thePlayer.inventory.armorInventory[0]; playerItems[1] = mc.thePlayer.getHeldItem(EnumHand.OFF_HAND);
S04PacketEntityEquipment pee = new S04PacketEntityEquipment(e.player.getEntityId(), 1, playerItems[1]); packetListener.save(new SPacketEntityEquipment(e.player.getEntityId(), EntityEquipmentSlot.OFFHAND, playerItems[1]));
packetListener.save(pee);
} }
if(playerItems[2] != mc.thePlayer.inventory.armorInventory[1]) { if (playerItems[2] != mc.thePlayer.inventory.armorInventory[0]) {
playerItems[2] = mc.thePlayer.inventory.armorInventory[1]; playerItems[2] = mc.thePlayer.inventory.armorInventory[0];
S04PacketEntityEquipment pee = new S04PacketEntityEquipment(e.player.getEntityId(), 2, playerItems[2]); packetListener.save(new SPacketEntityEquipment(e.player.getEntityId(), EntityEquipmentSlot.FEET, playerItems[2]));
packetListener.save(pee);
} }
if(playerItems[3] != mc.thePlayer.inventory.armorInventory[2]) { if (playerItems[3] != mc.thePlayer.inventory.armorInventory[1]) {
playerItems[3] = mc.thePlayer.inventory.armorInventory[2]; playerItems[3] = mc.thePlayer.inventory.armorInventory[1];
S04PacketEntityEquipment pee = new S04PacketEntityEquipment(e.player.getEntityId(), 3, playerItems[3]); packetListener.save(new SPacketEntityEquipment(e.player.getEntityId(), EntityEquipmentSlot.LEGS, playerItems[3]));
packetListener.save(pee);
} }
if(playerItems[4] != mc.thePlayer.inventory.armorInventory[3]) { if (playerItems[4] != mc.thePlayer.inventory.armorInventory[2]) {
playerItems[4] = mc.thePlayer.inventory.armorInventory[3]; playerItems[4] = mc.thePlayer.inventory.armorInventory[2];
S04PacketEntityEquipment pee = new S04PacketEntityEquipment(e.player.getEntityId(), 4, playerItems[4]); packetListener.save(new SPacketEntityEquipment(e.player.getEntityId(), EntityEquipmentSlot.CHEST, playerItems[4]));
packetListener.save(pee); }
if (playerItems[5] != mc.thePlayer.inventory.armorInventory[3]) {
playerItems[5] = mc.thePlayer.inventory.armorInventory[3];
packetListener.save(new SPacketEntityEquipment(e.player.getEntityId(), EntityEquipmentSlot.HEAD, playerItems[5]));
} }
//Leaving Ride //Leaving Ride
if((!mc.thePlayer.isRiding() && lastRiding != -1) || if((!mc.thePlayer.isRiding() && lastRiding != -1) ||
(mc.thePlayer.isRiding() && lastRiding != mc.thePlayer.ridingEntity.getEntityId())) { (mc.thePlayer.isRiding() && lastRiding != mc.thePlayer.getRidingEntity().getEntityId())) {
if(!mc.thePlayer.isRiding()) { if(!mc.thePlayer.isRiding()) {
lastRiding = -1; lastRiding = -1;
} else { } else {
lastRiding = mc.thePlayer.ridingEntity.getEntityId(); lastRiding = mc.thePlayer.getRidingEntity().getEntityId();
} }
packetListener.save(new SPacketEntityAttach(e.player, e.player.getRidingEntity()));
S1BPacketEntityAttach pea = new S1BPacketEntityAttach();
ByteBuf buf = Unpooled.buffer();
PacketBuffer pbuf = new PacketBuffer(buf);
pbuf.writeInt(e.player.getEntityId());
pbuf.writeInt(lastRiding);
pbuf.writeBoolean(false);
pea.readPacketData(pbuf);
packetListener.save(pea);
} }
//Sleeping //Sleeping
if(!mc.thePlayer.isPlayerSleeping() && wasSleeping) { if(!mc.thePlayer.isPlayerSleeping() && wasSleeping) {
packetListener.save(new S0BPacketAnimation(e.player, 2)); packetListener.save(new SPacketAnimation(e.player, 2));
wasSleeping = false; wasSleeping = false;
} }
// Active hand (e.g. eating, drinking, blocking)
if (mc.thePlayer.isHandActive() ^ wasHandActive || mc.thePlayer.getActiveHand() != lastActiveHand) {
wasHandActive = mc.thePlayer.isHandActive();
lastActiveHand = mc.thePlayer.getActiveHand();
EntityDataManager dataManager = new EntityDataManager(null);
int state = (wasHandActive ? 1 : 0) | (lastActiveHand == EnumHand.OFF_HAND ? 2 : 0);
dataManager.register(EntityLiving.HAND_STATES, (byte) state);
packetListener.save(new SPacketEntityMetadata(mc.thePlayer.getEntityId(), dataManager, true));
}
} catch(Exception e1) { } catch(Exception e1) {
e1.printStackTrace(); e1.printStackTrace();
} }
} }
@SubscribeEvent @SubscribeEvent
public void onPickupItem(PlayerEvent.ItemPickupEvent event) { public void onPickupItem(ItemPickupEvent event) {
try { try {
packetListener.save(new S0DPacketCollectItem(event.pickedUp.getEntityId(), event.player.getEntityId())); packetListener.save(new SPacketCollectItem(event.pickedUp.getEntityId(), event.player.getEntityId()));
} catch(Exception e) { } catch(Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
@SubscribeEvent
public void onStartEating(PlayerUseItemEvent.Start event) {
if(!event.entityPlayer.isEating()) return;
packetListener.save(new S0BPacketAnimation(event.entityPlayer, 3));
}
@SubscribeEvent @SubscribeEvent
public void onSleep(PlayerSleepInBedEvent event) { public void onSleep(PlayerSleepInBedEvent event) {
if(event.entityPlayer != mc.thePlayer) { try {
return; if (event.getEntityPlayer() != mc.thePlayer) {
} return;
}
packetListener.save(new S0APacketUseBed(event.entityPlayer, event.x, event.y, event.z)); packetListener.save(new SPacketUseBed(event.getEntityPlayer(), event.getPos()));
wasSleeping = true;
wasSleeping = true;
} catch(Exception e) {
e.printStackTrace();
}
} }
@SubscribeEvent @SubscribeEvent
public void enterMinecart(MinecartInteractEvent event) { public void enterMinecart(MinecartInteractEvent event) {
if(event.player != mc.thePlayer) { try {
return; if(event.getEntity() != mc.thePlayer) {
return;
}
packetListener.save(new SPacketEntityAttach(event.getPlayer(), event.getMinecart()));
lastRiding = event.getMinecart().getEntityId();
} catch(Exception e) {
e.printStackTrace();
} }
packetListener.save(new S1BPacketEntityAttach(0, event.player, event.minecart));
lastRiding = event.minecart.getEntityId();
} }
public void onBlockBreakAnim(int breakerId, int x, int y, int z, int progress) { public void onBlockBreakAnim(int breakerId, BlockPos pos, int progress) {
EntityPlayer thePlayer = mc.thePlayer; EntityPlayer thePlayer = mc.thePlayer;
if (thePlayer != null && breakerId == thePlayer.getEntityId()) { if (thePlayer != null && breakerId == thePlayer.getEntityId()) {
packetListener.save(new S25PacketBlockBreakAnim(breakerId, x, y, z, progress)); packetListener.save(new SPacketBlockBreakAnim(breakerId, pos, progress));
} }
} }

View File

@@ -1,10 +1,8 @@
package com.replaymod.recording.mixin; package com.replaymod.recording.mixin;
import com.replaymod.recording.ReplayModRecording; import com.replaymod.recording.ReplayModRecording;
import cpw.mods.fml.common.network.FMLNetworkEvent;
import net.minecraft.client.network.NetHandlerLoginClient; import net.minecraft.client.network.NetHandlerLoginClient;
import net.minecraft.network.NetworkManager; import net.minecraft.network.NetworkManager;
import org.spongepowered.asm.mixin.Final;
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;
@@ -15,15 +13,15 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
public abstract class MixinNetHandlerLoginClient { public abstract class MixinNetHandlerLoginClient {
@Shadow @Shadow
private @Final NetworkManager field_147393_d; private NetworkManager networkManager;
/** /**
* Starts the recording right before switching into PLAY state. * Starts the recording right before switching into PLAY state.
* We cannot use the {@link FMLNetworkEvent.ClientConnectedToServerEvent} * We cannot use the {@link net.minecraftforge.fml.common.network.FMLNetworkEvent.ClientConnectedToServerEvent}
* as it only fires after the forge handshake. * as it only fires after the forge handshake.
*/ */
@Inject(method = "handleLoginSuccess", at=@At("HEAD")) @Inject(method = "handleLoginSuccess", at=@At("HEAD"))
public void replayModRecording_initiateRecording(CallbackInfo cb) { public void replayModRecording_initiateRecording(CallbackInfo cb) {
ReplayModRecording.instance.initiateRecording(field_147393_d); ReplayModRecording.instance.initiateRecording(networkManager);
} }
} }

View File

@@ -3,20 +3,27 @@ package com.replaymod.recording.mixin;
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;
import net.minecraft.network.play.server.S01PacketJoinGame; import net.minecraft.client.network.NetworkPlayerInfo;
import net.minecraft.network.play.server.S07PacketRespawn; import net.minecraft.network.play.server.SPacketPlayerListItem;
import net.minecraft.network.play.server.SPacketRespawn;
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.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Map;
import java.util.UUID;
@Mixin(NetHandlerPlayClient.class) @Mixin(NetHandlerPlayClient.class)
public abstract class MixinNetHandlerPlayClient { public abstract class MixinNetHandlerPlayClient {
@Shadow @Shadow
private Minecraft gameController; private Minecraft gameController;
@Shadow
private Map<UUID, NetworkPlayerInfo> playerInfoMap;
public RecordingEventHandler getRecordingEventHandler() { public RecordingEventHandler getRecordingEventHandler() {
return ((RecordingEventHandler.RecordingEventSender) gameController.renderGlobal).getRecordingEventHandler(); return ((RecordingEventHandler.RecordingEventSender) gameController.renderGlobal).getRecordingEventHandler();
} }
@@ -24,27 +31,36 @@ public abstract class MixinNetHandlerPlayClient {
/** /**
* Record the own player entity joining the world. * Record the own player entity joining the world.
* We cannot use the {@link net.minecraftforge.event.entity.EntityJoinWorldEvent} because the entity id * We cannot use the {@link net.minecraftforge.event.entity.EntityJoinWorldEvent} because the entity id
* of the player is set afterwards * of the player is set afterwards and the tablist entry might not yet be sent.
* @param packet The packet * @param packet The packet
* @param ci Callback info * @param ci Callback info
*/ */
@Inject(method = "handleJoinGame", at=@At("RETURN")) @Inject(method = "handlePlayerListItem", at=@At("HEAD"))
public void recordOwnJoin(S01PacketJoinGame packet, CallbackInfo ci) { public void recordOwnJoin(SPacketPlayerListItem packet, CallbackInfo ci) {
if (gameController.thePlayer == null) return;
RecordingEventHandler handler = getRecordingEventHandler(); RecordingEventHandler handler = getRecordingEventHandler();
if (handler != null) { if (handler != null && packet.getAction() == SPacketPlayerListItem.Action.ADD_PLAYER) {
handler.onPlayerJoin(); for (SPacketPlayerListItem.AddPlayerData data : packet.getEntries()) {
if (data.getProfile() == null || data.getProfile().getId() == null) continue;
// Only add spawn packet for our own player and only if he isn't known yet
if (data.getProfile().getId().equals(Minecraft.getMinecraft().thePlayer.getGameProfile().getId())
&& !playerInfoMap.containsKey(data.getProfile().getId())) {
handler.onPlayerJoin();
}
}
} }
} }
/** /**
* Record the own player entity respawning. * Record the own player entity respawning.
* We cannot use the {@link net.minecraftforge.event.entity.EntityJoinWorldEvent} because that would also include * We cannot use the {@link net.minecraftforge.event.entity.EntityJoinWorldEvent} because that would also include
* the first spawn which is already handled by {@link #recordOwnJoin(S01PacketJoinGame, CallbackInfo)}. * the first spawn which is already handled by {@link #recordOwnJoin(SPacketPlayerListItem, CallbackInfo)}.
* @param packet The packet * @param packet The packet
* @param ci Callback info * @param ci Callback info
*/ */
@Inject(method = "handleRespawn", at=@At("RETURN")) @Inject(method = "handleRespawn", at=@At("RETURN"))
public void recordOwnRespawn(S07PacketRespawn packet, CallbackInfo ci) { public void recordOwnRespawn(SPacketRespawn packet, CallbackInfo ci) {
RecordingEventHandler handler = getRecordingEventHandler(); RecordingEventHandler handler = getRecordingEventHandler();
if (handler != null) { if (handler != null) {
handler.onPlayerRespawn(); handler.onPlayerRespawn();

View File

@@ -1,11 +1,11 @@
package com.replaymod.recording.mixin; package com.replaymod.recording.mixin;
import com.replaymod.recording.handler.FMLHandshakeFilter; import com.replaymod.recording.handler.FMLHandshakeFilter;
import cpw.mods.fml.common.network.handshake.FMLHandshakeCodec;
import cpw.mods.fml.common.network.handshake.NetworkDispatcher;
import cpw.mods.fml.relauncher.Side;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.embedded.EmbeddedChannel; import io.netty.channel.embedded.EmbeddedChannel;
import net.minecraftforge.fml.common.network.handshake.FMLHandshakeCodec;
import net.minecraftforge.fml.common.network.handshake.NetworkDispatcher;
import net.minecraftforge.fml.relauncher.Side;
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;

View File

@@ -0,0 +1,28 @@
package com.replaymod.recording.mixin;
import com.replaymod.recording.handler.RecordingEventHandler;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.PlayerControllerMP;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
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;
@Mixin(PlayerControllerMP.class)
public abstract class MixinPlayerControllerMP implements RecordingEventHandler.RecordingEventSender {
@Shadow
private Minecraft mc;
// 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)
// 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).
@Redirect(method = "onPlayerDestroyBlock", at = @At(value = "INVOKE",
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) {
world.playEvent(mc.thePlayer, type, pos, data);
}
}

View File

@@ -2,6 +2,7 @@ package com.replaymod.recording.mixin;
import com.replaymod.recording.handler.RecordingEventHandler; import com.replaymod.recording.handler.RecordingEventHandler;
import net.minecraft.client.renderer.RenderGlobal; import net.minecraft.client.renderer.RenderGlobal;
import net.minecraft.util.math.BlockPos;
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;
@@ -22,10 +23,10 @@ public abstract class MixinRenderGlobal implements RecordingEventHandler.Recordi
return recordingEventHandler; return recordingEventHandler;
} }
@Inject(method = "destroyBlockPartially", at = @At("HEAD")) @Inject(method = "sendBlockBreakProgress", at = @At("HEAD"))
public void saveBlockBreakProgressPacket(int breakerId, int x, int y, int z, int progress, CallbackInfo info) { public void saveBlockBreakProgressPacket(int breakerId, BlockPos pos, int progress, CallbackInfo info) {
if (recordingEventHandler != null) { if (recordingEventHandler != null) {
recordingEventHandler.onBlockBreakAnim(breakerId, x, y, z, progress); recordingEventHandler.onBlockBreakAnim(breakerId, pos, progress);
} }
} }
} }

View File

@@ -0,0 +1,62 @@
package com.replaymod.recording.mixin;
import com.replaymod.recording.handler.RecordingEventHandler;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.profiler.Profiler;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.WorldProvider;
import net.minecraft.world.storage.ISaveHandler;
import net.minecraft.world.storage.WorldInfo;
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;
@Mixin(WorldClient.class)
public abstract class MixinWorldClient extends World implements RecordingEventHandler.RecordingEventSender {
@Shadow
private Minecraft mc;
protected MixinWorldClient(ISaveHandler saveHandlerIn, WorldInfo info, WorldProvider providerIn, Profiler profilerIn, boolean client) {
super(saveHandlerIn, info, providerIn, profilerIn, client);
}
private RecordingEventHandler replayModRecording_getRecordingEventHandler() {
return ((RecordingEventHandler.RecordingEventSender) mc.renderGlobal).getRecordingEventHandler();
}
// Sounds that are emitted by thePlayer no longer take the long way over the server
// but are instead played directly by the client. The server only sends these sounds to
// other clients so we have to record them manually.
// E.g. Block place sounds
@Inject(method = "playSound", at = @At("HEAD"))
public void replayModRecording_recordClientSound(EntityPlayer player, double x, double y, double z, SoundEvent sound, SoundCategory category,
float volume, float pitch, CallbackInfo ci) {
if (player == mc.thePlayer) {
RecordingEventHandler handler = replayModRecording_getRecordingEventHandler();
if (handler != null) {
handler.onClientSound(sound, category, x, y, z, volume, pitch);
}
}
}
// Same goes for level events (also called effects). E.g. door open, block break, etc.
// These are handled in the World class, so we override the method in WorldClient and add our special handling.
@Override
public void playEvent(EntityPlayer player, int type, BlockPos pos, int data) {
if (player == mc.thePlayer) {
// We caused this event, the server won't send it to us
RecordingEventHandler handler = replayModRecording_getRecordingEventHandler();
if (handler != null) {
handler.onClientEffect(type, pos, data);
}
}
super.playEvent(player, type, pos, data);
}
}

View File

@@ -4,25 +4,29 @@ import com.replaymod.core.utils.Restrictions;
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 cpw.mods.fml.common.network.internal.FMLProxyPacket;
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;
import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInboundHandlerAdapter;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.entity.DataWatcher;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.network.EnumConnectionState; import net.minecraft.network.EnumConnectionState;
import net.minecraft.network.EnumPacketDirection;
import net.minecraft.network.Packet; import net.minecraft.network.Packet;
import net.minecraft.network.PacketBuffer; import net.minecraft.network.PacketBuffer;
import net.minecraft.network.datasync.EntityDataManager;
import net.minecraft.network.play.server.*; import net.minecraft.network.play.server.*;
import net.minecraft.util.ChatComponentText; import net.minecraft.util.text.TextComponentString;
import net.minecraftforge.fml.common.network.internal.FMLProxyPacket;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@@ -83,8 +87,8 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
public void save(Packet packet) { public void save(Packet packet) {
try { try {
if(packet instanceof S0CPacketSpawnPlayer) { if(packet instanceof SPacketSpawnPlayer) {
UUID uuid = ((S0CPacketSpawnPlayer) packet).field_148955_b.getId(); UUID uuid = ((SPacketSpawnPlayer) packet).getUniqueId();
Set<String> uuids = new HashSet<>(Arrays.asList(metaData.getPlayers())); Set<String> uuids = new HashSet<>(Arrays.asList(metaData.getPlayers()));
uuids.add(uuid.toString()); uuids.add(uuid.toString());
metaData.setPlayers(uuids.toArray(new String[uuids.size()])); metaData.setPlayers(uuids.toArray(new String[uuids.size()]));
@@ -150,35 +154,32 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
try { try {
Packet packet = (Packet) msg; Packet packet = (Packet) msg;
if(packet instanceof S0DPacketCollectItem) { if(packet instanceof SPacketCollectItem) {
if(mc.thePlayer != null || if(mc.thePlayer != null ||
((S0DPacketCollectItem) packet).func_149353_d() == mc.thePlayer.getEntityId()) { ((SPacketCollectItem) packet).getEntityID() == mc.thePlayer.getEntityId()) {
super.channelRead(ctx, msg); super.channelRead(ctx, msg);
return; return;
} }
} }
if (packet instanceof S3FPacketCustomPayload) { if (packet instanceof SPacketResourcePackSend) {
S3FPacketCustomPayload p = (S3FPacketCustomPayload) packet; save(resourcePackRecorder.handleResourcePack((SPacketResourcePackSend) packet));
if ("MC|RPack".equals(p.func_149169_c())) { return;
save(resourcePackRecorder.handleResourcePack(p));
return;
}
} }
if (packet instanceof FMLProxyPacket) { if (packet instanceof FMLProxyPacket) {
// This packet requires special handling // This packet requires special handling
save(((FMLProxyPacket) packet).toS3FPacket()); ((FMLProxyPacket) packet).toS3FPackets().forEach(this::save);
super.channelRead(ctx, msg); super.channelRead(ctx, msg);
return; return;
} }
save(packet); save(packet);
if (packet instanceof S3FPacketCustomPayload) { if (packet instanceof SPacketCustomPayload) {
S3FPacketCustomPayload p = (S3FPacketCustomPayload) packet; SPacketCustomPayload p = (SPacketCustomPayload) packet;
if (Restrictions.PLUGIN_CHANNEL.equals(p.func_149169_c())) { if (Restrictions.PLUGIN_CHANNEL.equals(p.getChannelName())) {
packet = new S40PacketDisconnect(new ChatComponentText("Please update to view this replay.")); packet = new SPacketDisconnect(new TextComponentString("Please update to view this replay."));
save(packet); save(packet);
} }
} }
@@ -191,33 +192,37 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
super.channelRead(ctx, msg); super.channelRead(ctx, msg);
} }
private <T> void DataManager_set(EntityDataManager dataManager, EntityDataManager.DataEntry<T> entry) {
dataManager.register(entry.getKey(), entry.getValue());
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private byte[] getPacketData(Packet packet) throws IOException { private byte[] getPacketData(Packet packet) throws IOException {
if(packet instanceof S0FPacketSpawnMob) { if (packet instanceof SPacketSpawnMob) {
S0FPacketSpawnMob p = (S0FPacketSpawnMob) packet; SPacketSpawnMob p = (SPacketSpawnMob) packet;
if (p.field_149043_l == null) { if (p.dataManager == null) {
p.field_149043_l = new DataWatcher(null); p.dataManager = new EntityDataManager(null);
if(p.func_149027_c() != null) { if (p.getDataManagerEntries() != null) {
for(DataWatcher.WatchableObject wo : (List<DataWatcher.WatchableObject>) p.func_149027_c()) { for (EntityDataManager.DataEntry<?> entry : p.getDataManagerEntries()) {
p.field_149043_l.addObject(wo.getDataValueId(), wo.getObject()); DataManager_set(p.dataManager, entry);
} }
} }
} }
} }
if(packet instanceof S0CPacketSpawnPlayer) { if (packet instanceof SPacketSpawnPlayer) {
S0CPacketSpawnPlayer p = (S0CPacketSpawnPlayer) packet; SPacketSpawnPlayer p = (SPacketSpawnPlayer) packet;
if (p.field_148960_i == null) { if (p.watcher == null) {
p.field_148960_i = new DataWatcher(null); p.watcher = new EntityDataManager(null);
if(p.func_148944_c() != null) { if (p.getDataManagerEntries() != null) {
for(DataWatcher.WatchableObject wo : (List<DataWatcher.WatchableObject>) p.func_148944_c()) { for (EntityDataManager.DataEntry<?> entry : p.getDataManagerEntries()) {
p.field_148960_i.addObject(wo.getDataValueId(), wo.getObject()); DataManager_set(p.watcher, entry);
} }
} }
} }
} }
Integer packetId = (Integer) EnumConnectionState.PLAY.func_150755_b().inverse().get(packet.getClass()); Integer packetId = EnumConnectionState.PLAY.getPacketId(EnumPacketDirection.CLIENTBOUND, packet);
if (packetId == null) { if (packetId == null) {
throw new IOException("Unknown packet type:" + packet.getClass()); throw new IOException("Unknown packet type:" + packet.getClass());
} }
@@ -235,7 +240,7 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
} }
public void addMarker() { public void addMarker() {
Entity view = Minecraft.getMinecraft().renderViewEntity; Entity view = Minecraft.getMinecraft().getRenderViewEntity();
int timestamp = (int) (System.currentTimeMillis() - startTime); int timestamp = (int) (System.currentTimeMillis() - startTime);
Marker marker = new Marker(); Marker marker = new Marker();

View File

@@ -2,23 +2,27 @@ package com.replaymod.recording.packet;
import com.google.common.hash.Hashing; import com.google.common.hash.Hashing;
import com.google.common.io.Files; import com.google.common.io.Files;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
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.GuiScreenWorking;
import net.minecraft.client.gui.GuiYesNo; import net.minecraft.client.gui.GuiYesNo;
import net.minecraft.client.gui.GuiYesNoCallback;
import net.minecraft.client.multiplayer.ServerData; import net.minecraft.client.multiplayer.ServerData;
import net.minecraft.client.multiplayer.ServerData.ServerResourceMode;
import net.minecraft.client.multiplayer.ServerList; import net.minecraft.client.multiplayer.ServerList;
import net.minecraft.client.resources.FileResourcePack; import net.minecraft.client.network.NetHandlerPlayClient;
import net.minecraft.client.resources.I18n; import net.minecraft.client.resources.I18n;
import net.minecraft.client.resources.ResourcePackRepository; import net.minecraft.client.resources.ResourcePackRepository;
import net.minecraft.network.play.server.S3FPacketCustomPayload; import net.minecraft.network.NetworkManager;
import net.minecraft.network.play.client.CPacketResourcePackStatus;
import net.minecraft.network.play.server.SPacketResourcePackSend;
import net.minecraft.util.HttpUtil; import net.minecraft.util.HttpUtil;
import org.apache.commons.io.Charsets; import org.apache.commons.io.FileUtils;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import javax.annotation.Nonnull;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
@@ -70,75 +74,140 @@ public class ResourcePackRecorder {
} }
} }
public synchronized S3FPacketCustomPayload handleResourcePack(S3FPacketCustomPayload packet) { public synchronized SPacketResourcePackSend handleResourcePack(SPacketResourcePackSend packet) {
final int requestId = nextRequestId++; final int requestId = nextRequestId++;
final String url = new String(packet.func_149168_d(), Charsets.UTF_8); final NetHandlerPlayClient netHandler = mc.getConnection();
final NetworkManager netManager = netHandler.getNetworkManager();
final String url = packet.getURL();
final String hash = packet.getHash();
ServerData serverData = mc.getCurrentServerData(); if (url.startsWith("level://")) {
ServerResourceMode resourceMode = serverData == null ? ServerResourceMode.PROMPT : serverData.getResourceMode(); String levelName = url.substring("level://".length());
if (resourceMode == ServerResourceMode.ENABLED) { File savesDir = new File(mc.mcDataDir, "saves");
downloadResourcePack(requestId, url); final File levelDir = new File(savesDir, levelName);
mc.getResourcePackRepository().obtainResourcePack(url);
} else if (resourceMode == ServerResourceMode.PROMPT) { if (levelDir.isFile()) {
// Lambdas MUST NOT be used with methods that need re-obfuscation in FG prior to 2.2 (will result in AbstractMethodError) netManager.sendPacket(new CPacketResourcePackStatus(hash, CPacketResourcePackStatus.Action.ACCEPTED));
//noinspection Convert2Lambda Futures.addCallback(mc.getResourcePackRepository().setResourcePackInstance(levelDir), new FutureCallback<Object>() {
mc.displayGuiScreen(new GuiYesNo(new GuiYesNoCallback() { @Override
@Override public void onSuccess(Object result) {
public void confirmClicked(boolean result, int id) { recordResourcePack(levelDir, requestId);
netManager.sendPacket(new CPacketResourcePackStatus(hash, CPacketResourcePackStatus.Action.SUCCESSFULLY_LOADED));
}
@Override
public void onFailure(@Nonnull Throwable throwable) {
netManager.sendPacket(new CPacketResourcePackStatus(hash, CPacketResourcePackStatus.Action.FAILED_DOWNLOAD));
}
});
} else {
netManager.sendPacket(new CPacketResourcePackStatus(hash, CPacketResourcePackStatus.Action.FAILED_DOWNLOAD));
}
} else {
final ServerData serverData = mc.getCurrentServerData();
if (serverData != null && serverData.getResourceMode() == ServerData.ServerResourceMode.ENABLED) {
netManager.sendPacket(new CPacketResourcePackStatus(hash, CPacketResourcePackStatus.Action.ACCEPTED));
downloadResourcePackFuture(requestId, url, hash);
} else if (serverData != null && serverData.getResourceMode() != ServerData.ServerResourceMode.PROMPT) {
netManager.sendPacket(new CPacketResourcePackStatus(hash, CPacketResourcePackStatus.Action.DECLINED));
} else {
mc.addScheduledTask(() -> mc.displayGuiScreen(new GuiYesNo((result, id) -> {
if (serverData != null) { if (serverData != null) {
serverData.setResourceMode(ServerResourceMode.ENABLED); serverData.setResourceMode(result ? ServerData.ServerResourceMode.ENABLED : ServerData.ServerResourceMode.DISABLED);
ServerList.func_147414_b(serverData);
} }
mc.displayGuiScreen(null);
if (result) { if (result) {
downloadResourcePack(requestId, url); netManager.sendPacket(new CPacketResourcePackStatus(hash, CPacketResourcePackStatus.Action.ACCEPTED));
downloadResourcePackFuture(requestId, url, hash);
} else {
netManager.sendPacket(new CPacketResourcePackStatus(hash, CPacketResourcePackStatus.Action.DECLINED));
} }
}
}, I18n.format("multiplayer.texturePrompt.line1"), I18n.format("multiplayer.texturePrompt.line2"), 0)); ServerList.saveSingleServer(serverData);
mc.displayGuiScreen(null);
}, I18n.format("multiplayer.texturePrompt.line1"), I18n.format("multiplayer.texturePrompt.line2"), 0)));
}
} }
return new S3FPacketCustomPayload(packet.func_149169_c(), ("replay://" + requestId).getBytes(Charsets.UTF_8)); return new SPacketResourcePackSend("replay://" + requestId, "");
} }
private void downloadResourcePack(final int requestId, String url) { private void downloadResourcePackFuture(int requestId, String url, final String hash) {
Futures.addCallback(downloadResourcePack(requestId, url, hash), new FutureCallback() {
@Override
public void onSuccess(Object result) {
mc.getConnection().sendPacket(new CPacketResourcePackStatus(hash, CPacketResourcePackStatus.Action.SUCCESSFULLY_LOADED));
}
@Override
public void onFailure(@Nonnull Throwable throwable) {
mc.getConnection().sendPacket(new CPacketResourcePackStatus(hash, CPacketResourcePackStatus.Action.FAILED_DOWNLOAD));
}
});
}
private ListenableFuture downloadResourcePack(final int requestId, String url, String hash) {
final ResourcePackRepository repo = mc.mcResourcePackRepository; final ResourcePackRepository repo = mc.mcResourcePackRepository;
String fileName;
if (hash.matches("^[a-f0-9]{40}$")) {
fileName = hash;
} else {
fileName = url.substring(url.lastIndexOf("/") + 1);
String fileName = url.substring(url.lastIndexOf("/") + 1); if (fileName.contains("?")) {
fileName = fileName.substring(0, fileName.indexOf("?"));
}
if (fileName.contains("?")) { if (!fileName.endsWith(".zip")) {
fileName = fileName.substring(0, fileName.indexOf("?")); return Futures.immediateFailedFuture(new IllegalArgumentException("Invalid filename; must end in .zip"));
}
fileName = "legacy_" + fileName.replaceAll("\\W", "");
} }
if (!fileName.endsWith(".zip")) { final File file = new File(repo.dirServerResourcepacks, fileName);
return; repo.lock.lock();
} try {
repo.clearResourcePack();
fileName = fileName.replaceAll("\\W", ""); if (file.exists() && hash.length() == 40) {
try {
String fileHash = Hashing.sha1().hashBytes(Files.toByteArray(file)).toString();
if (fileHash.equals(hash)) {
recordResourcePack(file, requestId);
return repo.setResourcePackInstance(file);
}
File file = new File(repo.field_148534_e, fileName); logger.warn("File " + file + " had wrong hash (expected " + hash + ", found " + fileHash + "). Deleting it.");
FileUtils.deleteQuietly(file);
HashMap<String, String> hashmap = new HashMap<>(); } catch (IOException ioexception) {
hashmap.put("X-Minecraft-Username", mc.getSession().getUsername()); logger.warn("File " + file + " couldn\'t be hashed. Deleting it.", ioexception);
hashmap.put("X-Minecraft-UUID", mc.getSession().getPlayerID()); FileUtils.deleteQuietly(file);
hashmap.put("X-Minecraft-Version", "1.7.10");
GuiScreenWorking guiScreen = new GuiScreenWorking();
Minecraft.getMinecraft().displayGuiScreen(guiScreen);
repo.func_148529_f();
repo.field_148533_g = true;
// Lambdas MUST NOT be used with methods that need re-obfuscation in FG prior to 2.2 (will result in AbstractMethodError)
//noinspection Convert2Lambda
HttpUtil.downloadResourcePack(file, url, new HttpUtil.DownloadListener() {
public void onDownloadComplete(File file) {
if (repo.field_148533_g) {
repo.field_148533_g = false;
repo.field_148532_f = new FileResourcePack(file);
Minecraft.getMinecraft().scheduleResourcesRefresh();
recordResourcePack(file, requestId);
} }
} }
}, hashmap, 50*1024*1024, guiScreen, Minecraft.getMinecraft().getProxy());
final GuiScreenWorking guiScreen = new GuiScreenWorking();
final Minecraft mc = Minecraft.getMinecraft();
Futures.getUnchecked(mc.addScheduledTask(() -> mc.displayGuiScreen(guiScreen)));
Map<String, String> sessionInfo = Minecraft.getSessionInfo();
repo.downloadingPacks = HttpUtil.downloadResourcePack(file, url, sessionInfo, 50 * 1024 * 1024, guiScreen, mc.getProxy());
Futures.addCallback(repo.downloadingPacks, new FutureCallback<Object>() {
@Override
public void onSuccess(Object value) {
recordResourcePack(file, requestId);
repo.setResourcePackInstance(file);
}
@Override
public void onFailure(@Nonnull Throwable throwable) {
throwable.printStackTrace();
}
});
return repo.downloadingPacks;
} finally {
repo.lock.unlock();
}
} }
} }

View File

@@ -3,13 +3,13 @@ package com.replaymod.render;
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 cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReport;
import net.minecraft.util.ReportedException; import net.minecraft.util.ReportedException;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.config.Configuration; import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
@@ -21,7 +21,6 @@ import java.util.List;
@Mod(modid = ReplayModRender.MOD_ID, @Mod(modid = ReplayModRender.MOD_ID,
version = "@MOD_VERSION@", version = "@MOD_VERSION@",
acceptedMinecraftVersions = "@MC_VERSION@", acceptedMinecraftVersions = "@MC_VERSION@",
acceptableRemoteVersions = "*",
useMetadata = true) useMetadata = true)
public class ReplayModRender { public class ReplayModRender {
public static final String MOD_ID = "replaymod-render"; public static final String MOD_ID = "replaymod-render";
@@ -46,7 +45,7 @@ public class ReplayModRender {
core = ReplayMod.instance; core = ReplayMod.instance;
configuration = new Configuration(event.getSuggestedConfigurationFile()); configuration = new Configuration(event.getSuggestedConfigurationFile());
FMLCommonHandler.instance().bus().register(this); MinecraftForge.EVENT_BUS.register(this);
core.getSettingsRegistry().register(Setting.class); core.getSettingsRegistry().register(Setting.class);
} }

View File

@@ -2,14 +2,13 @@ package com.replaymod.render;
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.utils.ByteBufferPool; import com.replaymod.render.utils.ByteBufferPool;
import com.replaymod.render.utils.StreamPipe; import com.replaymod.render.utils.StreamPipe;
import cpw.mods.fml.common.versioning.ComparableVersion;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
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 net.minecraft.util.Util;
import net.minecraftforge.fml.common.versioning.ComparableVersion;
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;
@@ -33,7 +32,6 @@ import static org.apache.commons.lang3.Validate.isTrue;
public class VideoWriter implements FrameConsumer<RGBFrame> { public class VideoWriter implements FrameConsumer<RGBFrame> {
private final VideoRenderer renderer;
private final RenderSettings settings; private final RenderSettings settings;
private final Process process; private final Process process;
private final OutputStream outputStream; private final OutputStream outputStream;
@@ -43,9 +41,8 @@ public class VideoWriter implements FrameConsumer<RGBFrame> {
private ByteArrayOutputStream ffmpegLog = new ByteArrayOutputStream(4096); private ByteArrayOutputStream ffmpegLog = new ByteArrayOutputStream(4096);
public VideoWriter(final VideoRenderer renderer) throws IOException { public VideoWriter(final RenderSettings settings) throws IOException {
this.renderer = renderer; this.settings = settings;
this.settings = renderer.getRenderSettings();
File outputFolder = settings.getOutputFile().getParentFile(); File outputFolder = settings.getOutputFile().getParentFile();
FileUtils.forceMkdir(outputFolder); FileUtils.forceMkdir(outputFolder);
@@ -60,13 +57,9 @@ public class VideoWriter implements FrameConsumer<RGBFrame> {
.replace("%FILTERS%", settings.getVideoFilters()); .replace("%FILTERS%", settings.getVideoFilters());
String executable = settings.getExportCommand().isEmpty() ? findFFmpeg() : settings.getExportCommand(); String executable = settings.getExportCommand().isEmpty() ? findFFmpeg() : settings.getExportCommand();
LOGGER.info("Starting {} with args: {}", executable, commandArgs); System.out.println("Starting " + executable + " with args: " + commandArgs);
String[] cmdline = new CommandLine(executable).addArguments(commandArgs).toStrings(); String[] cmdline = new CommandLine(executable).addArguments(commandArgs).toStrings();
try { process = new ProcessBuilder(cmdline).directory(outputFolder).start();
process = new ProcessBuilder(cmdline).directory(outputFolder).start();
} catch (IOException e) {
throw new NoFFmpegException(e);
}
File exportLogFile = new File(Minecraft.getMinecraft().mcDataDir, "export.log"); File exportLogFile = new File(Minecraft.getMinecraft().mcDataDir, "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();
@@ -159,16 +152,6 @@ public class VideoWriter implements FrameConsumer<RGBFrame> {
if (aborted) { if (aborted) {
return; return;
} }
try {
// Check whether this is a failure right at the beginning of the rendering process
// or at some later point (ffmpeg won't print the output file until the first frame
// has been written to stdin, so we can't already check for invalid args in <init>).
getVideoFile();
} catch (FFmpegStartupException e) {
// Possibly invalid ffmpeg arguments
renderer.setFailure(e);
return;
}
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()); exportDetails.addCrashSection("Export command", settings.getExportCommand());
@@ -190,7 +173,7 @@ public class VideoWriter implements FrameConsumer<RGBFrame> {
aborted = true; aborted = true;
} }
public File getVideoFile() throws FFmpegStartupException { public File getVideoFile() {
String log = ffmpegLog.toString(); String log = ffmpegLog.toString();
for (String line : log.split("\n")) { for (String line : log.split("\n")) {
if (line.startsWith("Output #0")) { if (line.startsWith("Output #0")) {
@@ -198,30 +181,6 @@ public class VideoWriter implements FrameConsumer<RGBFrame> {
return new File(settings.getOutputFile().getParentFile(), fileName); return new File(settings.getOutputFile().getParentFile(), fileName);
} }
} }
throw new FFmpegStartupException(settings, log); throw new IllegalStateException("No output file found.");
}
public static class NoFFmpegException extends IOException {
public NoFFmpegException(Throwable cause) {
super(cause);
}
}
public static class FFmpegStartupException extends IOException {
private final RenderSettings settings;
private final String log;
public FFmpegStartupException(RenderSettings settings, String log) {
this.settings = settings;
this.log = log;
}
public RenderSettings getSettings() {
return settings;
}
public String getLog() {
return log;
}
} }
} }

View File

@@ -4,10 +4,10 @@ import com.replaymod.render.RenderSettings;
import com.replaymod.render.frame.CubicOpenGlFrame; import com.replaymod.render.frame.CubicOpenGlFrame;
import com.replaymod.render.frame.ODSOpenGlFrame; import com.replaymod.render.frame.ODSOpenGlFrame;
import com.replaymod.render.frame.OpenGlFrame; import com.replaymod.render.frame.OpenGlFrame;
import com.replaymod.render.hooks.GLStateTracker;
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 net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReport;
import net.minecraft.util.ReportedException; import net.minecraft.util.ReportedException;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
@@ -15,13 +15,9 @@ import org.lwjgl.util.ReadableDimension;
import java.io.IOException; import java.io.IOException;
import static net.minecraft.client.renderer.GlStateManager.*;
import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT; import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT; import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D;
import static org.lwjgl.opengl.GL11.glClear;
import static org.lwjgl.opengl.GL11.glEnable;
import static org.lwjgl.opengl.GL11.glPopMatrix;
import static org.lwjgl.opengl.GL11.glPushMatrix;
public class ODSFrameCapturer implements FrameCapturer<ODSOpenGlFrame> { public class ODSFrameCapturer implements FrameCapturer<ODSOpenGlFrame> {
private static final ResourceLocation vertexResource = new ResourceLocation("replaymod", "shader/ods.vert"); private static final ResourceLocation vertexResource = new ResourceLocation("replaymod", "shader/ods.vert");
@@ -32,8 +28,8 @@ public class ODSFrameCapturer implements FrameCapturer<ODSOpenGlFrame> {
private final Program.Uniform directionVariable; private final Program.Uniform directionVariable;
private final Program.Uniform leftEyeVariable; private final Program.Uniform leftEyeVariable;
private final GLStateTracker.EnabledState[] previousStates = new GLStateTracker.EnabledState[2]; private final BooleanState[] previousStates = new BooleanState[3];
private final GLStateTracker.EnabledState previousFogState; private final BooleanState previousFogState;
private final Minecraft mc = Minecraft.getMinecraft(); private final Minecraft mc = Minecraft.getMinecraft();
@@ -46,11 +42,6 @@ public class ODSFrameCapturer implements FrameCapturer<ODSOpenGlFrame> {
return renderInfo.getFrameSize(); return renderInfo.getFrameSize();
} }
@Override
public int getFramesDone() {
return renderInfo.getFramesDone();
}
@Override @Override
public int getTotalFrames() { public int getTotalFrames() {
return renderInfo.getTotalFrames(); return renderInfo.getTotalFrames();
@@ -82,14 +73,15 @@ public class ODSFrameCapturer implements FrameCapturer<ODSOpenGlFrame> {
setTexture("lightMap", 1); setTexture("lightMap", 1);
linkState(0, "textureEnabled"); linkState(0, "textureEnabled");
linkState(1, "lightMapEnabled"); linkState(1, "lightMapEnabled");
linkState(2, "hurtTextureEnabled");
final Program.Uniform uniform = shaderProgram.getUniformVariable("fogEnabled"); final Program.Uniform uniform = shaderProgram.getUniformVariable("fogEnabled");
previousFogState = GLStateTracker.getInstance().fog; previousFogState = GlStateManager.fogState.fog;
uniform.set(previousFogState.isEnabled()); uniform.set(previousFogState.currentState);
GLStateTracker.getInstance().fog = new GLStateTracker.EnabledState() { GlStateManager.fogState.fog = new BooleanState(previousFogState.capability) {
@Override @Override
public void setEnabled(boolean enabled) { public void setState(boolean state) {
super.setEnabled(enabled); super.setState(state);
uniform.set(enabled); uniform.set(state);
} }
}; };
shaderProgram.stopUsing(); shaderProgram.stopUsing();
@@ -104,13 +96,13 @@ public class ODSFrameCapturer implements FrameCapturer<ODSOpenGlFrame> {
private void linkState(int id, String var) { private void linkState(int id, String var) {
final Program.Uniform uniform = shaderProgram.getUniformVariable(var); final Program.Uniform uniform = shaderProgram.getUniformVariable(var);
previousStates[id] = GLStateTracker.getInstance().texture[id]; previousStates[id] = GlStateManager.textureState[id].texture2DState;
uniform.set(previousStates[id].isEnabled()); uniform.set(previousStates[id].currentState);
GLStateTracker.getInstance().texture[id] = new GLStateTracker.EnabledState() { GlStateManager.textureState[id].texture2DState = new BooleanState(previousStates[id].capability) {
@Override @Override
public void setEnabled(boolean enabled) { public void setState(boolean state) {
super.setEnabled(enabled); super.setState(state);
uniform.set(enabled); uniform.set(state);
} }
}; };
} }
@@ -140,10 +132,10 @@ 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++) { for (int i = 0; i < 3; i++) {
GLStateTracker.getInstance().texture[i] = previousStates[i]; GlStateManager.textureState[i].texture2DState = previousStates[i];
} }
GLStateTracker.getInstance().fog = previousFogState; GlStateManager.fogState.fog = previousFogState;
} }
private class CubicStereoFrameCapturer extends CubicPboOpenGlFrameCapturer { private class CubicStereoFrameCapturer extends CubicPboOpenGlFrameCapturer {
@@ -155,17 +147,17 @@ public class ODSFrameCapturer implements FrameCapturer<ODSOpenGlFrame> {
protected OpenGlFrame renderFrame(int frameId, float partialTicks, CubicOpenGlFrameCapturer.Data captureData) { protected OpenGlFrame renderFrame(int frameId, float partialTicks, CubicOpenGlFrameCapturer.Data captureData) {
resize(getFrameWidth(), getFrameHeight()); resize(getFrameWidth(), getFrameHeight());
glPushMatrix(); pushMatrix();
frameBuffer().bindFramebuffer(true); frameBuffer().bindFramebuffer(true);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_TEXTURE_2D); enableTexture2D();
directionVariable.set(captureData.ordinal()); directionVariable.set(captureData.ordinal());
worldRenderer.renderWorld(partialTicks, null); worldRenderer.renderWorld(partialTicks, null);
frameBuffer().unbindFramebuffer(); frameBuffer().unbindFramebuffer();
glPopMatrix(); popMatrix();
return captureFrame(frameId, captureData); return captureFrame(frameId, captureData);
} }

View File

@@ -15,9 +15,9 @@ import org.lwjgl.util.WritableDimension;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import static net.minecraft.client.renderer.GlStateManager.*;
import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT; import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT; import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D;
public abstract class OpenGlFrameCapturer<F extends Frame, D extends CaptureData> implements FrameCapturer<F> { public abstract class OpenGlFrameCapturer<F extends Frame, D extends CaptureData> implements FrameCapturer<F> {
protected final WorldRenderer worldRenderer; protected final WorldRenderer worldRenderer;
@@ -76,16 +76,16 @@ public abstract class OpenGlFrameCapturer<F extends Frame, D extends CaptureData
protected OpenGlFrame renderFrame(int frameId, float partialTicks, D captureData) { protected OpenGlFrame renderFrame(int frameId, float partialTicks, D captureData) {
resize(getFrameWidth(), getFrameHeight()); resize(getFrameWidth(), getFrameHeight());
GL11.glPushMatrix(); pushMatrix();
frameBuffer().bindFramebuffer(true); frameBuffer().bindFramebuffer(true);
GL11.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
GL11.glEnable(GL_TEXTURE_2D); enableTexture2D();
worldRenderer.renderWorld(partialTicks, captureData); worldRenderer.renderWorld(partialTicks, captureData);
frameBuffer().unbindFramebuffer(); frameBuffer().unbindFramebuffer();
GL11.glPopMatrix(); popMatrix();
return captureFrame(frameId, captureData); return captureFrame(frameId, captureData);
} }

View File

@@ -6,8 +6,6 @@ import org.lwjgl.util.ReadableDimension;
public interface RenderInfo { public interface RenderInfo {
ReadableDimension getFrameSize(); ReadableDimension getFrameSize();
int getFramesDone();
int getTotalFrames(); int getTotalFrames();
float updateForNextFrame(); float updateForNextFrame();

View File

@@ -1,9 +1,9 @@
package com.replaymod.render.events; package com.replaymod.render.events;
import com.replaymod.render.rendering.VideoRenderer; import com.replaymod.render.rendering.VideoRenderer;
import cpw.mods.fml.common.eventhandler.Event;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import net.minecraftforge.fml.common.eventhandler.Event;
@RequiredArgsConstructor @RequiredArgsConstructor
public abstract class ReplayRenderEvent extends Event { public abstract class ReplayRenderEvent extends Event {

View File

@@ -1,109 +0,0 @@
package com.replaymod.render.gui;
import com.replaymod.render.RenderSettings;
import com.replaymod.render.VideoWriter;
import de.johni0702.minecraft.gui.container.GuiPanel;
import de.johni0702.minecraft.gui.container.GuiScreen;
import de.johni0702.minecraft.gui.container.GuiVerticalList;
import de.johni0702.minecraft.gui.element.GuiButton;
import de.johni0702.minecraft.gui.element.GuiElement;
import de.johni0702.minecraft.gui.element.GuiLabel;
import de.johni0702.minecraft.gui.layout.CustomLayout;
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
import de.johni0702.minecraft.gui.layout.VerticalLayout;
import net.minecraft.crash.CrashReport;
import net.minecraft.crash.CrashReportCategory;
import net.minecraft.util.ReportedException;
import java.util.Arrays;
import java.util.function.Consumer;
import static com.replaymod.render.ReplayModRender.LOGGER;
public class GuiExportFailed extends GuiScreen {
public static GuiExportFailed tryToRecover(VideoWriter.FFmpegStartupException e, Consumer<RenderSettings> doRestart) {
// Always log the error first
LOGGER.error("Rendering video:", e);
RenderSettings settings = e.getSettings();
// Check whether the user has configured some custom ffmpeg arguments
if (settings.getEncodingPreset().getValue().equals(settings.getExportArguments())) {
// If they haven't, then this is probably a faulty ffmpeg installation and there's nothing we can do
CrashReport crashReport = CrashReport.makeCrashReport(e, "Exporting video");
CrashReportCategory details = crashReport.makeCategory("Export details");
details.addCrashSection("Settings", settings);
details.addCrashSection("FFmpeg log", e.getLog());
throw new ReportedException(crashReport);
} else {
// If they have, ask them whether it was intentional
GuiExportFailed gui = new GuiExportFailed(e, doRestart);
gui.display();
return gui;
}
}
private final GuiLabel logLabel = new GuiLabel(this)
.setI18nText("replaymod.gui.rendering.error.ffmpeglog");
private final GuiVerticalList logList = new GuiVerticalList(this).setDrawShadow(true);
private final GuiButton resetButton = new GuiButton().setI18nLabel("gui.yes").setSize(100, 20);
private final GuiButton abortButton = new GuiButton().setI18nLabel("gui.no").setSize(100, 20);
private final GuiPanel info = new GuiPanel(this)
.setLayout(new VerticalLayout().setSpacing(4))
.addElements(new VerticalLayout.Data(0.5),
new GuiLabel().setI18nText("replaymod.gui.rendering.error.ffmpegargs.1"),
new GuiLabel().setI18nText("replaymod.gui.rendering.error.ffmpegargs.2"),
new GuiLabel(),
new GuiPanel().setLayout(new HorizontalLayout(HorizontalLayout.Alignment.CENTER).setSpacing(5))
.addElements(null, resetButton, abortButton)
);
{
setLayout(new CustomLayout<GuiScreen>() {
@Override
protected void layout(GuiScreen container, int width, int height) {
pos(info, width/2 - width(info)/2, (height/2 - height(info) - 30) / 2 + 30);
pos(logLabel, width/2 - width(logLabel)/2, height/2 + 4);
pos(logList, 10, y(logLabel) + height(logLabel) + 4);
size(logList, width - 10 - x(logList), height - 10 - y(logList));
}
});
setTitle(new GuiLabel().setI18nText("replaymod.gui.rendering.error.title"));
setBackground(Background.DIRT);
}
public GuiExportFailed(VideoWriter.FFmpegStartupException e, Consumer<RenderSettings> doRestart) {
logList.getListPanel().addElements(null,
Arrays.stream(e.getLog().replace("\t", " ").split("\n"))
.map(l -> new GuiLabel().setText(l))
.toArray(GuiElement[]::new));
resetButton.onClick(() -> {
RenderSettings oldSettings = e.getSettings();
doRestart.accept(new RenderSettings(
oldSettings.getRenderMethod(),
oldSettings.getEncodingPreset(),
oldSettings.getVideoWidth(),
oldSettings.getVideoHeight(),
oldSettings.getFramesPerSecond(),
oldSettings.getBitRate(),
oldSettings.getOutputFile(),
oldSettings.isRenderNameTags(),
oldSettings.isStabilizeYaw(),
oldSettings.isStabilizePitch(),
oldSettings.isStabilizeRoll(),
oldSettings.getChromaKeyingColor(),
oldSettings.isInject360Metadata(),
oldSettings.getAntiAliasing(),
oldSettings.getExportCommand(),
oldSettings.getEncodingPreset().getValue(),
oldSettings.isHighPerformance()
));
});
abortButton.onClick(() -> {
// Assume they know what they're doing
getMinecraft().displayGuiScreen(null);
});
}
}

View File

@@ -1,11 +1,8 @@
package com.replaymod.render.gui; package com.replaymod.render.gui;
import com.google.common.collect.Iterables;
import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.replaymod.core.utils.Utils;
import com.replaymod.render.ReplayModRender; import com.replaymod.render.ReplayModRender;
import com.replaymod.render.VideoWriter;
import com.replaymod.render.rendering.VideoRenderer; import com.replaymod.render.rendering.VideoRenderer;
import com.replaymod.render.utils.RenderJob; import com.replaymod.render.utils.RenderJob;
import com.replaymod.replay.ReplayHandler; import com.replaymod.replay.ReplayHandler;
@@ -14,7 +11,6 @@ import com.replaymod.replaystudio.util.I18n;
import de.johni0702.minecraft.gui.GuiRenderer; import de.johni0702.minecraft.gui.GuiRenderer;
import de.johni0702.minecraft.gui.RenderInfo; import de.johni0702.minecraft.gui.RenderInfo;
import de.johni0702.minecraft.gui.container.AbstractGuiClickableContainer; import de.johni0702.minecraft.gui.container.AbstractGuiClickableContainer;
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;
import de.johni0702.minecraft.gui.container.GuiVerticalList; import de.johni0702.minecraft.gui.container.GuiVerticalList;
@@ -29,10 +25,12 @@ 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.gui.GuiErrorScreen; import net.minecraft.client.gui.GuiErrorScreen;
import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReport;
import net.minecraft.util.ReportedException;
import org.lwjgl.util.Dimension; import org.lwjgl.util.Dimension;
import org.lwjgl.util.ReadableDimension; import org.lwjgl.util.ReadableDimension;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.io.IOException;
import java.util.List; import java.util.List;
import static com.replaymod.render.ReplayModRender.LOGGER; import static com.replaymod.render.ReplayModRender.LOGGER;
@@ -64,8 +62,7 @@ public class GuiRenderQueue extends AbstractGuiPopup<GuiRenderQueue> {
renameButton, removeButton), renameButton, removeButton),
closeButton); closeButton);
private final AbstractGuiScreen container; private final GuiContainer container;
private final ReplayHandler replayHandler;
private Entry selectedEntry; private Entry selectedEntry;
{ {
@@ -87,10 +84,9 @@ public class GuiRenderQueue extends AbstractGuiPopup<GuiRenderQueue> {
}).addElements(null, title, list, buttonPanel); }).addElements(null, title, list, buttonPanel);
} }
public GuiRenderQueue(AbstractGuiScreen container, GuiRenderSettings guiRenderSettings, ReplayHandler replayHandler, Timeline timeline) { public GuiRenderQueue(GuiContainer container, GuiRenderSettings guiRenderSettings, ReplayHandler replayHandler, Timeline timeline) {
super(container); super(container);
this.container = container; this.container = container;
this.replayHandler = replayHandler;
LOGGER.trace("Opening render queue popup"); LOGGER.trace("Opening render queue popup");
setBackgroundColor(Colors.DARK_TRANSPARENT); setBackgroundColor(Colors.DARK_TRANSPARENT);
@@ -192,46 +188,30 @@ public class GuiRenderQueue extends AbstractGuiPopup<GuiRenderQueue> {
renderButton.onClick(() -> { renderButton.onClick(() -> {
LOGGER.trace("Render button clicked"); LOGGER.trace("Render button clicked");
processQueue(queue); // Close all GUIs (so settings in GuiRenderSettings are saved)
getMinecraft().displayGuiScreen(null);
// Start rendering
for (RenderJob renderJob : queue) {
LOGGER.info("Starting render job {}", renderJob);
try {
VideoRenderer videoRenderer = new VideoRenderer(renderJob.getSettings(), replayHandler, renderJob.getTimeline());
videoRenderer.renderVideo();
} catch (IOException e) {
LOGGER.error("Rendering video:", e);
GuiErrorScreen errorScreen = new GuiErrorScreen(I18n.format("replaymod.gui.rendering.error.title"),
I18n.format("replaymod.gui.rendering.error.message"));
getMinecraft().displayGuiScreen(errorScreen);
return;
} catch (Throwable t) {
CrashReport crashReport = CrashReport.makeCrashReport(t, "Rendering video");
throw new ReportedException(crashReport);
}
}
}); });
updateButtons(); updateButtons();
} }
private void processQueue(Iterable<RenderJob> queue) {
// Close all GUIs (so settings in GuiRenderSettings are saved)
getMinecraft().displayGuiScreen(null);
// Start rendering
int jobsDone = 0;
for (RenderJob renderJob : queue) {
LOGGER.info("Starting render job {}", renderJob);
try {
VideoRenderer videoRenderer = new VideoRenderer(renderJob.getSettings(), replayHandler, renderJob.getTimeline());
videoRenderer.renderVideo();
} catch (VideoWriter.NoFFmpegException e) {
LOGGER.error("Rendering video:", e);
GuiErrorScreen errorScreen = new GuiErrorScreen(I18n.format("replaymod.gui.rendering.error.title"),
I18n.format("replaymod.gui.rendering.error.message"));
getMinecraft().displayGuiScreen(errorScreen);
return;
} catch (VideoWriter.FFmpegStartupException e) {
int jobsToSkip = jobsDone;
GuiExportFailed.tryToRecover(e, newSettings -> {
// Update current job with fixed ffmpeg arguments
renderJob.setSettings(newSettings);
// Restart queue, skipping the already completed jobs
processQueue(Iterables.skip(queue, jobsToSkip));
});
return;
} catch (Throwable t) {
Utils.error(LOGGER, this, CrashReport.makeCrashReport(t, "Rendering video"), () -> {});
container.display(); // Re-show the queue popup and the new error popup
return;
}
jobsDone++;
}
}
@Override @Override
public void open() { public void open() {
super.open(); super.open();

View File

@@ -7,7 +7,6 @@ import com.google.gson.GsonBuilder;
import com.google.gson.InstanceCreator; import com.google.gson.InstanceCreator;
import com.replaymod.render.RenderSettings; import com.replaymod.render.RenderSettings;
import com.replaymod.render.ReplayModRender; import com.replaymod.render.ReplayModRender;
import com.replaymod.render.VideoWriter;
import com.replaymod.render.rendering.VideoRenderer; import com.replaymod.render.rendering.VideoRenderer;
import com.replaymod.replay.ReplayHandler; import com.replaymod.replay.ReplayHandler;
import com.replaymod.replaystudio.pathing.path.Timeline; import com.replaymod.replaystudio.pathing.path.Timeline;
@@ -30,6 +29,7 @@ import de.johni0702.minecraft.gui.utils.Utils;
import net.minecraft.client.gui.GuiErrorScreen; import net.minecraft.client.gui.GuiErrorScreen;
import net.minecraft.client.resources.I18n; import net.minecraft.client.resources.I18n;
import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReport;
import net.minecraft.util.ReportedException;
import net.minecraftforge.common.config.Configuration; import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.common.config.Property; import net.minecraftforge.common.config.Property;
import org.lwjgl.util.Color; import org.lwjgl.util.Color;
@@ -39,13 +39,11 @@ import org.lwjgl.util.ReadableDimension;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.Map; import java.util.Map;
import static com.replaymod.core.utils.Utils.error;
import static com.replaymod.render.ReplayModRender.LOGGER;
public class GuiRenderSettings extends GuiScreen implements Closeable { public class GuiRenderSettings extends GuiScreen implements Closeable {
public final GuiPanel contentPanel = new GuiPanel(this).setBackgroundColor(Colors.DARK_TRANSPARENT); public final GuiPanel contentPanel = new GuiPanel(this).setBackgroundColor(Colors.DARK_TRANSPARENT);
public final GuiVerticalList settingsList = new GuiVerticalList(contentPanel).setDrawSlider(true); public final GuiVerticalList settingsList = new GuiVerticalList(contentPanel).setDrawSlider(true);
@@ -201,21 +199,15 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
try { try {
VideoRenderer videoRenderer = new VideoRenderer(save(false), replayHandler, timeline); VideoRenderer videoRenderer = new VideoRenderer(save(false), replayHandler, timeline);
videoRenderer.renderVideo(); videoRenderer.renderVideo();
} catch (VideoWriter.NoFFmpegException e) { } catch (IOException e) {
LOGGER.error("Rendering video:", e); e.printStackTrace();
GuiErrorScreen errorScreen = new GuiErrorScreen(I18n.format("replaymod.gui.rendering.error.title"), GuiErrorScreen errorScreen = new GuiErrorScreen(I18n.format("replaymod.gui.rendering.error.title"),
I18n.format("replaymod.gui.rendering.error.message")); I18n.format("replaymod.gui.rendering.error.message"));
getMinecraft().displayGuiScreen(errorScreen); getMinecraft().displayGuiScreen(errorScreen);
} catch (VideoWriter.FFmpegStartupException e) {
GuiExportFailed.tryToRecover(e, newSettings -> {
// Update settings with fixed ffmpeg arguments
exportArguments.setText(newSettings.getExportArguments());
// Restart rendering, this will also save the changed ffmpeg arguments
renderButton.onClick();
});
} catch (Throwable t) { } catch (Throwable t) {
error(LOGGER, GuiRenderSettings.this, CrashReport.makeCrashReport(t, "Rendering video"), () -> {}); CrashReport crashReport = CrashReport.makeCrashReport(t, "Rendering video");
display(); // Re-show the render settings gui and the new error popup throw new ReportedException(crashReport);
} }
} }
}).setSize(100, 20).setI18nLabel("replaymod.gui.render"); }).setSize(100, 20).setI18nLabel("replaymod.gui.render");

View File

@@ -0,0 +1,104 @@
package com.replaymod.render.hooks;
import com.replaymod.render.utils.JailingQueue;
import net.minecraft.client.renderer.RegionRenderCacheBuilder;
import net.minecraft.client.renderer.RenderGlobal;
import net.minecraft.client.renderer.chunk.ChunkCompileTaskGenerator;
import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher;
import net.minecraft.client.renderer.chunk.ChunkRenderWorker;
import net.minecraft.client.renderer.chunk.RenderChunk;
import java.lang.reflect.Field;
import java.util.Iterator;
import java.util.concurrent.PriorityBlockingQueue;
public class ChunkLoadingRenderGlobal {
private final RenderGlobal hooked;
private final ChunkRenderDispatcher renderDispatcher;
private final JailingQueue<ChunkCompileTaskGenerator> workerJailingQueue;
private final CustomChunkRenderWorker renderWorker;
private int frame;
@SuppressWarnings("unchecked")
public ChunkLoadingRenderGlobal(RenderGlobal renderGlobal) {
this.hooked = renderGlobal;
this.renderDispatcher = renderGlobal.renderDispatcher;
this.renderWorker = new CustomChunkRenderWorker(renderDispatcher, new RegionRenderCacheBuilder());
int workerThreads = renderDispatcher.listThreadedWorkers.size();
PriorityBlockingQueue<ChunkCompileTaskGenerator> queueChunkUpdates = renderDispatcher.queueChunkUpdates;
workerJailingQueue = new JailingQueue<>(queueChunkUpdates);
renderDispatcher.queueChunkUpdates = workerJailingQueue;
ChunkCompileTaskGenerator element = new ChunkCompileTaskGenerator(null, null, 0);
element.finish();
for (int i = 0; i < workerThreads; i++) {
queueChunkUpdates.add(element);
}
// Temporary workaround for dead lock, will be replaced by a new (ShaderMod compatible) mechanism later
//noinspection StatementWithEmptyBody
while (renderDispatcher.runChunkUploads(0)) {}
workerJailingQueue.jail(workerThreads);
renderDispatcher.queueChunkUpdates = queueChunkUpdates;
try {
Field hookField = RenderGlobal.class.getField("replayModRender_hook");
hookField.set(hooked, this);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new Error(e);
}
}
public void updateChunks() {
while (renderDispatcher.runChunkUploads(0)) {
hooked.displayListEntitiesDirty = true;
}
while (!renderDispatcher.queueChunkUpdates.isEmpty()) {
try {
renderWorker.processTask(renderDispatcher.queueChunkUpdates.poll());
} catch (InterruptedException ignored) { }
}
Iterator<RenderChunk> iterator = hooked.chunksToUpdate.iterator();
while (iterator.hasNext()) {
RenderChunk renderchunk = iterator.next();
renderDispatcher.updateChunkNow(renderchunk);
renderchunk.clearNeedsUpdate();
iterator.remove();
}
}
public void uninstall() {
workerJailingQueue.freeAll();
try {
Field hookField = RenderGlobal.class.getField("replayModRender_hook");
hookField.set(hooked, null);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new Error(e);
}
}
public int nextFrameId() {
return frame++;
}
/**
* Custom ChunkRenderWorker class providing access to the protected processTask method
*/
private static class CustomChunkRenderWorker extends ChunkRenderWorker {
public CustomChunkRenderWorker(ChunkRenderDispatcher p_i46202_1_, RegionRenderCacheBuilder p_i46202_2_) {
super(p_i46202_1_, p_i46202_2_);
}
@Override
protected void processTask(ChunkCompileTaskGenerator p_178474_1_) throws InterruptedException {
super.processTask(p_178474_1_);
}
}
}

View File

@@ -2,12 +2,11 @@ package com.replaymod.render.hooks;
import com.replaymod.render.RenderSettings; import com.replaymod.render.RenderSettings;
import com.replaymod.render.capturer.CaptureData; import com.replaymod.render.capturer.CaptureData;
import com.replaymod.render.capturer.RenderInfo;
import com.replaymod.render.capturer.WorldRenderer; import com.replaymod.render.capturer.WorldRenderer;
import cpw.mods.fml.common.FMLCommonHandler;
import lombok.Getter; import lombok.Getter;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import org.lwjgl.opengl.GL11; import net.minecraft.client.renderer.GlStateManager;
import net.minecraftforge.fml.common.FMLCommonHandler;
import java.io.IOException; import java.io.IOException;
@@ -17,16 +16,12 @@ public class EntityRendererHandler implements WorldRenderer {
@Getter @Getter
protected final RenderSettings settings; protected final RenderSettings settings;
@Getter
private final RenderInfo renderInfo;
public CaptureData data; public CaptureData data;
public boolean omnidirectional; public boolean omnidirectional;
public EntityRendererHandler(RenderSettings settings, RenderInfo renderInfo) { public EntityRendererHandler(RenderSettings settings) {
this.settings = settings; this.settings = settings;
this.renderInfo = renderInfo;
((IEntityRenderer) mc.entityRenderer).replayModRender_setHandler(this); ((IEntityRenderer) mc.entityRenderer).replayModRender_setHandler(this);
} }
@@ -42,11 +37,11 @@ public class EntityRendererHandler implements WorldRenderer {
mc.entityRenderer.updateLightmap(partialTicks); mc.entityRenderer.updateLightmap(partialTicks);
GL11.glEnable(GL11.GL_DEPTH_TEST); GlStateManager.enableDepth();
GL11.glEnable(GL11.GL_ALPHA_TEST); GlStateManager.enableAlpha();
GL11.glAlphaFunc(516, 0.5F); GlStateManager.alphaFunc(516, 0.5F);
mc.entityRenderer.renderWorld(partialTicks, finishTimeNano); mc.entityRenderer.renderWorldPass(2, partialTicks, finishTimeNano);
FMLCommonHandler.instance().onRenderTickEnd(partialTicks); FMLCommonHandler.instance().onRenderTickEnd(partialTicks);
} }

View File

@@ -1,63 +0,0 @@
package com.replaymod.render.hooks;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
/**
* Tracks OpenGL state for use in the ODS shader.
* All methods should be called from the GL mixins only.
*/
public class GLStateTracker {
private static ThreadLocal<GLStateTracker> stateTracker = ThreadLocal.withInitial(GLStateTracker::new);
public static GLStateTracker getInstance() {
return stateTracker.get();
}
public int activeTexture;
public EnabledState fog = new EnabledState();
public EnabledState[] texture = new EnabledState[32];
{
for (int i = 0; i < texture.length; i++) {
texture[i] = new EnabledState();
}
}
public void updateActiveTexture(int magic) {
this.activeTexture = magic - GL13.GL_TEXTURE0;
}
public void updateEnabledState(int magic, boolean enabled) {
switch (magic) {
case GL11.GL_FOG: fog.setEnabled(enabled); break;
case GL11.GL_TEXTURE_2D: texture[activeTexture].setEnabled(enabled); break;
}
}
public static class EnabledState {
private boolean enabled;
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public boolean isEnabled() {
return enabled;
}
}
// Called via ASM (see GLStateTrackerTransformer)
@Deprecated
@SuppressWarnings("unused")
public static void hook_glEnable(int magic) {
GL11.glEnable(magic);
getInstance().updateEnabledState(magic, true);
}
// Called via ASM (see GLStateTrackerTransformer)
@Deprecated
@SuppressWarnings("unused")
public static void hook_glDisable(int magic) {
GL11.glDisable(magic);
getInstance().updateEnabledState(magic, false);
}
}

View File

@@ -4,6 +4,7 @@ import com.coremedia.iso.IsoFile;
import com.coremedia.iso.boxes.*; import com.coremedia.iso.boxes.*;
import com.google.common.primitives.Bytes; import com.google.common.primitives.Bytes;
import com.googlecode.mp4parser.BasicContainer; import com.googlecode.mp4parser.BasicContainer;
import net.minecraftforge.fml.common.FMLLog;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
@@ -12,8 +13,6 @@ import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import static com.replaymod.render.ReplayModRender.LOGGER;
public class MetadataInjector { public class MetadataInjector {
private static final String STITCHING_SOFTWARE = "Minecraft ReplayMod"; private static final String STITCHING_SOFTWARE = "Minecraft ReplayMod";
@@ -97,7 +96,7 @@ public class MetadataInjector {
videoFileOutputStream = new FileOutputStream(videoFile); videoFileOutputStream = new FileOutputStream(videoFile);
tempIsoFile.getBox(videoFileOutputStream.getChannel()); tempIsoFile.getBox(videoFileOutputStream.getChannel());
} catch(Exception e) { } catch(Exception e) {
LOGGER.error("360 Degree Metadata couldn't be injected", e); FMLLog.getLogger().error("360 Degree Metadata couldn't be injected", e);
} finally { } finally {
IOUtils.closeQuietly(tempIsoFile); IOUtils.closeQuietly(tempIsoFile);
IOUtils.closeQuietly(videoFileOutputStream); IOUtils.closeQuietly(videoFileOutputStream);

View File

@@ -1,48 +0,0 @@
package com.replaymod.render.mixin;
import com.replaymod.render.hooks.EntityRendererHandler;
import net.minecraft.client.Minecraft;
import net.minecraft.client.particle.EffectRenderer;
import net.minecraft.client.particle.EntityFX;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.entity.Entity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(EffectRenderer.class)
public abstract class MixinEffectRenderer {
@Redirect(method = "renderParticles", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/particle/EntityFX;renderParticle(Lnet/minecraft/client/renderer/Tesselator;FFFFFF)V"))
private void renderNormalParticle(EntityFX fx, Tessellator tesselator, float partialTicks,
float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) {
renderParticle(fx, tesselator, partialTicks, rotX, rotXZ, rotZ, rotYZ, rotXY);
}
@Redirect(method = "renderLitParticles", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/particle/EntityFX;renderParticle(Lnet/minecraft/client/renderer/Tesselator;FFFFFF)V"))
private void renderLitParticle(EntityFX fx, Tessellator tesselator, float partialTicks,
float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) {
renderParticle(fx, tesselator, partialTicks, rotX, rotXZ, rotZ, rotYZ, rotXY);
}
private void renderParticle(EntityFX fx, Tessellator tessellator, float partialTicks,
float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) {
EntityRendererHandler handler = ((EntityRendererHandler.IEntityRenderer) Minecraft.getMinecraft().entityRenderer).replayModRender_getHandler();
if (handler != null && handler.omnidirectional) {
Entity view = Minecraft.getMinecraft().renderViewEntity;
// Align all particles towards the camera
double dx = fx.prevPosX + (fx.posX - fx.prevPosX) * partialTicks - view.posX;
double dy = fx.prevPosY + (fx.posY - fx.prevPosY) * partialTicks - view.posY;
double dz = fx.prevPosZ + (fx.posZ - fx.prevPosZ) * partialTicks - view.posZ;
double pitch = -Math.atan2(dy, Math.sqrt(dx * dx + dz * dz));
double yaw = -Math.atan2(dx, dz);
rotX = (float) Math.cos(yaw);
rotZ = (float) Math.sin(yaw);
rotXZ = (float) Math.cos(pitch);
rotYZ = (float) (-rotZ * Math.sin(pitch));
rotXY = (float) (rotX * Math.sin(pitch));
}
fx.renderParticle(tessellator, partialTicks, rotX, rotXZ, rotZ, rotYZ, rotXY);
}
}

View File

@@ -7,12 +7,12 @@ import com.replaymod.render.hooks.EntityRendererHandler;
import com.replaymod.replay.camera.CameraEntity; import com.replaymod.replay.camera.CameraEntity;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.EntityRenderer; import net.minecraft.client.renderer.EntityRenderer;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.RenderGlobal; import net.minecraft.client.renderer.RenderGlobal;
import net.minecraft.client.settings.GameSettings; import net.minecraft.client.settings.GameSettings;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.MovingObjectPosition; import net.minecraft.util.math.RayTraceResult;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
import org.lwjgl.util.ReadableColor; import org.lwjgl.util.ReadableColor;
import org.lwjgl.util.glu.Project; import org.lwjgl.util.glu.Project;
@@ -40,15 +40,6 @@ public abstract class MixinEntityRenderer implements EntityRendererHandler.IEnti
return replayModRender_handler; return replayModRender_handler;
} }
@Redirect(method = "renderWorld", at = @At(value = "INVOKE",target =
"Lnet/minecraft/client/renderer/RenderGlobal;updateRenderers(Lnet/minecraft/entity/EntityLivingBase;Z)Z"))
private boolean replayModRender_updateAllChunks(RenderGlobal self, EntityLivingBase view, boolean renderAllChunks) {
if (replayModRender_handler != null) {
renderAllChunks = true;
}
return self.updateRenderers(view, renderAllChunks);
}
@Inject(method = "setupFog", at = @At("HEAD"), cancellable = true) @Inject(method = "setupFog", at = @At("HEAD"), cancellable = true)
private void replayModRender_onSetupFog(int fogDistanceFlag, float partialTicks, CallbackInfo ci) { private void replayModRender_onSetupFog(int fogDistanceFlag, float partialTicks, CallbackInfo ci) {
if (replayModRender_handler == null) return; if (replayModRender_handler == null) return;
@@ -60,7 +51,7 @@ public abstract class MixinEntityRenderer implements EntityRendererHandler.IEnti
@Inject(method = "orientCamera", at = @At("HEAD")) @Inject(method = "orientCamera", at = @At("HEAD"))
private void replayModRender_resetRotationIfNeeded(float partialTicks, CallbackInfo ci) { private void replayModRender_resetRotationIfNeeded(float partialTicks, CallbackInfo ci) {
if (replayModRender_handler != null) { if (replayModRender_handler != null) {
Entity entity = Minecraft.getMinecraft().renderViewEntity; Entity entity = Minecraft.getMinecraft().getRenderViewEntity();
RenderSettings settings = replayModRender_handler.getSettings(); RenderSettings settings = replayModRender_handler.getSettings();
if (settings.isStabilizeYaw()) { if (settings.isStabilizeYaw()) {
entity.prevRotationYaw = entity.rotationYaw = 0; entity.prevRotationYaw = entity.rotationYaw = 0;
@@ -83,7 +74,7 @@ public abstract class MixinEntityRenderer implements EntityRendererHandler.IEnti
@Inject(method = "setupCameraTransform", at = @At("HEAD")) @Inject(method = "setupCameraTransform", at = @At("HEAD"))
private void replayModRender_beforeSetupCameraTransform(float partialTicks, int renderPass, CallbackInfo ci) { private void replayModRender_beforeSetupCameraTransform(float partialTicks, int renderPass, CallbackInfo ci) {
if (replayModRender_handler != null) { if (replayModRender_handler != null) {
Entity entity = Minecraft.getMinecraft().renderViewEntity; Entity entity = Minecraft.getMinecraft().getRenderViewEntity();
orgYaw = entity.rotationYaw; orgYaw = entity.rotationYaw;
orgPitch = entity.rotationPitch; orgPitch = entity.rotationPitch;
orgPrevYaw = entity.prevRotationYaw; orgPrevYaw = entity.prevRotationYaw;
@@ -95,7 +86,7 @@ public abstract class MixinEntityRenderer implements EntityRendererHandler.IEnti
@Inject(method = "setupCameraTransform", at = @At("RETURN")) @Inject(method = "setupCameraTransform", at = @At("RETURN"))
private void replayModRender_afterSetupCameraTransform(float partialTicks, int renderPass, CallbackInfo ci) { private void replayModRender_afterSetupCameraTransform(float partialTicks, int renderPass, CallbackInfo ci) {
if (replayModRender_handler != null) { if (replayModRender_handler != null) {
Entity entity = Minecraft.getMinecraft().renderViewEntity; Entity entity = Minecraft.getMinecraft().getRenderViewEntity();
entity.rotationYaw = orgYaw; entity.rotationYaw = orgYaw;
entity.rotationPitch = orgPitch; entity.rotationPitch = orgPitch;
entity.prevRotationYaw = orgPrevYaw; entity.prevRotationYaw = orgPrevYaw;
@@ -113,7 +104,7 @@ public abstract class MixinEntityRenderer implements EntityRendererHandler.IEnti
ci.cancel(); ci.cancel();
return; // No spectator hands during 360° view, we wouldn't even know where to put it return; // No spectator hands during 360° view, we wouldn't even know where to put it
} }
Entity currentEntity = Minecraft.getMinecraft().renderViewEntity; Entity currentEntity = Minecraft.getMinecraft().getRenderViewEntity();
if (currentEntity instanceof EntityPlayer && !(currentEntity instanceof CameraEntity)) { if (currentEntity instanceof EntityPlayer && !(currentEntity instanceof CameraEntity)) {
if (renderPass == 2) { // Need to update render pass if (renderPass == 2) { // Need to update render pass
renderPass = replayModRender_handler.data == StereoscopicOpenGlFrameCapturer.Data.LEFT_EYE ? 1 : 0; renderPass = replayModRender_handler.data == StereoscopicOpenGlFrameCapturer.Data.LEFT_EYE ? 1 : 0;
@@ -127,17 +118,16 @@ public abstract class MixinEntityRenderer implements EntityRendererHandler.IEnti
@Shadow @Shadow
public abstract void renderHand(float partialTicks, int renderPass); public abstract void renderHand(float partialTicks, int renderPass);
@Redirect(method = "renderWorld", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderGlobal;drawSelectionBox(Lnet/minecraft/entity/player/EntityPlayer;Lnet/minecraft/util/MovingObjectPosition;IF)V")) @Redirect(method = "renderWorldPass", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderGlobal;drawSelectionBox(Lnet/minecraft/entity/player/EntityPlayer;Lnet/minecraft/util/math/RayTraceResult;IF)V"))
private void replayModRender_drawSelectionBox(RenderGlobal instance, EntityPlayer player, MovingObjectPosition mop, int alwaysZero, float partialTicks) { private void replayModRender_drawSelectionBox(RenderGlobal instance, EntityPlayer player, RayTraceResult rtr, int alwaysZero, float partialTicks) {
if (replayModRender_handler == null) { if (replayModRender_handler == null) {
instance.drawSelectionBox(player, mop, alwaysZero, partialTicks); instance.drawSelectionBox(player, rtr, alwaysZero, partialTicks);
} }
} }
private int orgRenderDistanceChunks; private int orgRenderDistanceChunks;
@Inject(method = "renderWorld", at = @At(value = "INVOKE", ordinal = 0, shift = At.Shift.AFTER, @Inject(method = "renderWorldPass", at = @At(value = "JUMP", ordinal = 0))
target = "Lnet/minecraft/client/renderer/culling/ClippingHelperImpl;getInstance()Lnet/minecraft/client/renderer/culling/ClippingHelper;"))
private void replayModRender_beforeRenderSky(CallbackInfo ci) { private void replayModRender_beforeRenderSky(CallbackInfo ci) {
if (replayModRender_handler != null && replayModRender_handler.getSettings().getChromaKeyingColor() != null) { if (replayModRender_handler != null && replayModRender_handler.getSettings().getChromaKeyingColor() != null) {
GameSettings settings = Minecraft.getMinecraft().gameSettings; GameSettings settings = Minecraft.getMinecraft().gameSettings;
@@ -146,15 +136,15 @@ public abstract class MixinEntityRenderer implements EntityRendererHandler.IEnti
} }
} }
@Redirect(method = "renderWorld", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderGlobal;renderSky(F)V")) @Redirect(method = "renderWorldPass", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderGlobal;renderSky(FI)V"))
private void replayModRender_renderSky(RenderGlobal instance, float partialTicks) { private void replayModRender_renderSky(RenderGlobal instance, float partialTicks, int renderPass) {
if (replayModRender_handler != null && replayModRender_handler.getSettings().getChromaKeyingColor() != null) { if (replayModRender_handler != null && replayModRender_handler.getSettings().getChromaKeyingColor() != null) {
Minecraft.getMinecraft().gameSettings.renderDistanceChunks = orgRenderDistanceChunks; Minecraft.getMinecraft().gameSettings.renderDistanceChunks = orgRenderDistanceChunks;
ReadableColor color = replayModRender_handler.getSettings().getChromaKeyingColor(); ReadableColor color = replayModRender_handler.getSettings().getChromaKeyingColor();
GL11.glClearColor(color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, 1); GlStateManager.clearColor(color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, 1);
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT); GlStateManager.clear(GL11.GL_COLOR_BUFFER_BIT);
} else { } else {
instance.renderSky(partialTicks); instance.renderSky(partialTicks, renderPass);
} }
} }
@@ -162,24 +152,24 @@ public abstract class MixinEntityRenderer implements EntityRendererHandler.IEnti
* Stereoscopic Renderer * Stereoscopic Renderer
*/ */
@Inject(method = "setupCameraTransform", at = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glLoadIdentity()V", shift = At.Shift.AFTER, ordinal = 0, remap = false)) @Inject(method = "setupCameraTransform", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GlStateManager;loadIdentity()V", shift = At.Shift.AFTER, ordinal = 0))
private void replayModRender_setupStereoscopicProjection(float partialTicks, int renderPass, CallbackInfo ci) { private void replayModRender_setupStereoscopicProjection(float partialTicks, int renderPass, CallbackInfo ci) {
if (replayModRender_getHandler() != null) { if (replayModRender_getHandler() != null) {
if (replayModRender_getHandler().data == StereoscopicOpenGlFrameCapturer.Data.LEFT_EYE) { if (replayModRender_getHandler().data == StereoscopicOpenGlFrameCapturer.Data.LEFT_EYE) {
GL11.glTranslatef(0.07f, 0, 0); GlStateManager.translate(0.07, 0, 0);
} else if (replayModRender_getHandler().data == StereoscopicOpenGlFrameCapturer.Data.RIGHT_EYE) { } else if (replayModRender_getHandler().data == StereoscopicOpenGlFrameCapturer.Data.RIGHT_EYE) {
GL11.glTranslatef(-0.07f, 0, 0); GlStateManager.translate(-0.07, 0, 0);
} }
} }
} }
@Inject(method = "setupCameraTransform", at = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glLoadIdentity()V", shift = At.Shift.AFTER, ordinal = 1, remap = false)) @Inject(method = "setupCameraTransform", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GlStateManager;loadIdentity()V", shift = At.Shift.AFTER, ordinal = 1))
private void replayModRender_setupStereoscopicModelView(float partialTicks, int renderPass, CallbackInfo ci) { private void replayModRender_setupStereoscopicModelView(float partialTicks, int renderPass, CallbackInfo ci) {
if (replayModRender_getHandler() != null) { if (replayModRender_getHandler() != null) {
if (replayModRender_getHandler().data == StereoscopicOpenGlFrameCapturer.Data.LEFT_EYE) { if (replayModRender_getHandler().data == StereoscopicOpenGlFrameCapturer.Data.LEFT_EYE) {
GL11.glTranslatef(0.1f, 0, 0); GlStateManager.translate(0.1, 0, 0);
} else if (replayModRender_getHandler().data == StereoscopicOpenGlFrameCapturer.Data.RIGHT_EYE) { } else if (replayModRender_getHandler().data == StereoscopicOpenGlFrameCapturer.Data.RIGHT_EYE) {
GL11.glTranslatef(-0.1f, 0, 0); GlStateManager.translate(-0.1, 0, 0);
} }
} }
} }
@@ -193,11 +183,16 @@ public abstract class MixinEntityRenderer implements EntityRendererHandler.IEnti
replayModRender_gluPerspective(fovY, aspect, zNear, zFar); replayModRender_gluPerspective(fovY, aspect, zNear, zFar);
} }
@Redirect(method = "renderHand", at = @At(value = "INVOKE", target = "Lorg/lwjgl/util/glu/Project;gluPerspective(FFFF)V", remap = false)) @Redirect(method = "renderWorldPass", at = @At(value = "INVOKE", target = "Lorg/lwjgl/util/glu/Project;gluPerspective(FFFF)V", remap = false))
private void replayModRender_gluPerspective$1(float fovY, float aspect, float zNear, float zFar) { private void replayModRender_gluPerspective$1(float fovY, float aspect, float zNear, float zFar) {
replayModRender_gluPerspective(fovY, aspect, zNear, zFar); replayModRender_gluPerspective(fovY, aspect, zNear, zFar);
} }
@Redirect(method = "renderCloudsCheck", at = @At(value = "INVOKE", target = "Lorg/lwjgl/util/glu/Project;gluPerspective(FFFF)V", remap = false))
private void replayModRender_gluPerspective$2(float fovY, float aspect, float zNear, float zFar) {
replayModRender_gluPerspective(fovY, aspect, zNear, zFar);
}
@Override @Override
public void replayModRender_gluPerspective(float fovY, float aspect, float zNear, float zFar) { public void replayModRender_gluPerspective(float fovY, float aspect, float zNear, float zFar) {
if (replayModRender_getHandler() != null && replayModRender_getHandler().omnidirectional) { if (replayModRender_getHandler() != null && replayModRender_getHandler().omnidirectional) {
@@ -212,28 +207,28 @@ public abstract class MixinEntityRenderer implements EntityRendererHandler.IEnti
if (replayModRender_getHandler() != null && replayModRender_getHandler().data instanceof CubicOpenGlFrameCapturer.Data) { if (replayModRender_getHandler() != null && replayModRender_getHandler().data instanceof CubicOpenGlFrameCapturer.Data) {
switch ((CubicOpenGlFrameCapturer.Data) replayModRender_getHandler().data) { switch ((CubicOpenGlFrameCapturer.Data) replayModRender_getHandler().data) {
case FRONT: case FRONT:
GL11.glRotatef(0, 0.0F, 1.0F, 0.0F); GlStateManager.rotate(0, 0.0F, 1.0F, 0.0F);
break; break;
case RIGHT: case RIGHT:
GL11.glRotatef(90, 0.0F, 1.0F, 0.0F); GlStateManager.rotate(90, 0.0F, 1.0F, 0.0F);
break; break;
case BACK: case BACK:
GL11.glRotatef(180, 0.0F, 1.0F, 0.0F); GlStateManager.rotate(180, 0.0F, 1.0F, 0.0F);
break; break;
case LEFT: case LEFT:
GL11.glRotatef(-90, 0.0F, 1.0F, 0.0F); GlStateManager.rotate(-90, 0.0F, 1.0F, 0.0F);
break; break;
case BOTTOM: case BOTTOM:
GL11.glRotatef(90, 1.0F, 0.0F, 0.0F); GlStateManager.rotate(90, 1.0F, 0.0F, 0.0F);
break; break;
case TOP: case TOP:
GL11.glRotatef(-90, 1.0F, 0.0F, 0.0F); GlStateManager.rotate(-90, 1.0F, 0.0F, 0.0F);
break; break;
} }
} }
if (replayModRender_getHandler() != null && replayModRender_getHandler().omnidirectional) { if (replayModRender_getHandler() != null && replayModRender_getHandler().omnidirectional) {
// Minecraft goes back a little so we have to revert that // Minecraft goes back a little so we have to revert that
GL11.glTranslatef(0.0F, 0.0F, 0.1F); GlStateManager.translate(0.0F, 0.0F, 0.1F);
} }
} }
} }

View File

@@ -1,23 +0,0 @@
package com.replaymod.render.mixin;
import com.replaymod.render.hooks.EntityRendererHandler;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.culling.Frustrum;
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.callback.CallbackInfoReturnable;
@Mixin(Frustrum.class)
public abstract class MixinFrustum {
@Inject(method = "isBoxInFrustum", at = @At("HEAD"), cancellable = true)
public void isBoxInFrustum(double minX, double minY, double minZ, double maxX, double maxY, double maxZ, CallbackInfoReturnable<Boolean> ci) {
EntityRendererHandler handler = ((EntityRendererHandler.IEntityRenderer) Minecraft.getMinecraft().entityRenderer).replayModRender_getHandler();
if (handler != null && handler.omnidirectional && handler.data == null) {
// Normally the camera is always facing the direction of the omnidirectional image face that is currently
// getting rendered. With ODS however, the camera is always facing forwards and the turning happens in the
// vertex shader (non-trivial due to stereo). As such, all chunks need to be rendered all the time for ODS.
ci.setReturnValue(true);
}
}
}

View File

@@ -1,16 +0,0 @@
package com.replaymod.render.mixin;
import com.replaymod.render.hooks.GLStateTracker;
import net.minecraft.client.renderer.OpenGlHelper;
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.callback.CallbackInfo;
@Mixin(OpenGlHelper.class)
public abstract class MixinOpenGlHelper {
@Inject(method = "setActiveTexture", at = @At("RETURN"))
private static void replayModRender_trackActiveTexture(int magic, CallbackInfo ci) {
GLStateTracker.getInstance().updateActiveTexture(magic);
}
}

View File

@@ -0,0 +1,47 @@
package com.replaymod.render.mixin;
import com.replaymod.render.hooks.EntityRendererHandler;
import net.minecraft.client.Minecraft;
import net.minecraft.client.particle.Particle;
import net.minecraft.client.particle.ParticleManager;
import net.minecraft.client.renderer.VertexBuffer;
import net.minecraft.entity.Entity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(ParticleManager.class)
public abstract class MixinParticleManager {
@Redirect(method = "renderParticles", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/particle/Particle;renderParticle(Lnet/minecraft/client/renderer/VertexBuffer;Lnet/minecraft/entity/Entity;FFFFFF)V"))
private void renderNormalParticle(Particle particle, VertexBuffer vertexBuffer, Entity view, float partialTicks,
float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) {
renderParticle(particle, vertexBuffer, view, partialTicks, rotX, rotXZ, rotZ, rotYZ, rotXY);
}
@Redirect(method = "renderLitParticles", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/particle/Particle;renderParticle(Lnet/minecraft/client/renderer/VertexBuffer;Lnet/minecraft/entity/Entity;FFFFFF)V"))
private void renderLitParticle(Particle particle, VertexBuffer vertexBuffer, Entity view, float partialTicks,
float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) {
renderParticle(particle, vertexBuffer, view, partialTicks, rotX, rotXZ, rotZ, rotYZ, rotXY);
}
private void renderParticle(Particle particle, VertexBuffer vertexBuffer, Entity view, float partialTicks,
float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) {
EntityRendererHandler handler = ((EntityRendererHandler.IEntityRenderer) Minecraft.getMinecraft().entityRenderer).replayModRender_getHandler();
if (handler != null && handler.omnidirectional) {
// Align all particles towards the camera
double dx = particle.prevPosX + (particle.posX - particle.prevPosX) * partialTicks - view.posX;
double dy = particle.prevPosY + (particle.posY - particle.prevPosY) * partialTicks - view.posY;
double dz = particle.prevPosZ + (particle.posZ - particle.prevPosZ) * partialTicks - view.posZ;
double pitch = -Math.atan2(dy, Math.sqrt(dx * dx + dz * dz));
double yaw = -Math.atan2(dx, dz);
rotX = (float) Math.cos(yaw);
rotZ = (float) Math.sin(yaw);
rotXZ = (float) Math.cos(pitch);
rotYZ = (float) (-rotZ * Math.sin(pitch));
rotXY = (float) (rotX * Math.sin(pitch));
}
particle.renderParticle(vertexBuffer, view, partialTicks, rotX, rotXZ, rotZ, rotYZ, rotXY);
}
}

View File

@@ -2,20 +2,19 @@ package com.replaymod.render.mixin;
import com.replaymod.render.hooks.EntityRendererHandler; import com.replaymod.render.hooks.EntityRendererHandler;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.entity.RendererLivingEntity; import net.minecraft.client.renderer.entity.Render;
import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.Entity;
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.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(RendererLivingEntity.class) @Mixin(Render.class)
public abstract class MixinRender { public abstract class MixinRender {
@Inject(method = "canRenderName", at = @At("HEAD"), cancellable = true) @Inject(method = "renderLivingLabel", at = @At("HEAD"), cancellable = true)
private void replayModRender_areAllNamesHidden(EntityLivingBase entity, CallbackInfoReturnable<Boolean> ci) { private void replayModRender_areAllNamesHidden(Entity entityIn, String str, double x, double y, double z, int maxDistance, CallbackInfo ci) {
EntityRendererHandler handler = ((EntityRendererHandler.IEntityRenderer) Minecraft.getMinecraft().entityRenderer).replayModRender_getHandler(); EntityRendererHandler handler = ((EntityRendererHandler.IEntityRenderer) Minecraft.getMinecraft().entityRenderer).replayModRender_getHandler();
if (handler != null && !handler.getSettings().isRenderNameTags()) { if (handler != null && !handler.getSettings().isRenderNameTags()) {
ci.setReturnValue(false);
ci.cancel(); ci.cancel();
} }
} }

View File

@@ -0,0 +1,55 @@
package com.replaymod.render.mixin;
import com.replaymod.render.hooks.ChunkLoadingRenderGlobal;
import net.minecraft.client.renderer.RenderGlobal;
import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher;
import net.minecraft.client.renderer.culling.ICamera;
import net.minecraft.entity.Entity;
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;
@Mixin(RenderGlobal.class)
public abstract class MixinRenderGlobal {
public ChunkLoadingRenderGlobal replayModRender_hook;
private boolean replayModRender_passThroughSetupTerrain;
@Shadow
public boolean displayListEntitiesDirty;
@Shadow
public ChunkRenderDispatcher renderDispatcher;
@Shadow
public abstract void setupTerrain(Entity viewEntity, double partialTicks, ICamera camera,
int frameCount, boolean playerSpectator);
@Inject(method = "setupTerrain", at = @At("HEAD"), cancellable = true)
public void replayModRender_setupTerrain(Entity viewEntity, double partialTicks, ICamera camera,
int frameCount, boolean playerSpectator, CallbackInfo ci) {
if (replayModRender_hook != null && !replayModRender_passThroughSetupTerrain) {
replayModRender_passThroughSetupTerrain = true;
do {
setupTerrain(viewEntity, partialTicks, camera, replayModRender_hook.nextFrameId(), playerSpectator);
replayModRender_hook.updateChunks();
} while (displayListEntitiesDirty);
displayListEntitiesDirty = true;
replayModRender_passThroughSetupTerrain = false;
ci.cancel();
}
}
@Inject(method = "updateChunks", at = @At("HEAD"), cancellable = true)
public void replayModRender_updateChunks(long finishTimeNano, CallbackInfo ci) {
if (replayModRender_hook != null) {
replayModRender_hook.updateChunks();
ci.cancel();
}
}
}

View File

@@ -8,7 +8,7 @@ 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.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(RenderManager.class) @Mixin(RenderManager.class)
public abstract class MixinRenderManager { public abstract class MixinRenderManager {
@@ -19,7 +19,7 @@ public abstract class MixinRenderManager {
private float playerViewY; private float playerViewY;
@Inject(method = "doRenderEntity", at = @At("HEAD")) @Inject(method = "doRenderEntity", at = @At("HEAD"))
private void replayModRender_reorientForCubicRendering(Entity entity, double dx, double dy, double dz, float iDoNotKnow, float partialTicks, boolean iDoNotCare, CallbackInfoReturnable ci) { private void replayModRender_reorientForCubicRendering(Entity entity, double dx, double dy, double dz, float iDoNotKnow, float partialTicks, boolean iDoNotCare, CallbackInfo ci) {
EntityRendererHandler handler = ((EntityRendererHandler.IEntityRenderer) Minecraft.getMinecraft().entityRenderer).replayModRender_getHandler(); EntityRendererHandler handler = ((EntityRendererHandler.IEntityRenderer) Minecraft.getMinecraft().entityRenderer).replayModRender_getHandler();
if (handler != null && handler.omnidirectional) { if (handler != null && handler.omnidirectional) {
double pitch = -Math.atan2(dy, Math.sqrt(dx * dx + dz * dz)); double pitch = -Math.atan2(dy, Math.sqrt(dx * dx + dz * dz));

View File

@@ -1,21 +0,0 @@
package com.replaymod.render.mixin;
import com.replaymod.render.hooks.EntityRendererHandler;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.entity.RendererLivingEntity;
import net.minecraft.entity.EntityLivingBase;
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.callback.CallbackInfoReturnable;
@Mixin(RendererLivingEntity.class)
public abstract class MixinRendererLivingEntity {
@Inject(method = "canRenderName", at = @At("HEAD"), cancellable = true)
private void replayModRender_areAllNamesHidden(EntityLivingBase entity, CallbackInfoReturnable<Boolean> ci) {
EntityRendererHandler handler = ((EntityRendererHandler.IEntityRenderer) Minecraft.getMinecraft().entityRenderer).replayModRender_getHandler();
if (handler != null && !handler.getSettings().isRenderNameTags()) {
ci.setReturnValue(false); //this calls the cancel method
}
}
}

View File

@@ -30,9 +30,9 @@ public class Pipelines {
RenderSettings settings = renderInfo.getRenderSettings(); RenderSettings settings = renderInfo.getRenderSettings();
FrameCapturer<OpenGlFrame> capturer; FrameCapturer<OpenGlFrame> capturer;
if (PixelBufferObject.SUPPORTED) { if (PixelBufferObject.SUPPORTED) {
capturer = new SimplePboOpenGlFrameCapturer(new EntityRendererHandler(settings, renderInfo), renderInfo); capturer = new SimplePboOpenGlFrameCapturer(new EntityRendererHandler(settings), renderInfo);
} else { } else {
capturer = new SimpleOpenGlFrameCapturer(new EntityRendererHandler(settings, renderInfo), renderInfo); capturer = new SimpleOpenGlFrameCapturer(new EntityRendererHandler(settings), renderInfo);
} }
return new Pipeline<>(capturer, new OpenGlToRGBProcessor(), consumer); return new Pipeline<>(capturer, new OpenGlToRGBProcessor(), consumer);
} }
@@ -41,9 +41,9 @@ public class Pipelines {
RenderSettings settings = renderInfo.getRenderSettings(); RenderSettings settings = renderInfo.getRenderSettings();
FrameCapturer<StereoscopicOpenGlFrame> capturer; FrameCapturer<StereoscopicOpenGlFrame> capturer;
if (PixelBufferObject.SUPPORTED) { if (PixelBufferObject.SUPPORTED) {
capturer = new StereoscopicPboOpenGlFrameCapturer(new EntityRendererHandler(settings, renderInfo), renderInfo); capturer = new StereoscopicPboOpenGlFrameCapturer(new EntityRendererHandler(settings), renderInfo);
} else { } else {
capturer = new StereoscopicOpenGlFrameCapturer(new EntityRendererHandler(settings, renderInfo), renderInfo); capturer = new StereoscopicOpenGlFrameCapturer(new EntityRendererHandler(settings), renderInfo);
} }
return new Pipeline<>(capturer, new StereoscopicToRGBProcessor(), consumer); return new Pipeline<>(capturer, new StereoscopicToRGBProcessor(), consumer);
} }
@@ -52,9 +52,9 @@ public class Pipelines {
RenderSettings settings = renderInfo.getRenderSettings(); RenderSettings settings = renderInfo.getRenderSettings();
FrameCapturer<CubicOpenGlFrame> capturer; FrameCapturer<CubicOpenGlFrame> capturer;
if (PixelBufferObject.SUPPORTED) { if (PixelBufferObject.SUPPORTED) {
capturer = new CubicPboOpenGlFrameCapturer(new EntityRendererHandler(settings, renderInfo), renderInfo, settings.getVideoWidth() / 4); capturer = new CubicPboOpenGlFrameCapturer(new EntityRendererHandler(settings), renderInfo, settings.getVideoWidth() / 4);
} else { } else {
capturer = new CubicOpenGlFrameCapturer(new EntityRendererHandler(settings, renderInfo), renderInfo, settings.getVideoWidth() / 4); capturer = new CubicOpenGlFrameCapturer(new EntityRendererHandler(settings), renderInfo, settings.getVideoWidth() / 4);
} }
return new Pipeline<>(capturer, new CubicToRGBProcessor(), consumer); return new Pipeline<>(capturer, new CubicToRGBProcessor(), consumer);
} }
@@ -63,9 +63,9 @@ public class Pipelines {
RenderSettings settings = renderInfo.getRenderSettings(); RenderSettings settings = renderInfo.getRenderSettings();
FrameCapturer<CubicOpenGlFrame> capturer; FrameCapturer<CubicOpenGlFrame> capturer;
if (PixelBufferObject.SUPPORTED) { if (PixelBufferObject.SUPPORTED) {
capturer = new CubicPboOpenGlFrameCapturer(new EntityRendererHandler(settings, renderInfo), renderInfo, settings.getVideoWidth() / 4); capturer = new CubicPboOpenGlFrameCapturer(new EntityRendererHandler(settings), renderInfo, settings.getVideoWidth() / 4);
} else { } else {
capturer = new CubicOpenGlFrameCapturer(new EntityRendererHandler(settings, renderInfo), renderInfo, settings.getVideoWidth() / 4); capturer = new CubicOpenGlFrameCapturer(new EntityRendererHandler(settings), renderInfo, settings.getVideoWidth() / 4);
} }
return new Pipeline<>(capturer, new EquirectangularToRGBProcessor(settings.getVideoWidth() / 4), consumer); return new Pipeline<>(capturer, new EquirectangularToRGBProcessor(settings.getVideoWidth() / 4), consumer);
} }
@@ -73,7 +73,7 @@ public class Pipelines {
public static Pipeline<ODSOpenGlFrame, RGBFrame> newODSPipeline(RenderInfo renderInfo, FrameConsumer<RGBFrame> consumer) { public static Pipeline<ODSOpenGlFrame, RGBFrame> newODSPipeline(RenderInfo renderInfo, FrameConsumer<RGBFrame> consumer) {
RenderSettings settings = renderInfo.getRenderSettings(); RenderSettings settings = renderInfo.getRenderSettings();
FrameCapturer<ODSOpenGlFrame> capturer = FrameCapturer<ODSOpenGlFrame> capturer =
new ODSFrameCapturer(new EntityRendererHandler(settings, renderInfo), renderInfo, settings.getVideoWidth() / 4); new ODSFrameCapturer(new EntityRendererHandler(settings), renderInfo, settings.getVideoWidth() / 4);
return new Pipeline<>(capturer, new ODSToRGBProcessor(settings.getVideoWidth() / 4), consumer); return new Pipeline<>(capturer, new ODSToRGBProcessor(settings.getVideoWidth() / 4), consumer);
} }
} }

View File

@@ -1,6 +1,5 @@
package com.replaymod.render.rendering; package com.replaymod.render.rendering;
import com.replaymod.core.ReplayMod;
import com.replaymod.pathing.player.AbstractTimelinePlayer; import com.replaymod.pathing.player.AbstractTimelinePlayer;
import com.replaymod.pathing.player.ReplayTimer; import com.replaymod.pathing.player.ReplayTimer;
import com.replaymod.pathing.properties.TimestampProperty; import com.replaymod.pathing.properties.TimestampProperty;
@@ -12,21 +11,22 @@ import com.replaymod.render.events.ReplayRenderEvent;
import com.replaymod.render.frame.RGBFrame; import com.replaymod.render.frame.RGBFrame;
import com.replaymod.render.gui.GuiRenderingDone; import com.replaymod.render.gui.GuiRenderingDone;
import com.replaymod.render.gui.GuiVideoRenderer; import com.replaymod.render.gui.GuiVideoRenderer;
import com.replaymod.render.hooks.ChunkLoadingRenderGlobal;
import com.replaymod.render.metadata.MetadataInjector; import com.replaymod.render.metadata.MetadataInjector;
import com.replaymod.render.utils.SoundHandler;
import com.replaymod.replay.ReplayHandler; import com.replaymod.replay.ReplayHandler;
import com.replaymod.replaystudio.pathing.path.Keyframe; import com.replaymod.replaystudio.pathing.path.Keyframe;
import com.replaymod.replaystudio.pathing.path.Path; import com.replaymod.replaystudio.pathing.path.Path;
import com.replaymod.replaystudio.pathing.path.Timeline; import com.replaymod.replaystudio.pathing.path.Timeline;
import cpw.mods.fml.common.FMLCommonHandler;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.audio.SoundCategory;
import net.minecraft.client.gui.ScaledResolution; import net.minecraft.client.gui.ScaledResolution;
import net.minecraft.client.renderer.OpenGlHelper; import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.client.shader.Framebuffer; import net.minecraft.client.shader.Framebuffer;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.Timer; import net.minecraft.util.Timer;
import net.minecraftforge.common.MinecraftForge;
import org.lwjgl.input.Mouse; import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.Display; import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.Dimension; import org.lwjgl.util.Dimension;
import org.lwjgl.util.ReadableDimension; import org.lwjgl.util.ReadableDimension;
@@ -39,10 +39,9 @@ import java.util.concurrent.Future;
import java.util.concurrent.FutureTask; import java.util.concurrent.FutureTask;
import static com.google.common.collect.Iterables.getLast; import static com.google.common.collect.Iterables.getLast;
import static com.replaymod.render.ReplayModRender.LOGGER; import static net.minecraft.client.renderer.GlStateManager.*;
import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT; import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT; import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D;
public class VideoRenderer implements RenderInfo { public class VideoRenderer implements RenderInfo {
private final Minecraft mc = Minecraft.getMinecraft(); private final Minecraft mc = Minecraft.getMinecraft();
@@ -55,10 +54,11 @@ public class VideoRenderer implements RenderInfo {
private int fps; private int fps;
private boolean mouseWasGrabbed; private boolean mouseWasGrabbed;
private boolean debugInfoWasShown; private boolean debugInfoWasShown;
private Map originalSoundLevels; private Map<SoundCategory, Float> originalSoundLevels;
private TimelinePlayer timelinePlayer; private TimelinePlayer timelinePlayer;
private Future<Void> timelinePlayerFuture; private Future<Void> timelinePlayerFuture;
private ChunkLoadingRenderGlobal chunkLoadingRenderGlobal;
private int framesDone; private int framesDone;
private int totalFrames; private int totalFrames;
@@ -66,7 +66,6 @@ public class VideoRenderer implements RenderInfo {
private final GuiVideoRenderer gui; private final GuiVideoRenderer gui;
private boolean paused; private boolean paused;
private boolean cancelled; private boolean cancelled;
private volatile Throwable failureCause;
private Framebuffer guiFramebuffer; private Framebuffer guiFramebuffer;
private int displayWidth, displayHeight; private int displayWidth, displayHeight;
@@ -77,7 +76,7 @@ public class VideoRenderer implements RenderInfo {
this.timeline = timeline; this.timeline = timeline;
this.gui = new GuiVideoRenderer(this); this.gui = new GuiVideoRenderer(this);
this.renderingPipeline = Pipelines.newPipeline(settings.getRenderMethod(), this, this.renderingPipeline = Pipelines.newPipeline(settings.getRenderMethod(), this,
videoWriter = new VideoWriter(this) { videoWriter = new VideoWriter(settings) {
@Override @Override
public void consume(RGBFrame frame) { public void consume(RGBFrame frame) {
gui.updatePreview(frame); gui.updatePreview(frame);
@@ -90,8 +89,8 @@ public class VideoRenderer implements RenderInfo {
* Render this video. * Render this video.
* @return {@code true} if rendering was successful, {@code false} if the user aborted rendering (or the window was closed) * @return {@code true} if rendering was successful, {@code false} if the user aborted rendering (or the window was closed)
*/ */
public boolean renderVideo() throws Throwable { public boolean renderVideo() {
FMLCommonHandler.instance().bus().post(new ReplayRenderEvent.Pre(this)); MinecraftForge.EVENT_BUS.post(new ReplayRenderEvent.Pre(this));
setup(); setup();
@@ -137,11 +136,7 @@ public class VideoRenderer implements RenderInfo {
finish(); finish();
FMLCommonHandler.instance().bus().post(new ReplayRenderEvent.Post(this)); MinecraftForge.EVENT_BUS.post(new ReplayRenderEvent.Post(this));
if (failureCause != null) {
throw failureCause;
}
return !cancelled; return !cancelled;
} }
@@ -201,9 +196,9 @@ public class VideoRenderer implements RenderInfo {
for (SoundCategory category : SoundCategory.values()) { for (SoundCategory category : SoundCategory.values()) {
mutedSounds.put(category, 0f); mutedSounds.put(category, 0f);
} }
originalSoundLevels = mc.gameSettings.mapSoundLevels; originalSoundLevels = mc.gameSettings.soundLevels;
mutedSounds.put(SoundCategory.MASTER, (Float) originalSoundLevels.get(SoundCategory.MASTER)); mutedSounds.put(SoundCategory.MASTER, originalSoundLevels.get(SoundCategory.MASTER));
mc.gameSettings.mapSoundLevels = mutedSounds; mc.gameSettings.soundLevels = mutedSounds;
fps = settings.getFramesPerSecond(); fps = settings.getFramesPerSecond();
@@ -224,8 +219,7 @@ public class VideoRenderer implements RenderInfo {
updateDisplaySize(); updateDisplaySize();
ScaledResolution scaled = new ScaledResolution(mc, displayWidth, displayHeight); chunkLoadingRenderGlobal = new ChunkLoadingRenderGlobal(mc.renderGlobal);
gui.toMinecraft().setWorldAndResolution(mc, scaled.getScaledWidth(), scaled.getScaledHeight());
// Set up our own framebuffer to render the GUI to // Set up our own framebuffer to render the GUI to
guiFramebuffer = new Framebuffer(displayWidth, displayHeight, true); guiFramebuffer = new Framebuffer(displayWidth, displayHeight, true);
@@ -245,19 +239,16 @@ public class VideoRenderer implements RenderInfo {
if (mouseWasGrabbed) { if (mouseWasGrabbed) {
mc.mouseHelper.grabMouseCursor(); mc.mouseHelper.grabMouseCursor();
} }
mc.gameSettings.mapSoundLevels = originalSoundLevels; mc.gameSettings.soundLevels = originalSoundLevels;
mc.displayGuiScreen(null); mc.displayGuiScreen(null);
if (chunkLoadingRenderGlobal != null) {
ReplayMod.soundHandler.playRenderSuccessSound(); chunkLoadingRenderGlobal.uninstall();
try {
if (!hasFailed()) {
new GuiRenderingDone(ReplayModRender.instance, videoWriter.getVideoFile(), totalFrames, settings).display();
}
} catch (VideoWriter.FFmpegStartupException e) {
setFailure(e);
} }
new SoundHandler().playRenderSuccessSound();
new GuiRenderingDone(ReplayModRender.instance, videoWriter.getVideoFile(), totalFrames, settings).display();
// Finally, resize the Minecraft framebuffer to the actual width/height of the window // Finally, resize the Minecraft framebuffer to the actual width/height of the window
mc.resize(displayWidth, displayHeight); mc.resize(displayWidth, displayHeight);
} }
@@ -270,7 +261,11 @@ public class VideoRenderer implements RenderInfo {
} }
mc.currentScreen = gui.toMinecraft(); mc.currentScreen = gui.toMinecraft();
mc.runTick(); try {
mc.runTick();
} catch (IOException e) {
throw new RuntimeException(e);
}
} }
public void drawGui() { public void drawGui() {
@@ -281,17 +276,23 @@ public class VideoRenderer implements RenderInfo {
guiFramebuffer.createBindFramebuffer(mc.displayWidth, mc.displayHeight); guiFramebuffer.createBindFramebuffer(mc.displayWidth, mc.displayHeight);
} }
GL11.glPushMatrix(); pushMatrix();
GL11.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
GL11.glEnable(GL_TEXTURE_2D); enableTexture2D();
guiFramebuffer.bindFramebuffer(true); guiFramebuffer.bindFramebuffer(true);
mc.entityRenderer.setupOverlayRendering(); mc.entityRenderer.setupOverlayRendering();
ScaledResolution scaled = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight); ScaledResolution scaled = new ScaledResolution(mc);
gui.toMinecraft().setWorldAndResolution(mc, scaled.getScaledWidth(), scaled.getScaledHeight()); gui.toMinecraft().setWorldAndResolution(mc, scaled.getScaledWidth(), scaled.getScaledHeight());
gui.toMinecraft().handleInput(); try {
gui.toMinecraft().handleInput();
} catch (IOException e) {
// That's a strange exception from this kind of method O_o
// It isn't actually thrown here, so we'll deal with it the easy way
throw new RuntimeException(e);
}
int mouseX = Mouse.getX() * scaled.getScaledWidth() / mc.displayWidth; int mouseX = Mouse.getX() * scaled.getScaledWidth() / mc.displayWidth;
int mouseY = scaled.getScaledHeight() - Mouse.getY() * scaled.getScaledHeight() / mc.displayHeight - 1; int mouseY = scaled.getScaledHeight() - Mouse.getY() * scaled.getScaledHeight() / mc.displayHeight - 1;
@@ -299,17 +300,17 @@ public class VideoRenderer implements RenderInfo {
gui.toMinecraft().drawScreen(mouseX, mouseY, 0); gui.toMinecraft().drawScreen(mouseX, mouseY, 0);
guiFramebuffer.unbindFramebuffer(); guiFramebuffer.unbindFramebuffer();
GL11.glPopMatrix(); popMatrix();
GL11.glPushMatrix(); pushMatrix();
guiFramebuffer.framebufferRender(displayWidth, displayHeight); guiFramebuffer.framebufferRender(displayWidth, displayHeight);
GL11.glPopMatrix(); popMatrix();
// if not in high performance mode, update the gui size if screen size changed // if not in high performance mode, update the gui size if screen size changed
// otherwise just swap the progress gui to screen // otherwise just swap the progress gui to screen
if (settings.isHighPerformance()) { if (settings.isHighPerformance()) {
Display.update(); Display.update();
} else { } else {
mc.resetSize(); mc.updateDisplay();
} }
if (Mouse.isGrabbed()) { if (Mouse.isGrabbed()) {
Mouse.setGrabbed(false); Mouse.setGrabbed(false);
@@ -322,7 +323,7 @@ public class VideoRenderer implements RenderInfo {
return; return;
} }
} }
} while (paused && !hasFailed()); } while (paused);
} }
private boolean displaySizeChanged() { private boolean displaySizeChanged() {
@@ -363,20 +364,6 @@ public class VideoRenderer implements RenderInfo {
renderingPipeline.cancel(); renderingPipeline.cancel();
} }
public boolean hasFailed() {
return failureCause != null;
}
public synchronized void setFailure(Throwable cause) {
if (this.failureCause != null) {
LOGGER.error("Further failure during failed rendering: ", cause);
} else {
LOGGER.error("Failure during rendering: ", cause);
this.failureCause = cause;
cancel();
}
}
private class TimelinePlayer extends AbstractTimelinePlayer { private class TimelinePlayer extends AbstractTimelinePlayer {
public TimelinePlayer(ReplayHandler replayHandler) { public TimelinePlayer(ReplayHandler replayHandler) {
super(replayHandler); super(replayHandler);

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