Compare commits

..

50 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
Jonas Herzig
91573d9487 Update jGui, ReplayStudio and Translations
jGui:
9e84e72 Fix inversion of empty selection

ReplayStudio:
ff12b1d Remove invalid filter from services files
c6a9325 Proper value wrapping in CatmullRomSplineInterpolator (fixes #8 GH)
2017-05-31 09:16:19 +02:00
Jonas Herzig
d71358bcba Add confirmation dialog before overwriting path presets (fixes #65) 2017-05-21 19:34:46 +02:00
Jonas Herzig
1a55983986 Move addCallback method from integration test Utils to main Utils 2017-05-21 19:34:46 +02:00
Jonas Herzig
18c5bcd469 Be more cooperative with other mods on the ingame menu (fixes #42) 2017-05-21 18:59:29 +02:00
Jonas Herzig
c054fe83f6 Register keys for simplepathing only once (fixes #63) 2017-05-21 17:11:15 +02:00
Jonas Herzig
f13297c5a5 Fix default interpolator handling in keyframe gui and after loading (fixes #64) 2017-05-21 16:29:59 +02:00
Jonas Herzig
391f304c5f Show error popup if entity tracker fails to load 2017-05-21 12:42:13 +02:00
Jonas Herzig
0e0eaaa9a7 Fix entity tracker being set even if it failed loading (fixes #50) 2017-05-21 12:27:20 +02:00
Jonas Herzig
a34bbbcd68 Fix NPE when spectating a player without a camera entity 2017-05-21 11:57:30 +02:00
Jonas Herzig
60879fbbed Change local class to be an inner class because Srg2Source breaks with it 2017-04-21 17:24:43 +02:00
Jonas Herzig
dfafbecf35 Load translations from github repo, only reload language not all resource packs 2017-04-21 16:36:47 +02:00
Jonas Herzig
8a6ec5191b Add missing tooltips to player overview and various missing chat messages 2017-04-21 16:36:47 +02:00
Jonas Herzig
5677fc54cb Re-add warning to replay editor 2017-04-21 16:36:47 +02:00
Jonas Herzig
748a91e7b2 Fix missing tooltips on fav/like/dislike buttons in replay center 2017-04-21 16:36:47 +02:00
Jonas Herzig
ee24866f29 Fix upload replay button not being updated when name/tags change 2017-04-21 16:36:47 +02:00
Jonas Herzig
dba085c700 Move translations into separate repo 2017-04-21 16:36:47 +02:00
Jonas Herzig
3f0e3e725e Fix NPE when receiving Replay|Restrict messages (fixes #16 GH) 2017-04-21 13:38:08 +02:00
johni0702
40e1d850da Fix crash when saving the last keyframe in the edit gui (fixes #61) 2017-04-14 17:31:16 +02:00
johni0702
03aada1f5f Update Mixin to 0.6.8 (fixes #9 GH) 2017-04-14 17:31:16 +02:00
johni0702
0c1dc658ad Fix interpolator being lost when moving keyframe to the end (fixes #62) 2017-04-14 17:31:16 +02:00
johni0702
fcbbbc9ea1 Add integration test. Run with ./gradlew runIntegrationTest 2017-04-14 17:31:15 +02:00
johni0702
fe6ded0143 Fix movement of keyframe via GuiEditKeyframe not updating selected keyframe
Selecting a keyframe, double clicking it to open the gui, changing the
timestamp, saving and then clicking the Add/Remove Keyframe button used to
crash the client. This is fixed by updating the selection whenever the time is
changed.
2017-04-13 18:18:17 +02:00
johni0702
f58fa8f9c2 Fix Login GUI not respecting other GUIs opened on startup
Previously the Login GUI would simply replace the currently active GUI.
Now the currently active GUI is stored and then re-opened after login.
2017-04-13 18:12:07 +02:00
johni0702
4fc3a3166c Fix OpenEye being installed into working dir instead of mcDataDir 2017-04-13 18:06:21 +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
110 changed files with 2151 additions and 1672 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

@@ -5,3 +5,7 @@
[submodule "ReplayStudio"] [submodule "ReplayStudio"]
path = ReplayStudio path = ReplayStudio
url = https://github.com/ReplayMod/ReplayStudio url = https://github.com/ReplayMod/ReplayStudio
[submodule "src/main/resources/assets/replaymod/lang"]
path = src/main/resources/assets/replaymod/lang
url = https://github.com/ReplayMod/Translations

View File

@@ -1,7 +1,5 @@
import groovy.json.JsonOutput import groovy.json.JsonOutput
import java.util.zip.ZipInputStream
buildscript { buildscript {
repositories { repositories {
mavenCentral() mavenCentral()
@@ -19,8 +17,8 @@ buildscript {
} }
} }
dependencies { dependencies {
classpath 'net.minecraftforge.gradle:ForgeGradle:2.0-SNAPSHOT' classpath 'net.minecraftforge.gradle:ForgeGradle:2.2-SNAPSHOT'
classpath 'org.spongepowered:mixingradle:0.3-SNAPSHOT' classpath 'org.spongepowered:mixingradle:0.4-SNAPSHOT'
} }
} }
@@ -36,9 +34,9 @@ archivesBaseName = "replaymod"
minecraft { minecraft {
coreMod = 'com.replaymod.core.LoadingPlugin' coreMod = 'com.replaymod.core.LoadingPlugin'
version = '1.8-11.14.4.1563' version = '1.9.4-12.17.0.1976'
runDir = "eclipse" runDir = "eclipse"
mappings = "snapshot_nodoc_20141130" mappings = "snapshot_20160518"
replace '@MOD_VERSION@', project.version replace '@MOD_VERSION@', project.version
replace '@MC_VERSION@', project.minecraft.version replace '@MC_VERSION@', project.minecraft.version
} }
@@ -61,7 +59,7 @@ configurations {
dependencies { dependencies {
compile 'org.projectlombok:lombok:1.16.4' compile 'org.projectlombok:lombok:1.16.4'
compile 'org.spongepowered:mixin:0.6.4-SNAPSHOT' compile 'org.spongepowered:mixin:0.6.8-SNAPSHOT'
shade 'com.googlecode.mp4parser:isoparser:1.1.7' shade 'com.googlecode.mp4parser:isoparser:1.1.7'
shade 'org.apache.commons:commons-exec:1.3' shade 'org.apache.commons:commons-exec:1.3'
shade 'com.google.apis:google-api-services-youtube:v3-rev178-1.22.0' shade 'com.google.apis:google-api-services-youtube:v3-rev178-1.22.0'
@@ -103,27 +101,6 @@ jar {
} }
from noticeDir from noticeDir
def langDir = file("$buildDir/languages")
doFirst {
try {
langDir.deleteDir()
langDir.mkdirs()
def dir = new File(langDir, 'assets/replaymod/lang/')
dir.mkdirs()
def zip = new ZipInputStream(new URL('http://replaymod.com/api/grab_languages').openStream())
def e;
while ((e = zip.nextEntry) != null) {
new File(dir, e.getName()) << zip
zip.closeEntry()
}
} catch(Exception e) {
e.printStackTrace();
}
}
from (langDir) {
exclude '**/en_US.lang'
}
from ({shade().collect { it.isDirectory() ? it : zipTree(it) }}) { from ({shade().collect { it.isDirectory() ? it : zipTree(it) }}) {
exclude '**/NOTICE*' exclude '**/NOTICE*'
// exclude everything taken in from jGui for running the mod in a dev environment // exclude everything taken in from jGui for running the mod in a dev environment
@@ -138,6 +115,7 @@ jar {
manifest { manifest {
attributes 'TweakClass': 'org.spongepowered.asm.launch.MixinTweaker', attributes 'TweakClass': 'org.spongepowered.asm.launch.MixinTweaker',
'TweakOrder': '0', 'TweakOrder': '0',
'FMLCorePluginContainsFMLMod': 'true',
'FMLAT': 'replaymod_at.cfg' 'FMLAT': 'replaymod_at.cfg'
} }
} }
@@ -188,6 +166,13 @@ sourceSets {
} }
refMap = "mixins.replaymod.refmap.json" refMap = "mixins.replaymod.refmap.json"
} }
integrationTest {
compileClasspath += main.runtimeClasspath + main.output
java {
srcDir file('src/integration-test/java')
}
resources.srcDir file('src/integration-test/resources')
}
} }
task copySrg(type: Copy, dependsOn: 'genSrgs') { task copySrg(type: Copy, dependsOn: 'genSrgs') {
@@ -199,6 +184,81 @@ 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"]) {
main = 'GradleStart'
standardOutput = System.out
errorOutput = System.err
workingDir file(minecraft.runDir)
def testDir = new File(minecraft.runDir, "integration-test")
doFirst {
testDir.deleteDir()
testDir.mkdirs()
}
doLast {
testDir.deleteDir()
}
afterEvaluate {
def runClient = tasks.getByName("runClient")
runIntegrationTest.jvmArgs = runClient.jvmArgs + "-Dfml.noGrab=true"
runIntegrationTest.args = runClient.args + "--gameDir" + testDir.canonicalPath
runIntegrationTest.classpath runClient.classpath + sourceSets.integrationTest.output
}
}
def generateVersionsJson() { def generateVersionsJson() {
// List all tags // List all tags
def stdout = new ByteArrayOutputStream() def stdout = new ByteArrayOutputStream()

2
jGui

Submodule jGui updated: a77d2f3f50...e2bd90628e

View File

@@ -0,0 +1,30 @@
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Entry point for running integration tests from within the IDE.
* This is <b>not</b> called when running integration tests directly from gradle.
*/
public class IntegrationTest {
public static void main(String[] args) throws Throwable {
// Prevent MC from grabbing our mouse during the test (it's not needed)
System.setProperty("fml.noGrab", "true");
// Make sure the test folder exists and is fresh
File gameDir = new File(System.getProperty("user.dir"), "integration-test");
if (gameDir.exists()) {
FileUtils.forceDelete(gameDir);
}
FileUtils.forceMkdir(gameDir);
// Set game dir to test folder and call regular entry point
List<String> argsList = new ArrayList<>(Arrays.asList(args));
argsList.add("--gameDir");
argsList.add(gameDir.getCanonicalPath());
GradleStart.main(argsList.toArray(new String[argsList.size()]));
}
}

View File

@@ -0,0 +1,340 @@
package com.replaymod.core;
import com.google.common.collect.Iterables;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import de.johni0702.minecraft.gui.container.AbstractGuiOverlay;
import de.johni0702.minecraft.gui.container.AbstractGuiScreen;
import de.johni0702.minecraft.gui.container.GuiContainer;
import de.johni0702.minecraft.gui.container.GuiOverlay;
import de.johni0702.minecraft.gui.container.GuiScreen;
import de.johni0702.minecraft.gui.element.GuiButton;
import de.johni0702.minecraft.gui.element.GuiElement;
import de.johni0702.minecraft.gui.element.GuiTexturedButton;
import de.johni0702.minecraft.gui.popup.AbstractGuiPopup;
import de.johni0702.minecraft.gui.utils.Consumer;
import net.minecraft.client.Minecraft;
import net.minecraft.client.settings.KeyBinding;
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 java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.concurrent.TimeoutException;
import static com.replaymod.core.ReplayModIntegrationTest.LOGGER;
import static com.replaymod.core.utils.Utils.addCallback;
public abstract class AbstractTask implements Task {
public static Task create(Consumer<AbstractTask> init) {
return new AbstractTask() {
@Override
protected void init() {
init.consume(this);
}
};
}
public static final Minecraft mc = Minecraft.getMinecraft();
public final ReplayMod core = ReplayMod.instance;
public SettableFuture<Void> future;
@Override
public ListenableFuture<Void> execute() {
future = SettableFuture.create();
MinecraftForge.EVENT_BUS.register(this);
addCallback(future, success -> {
MinecraftForge.EVENT_BUS.unregister(this);
}, error -> {});
init();
return future;
}
protected void init() {}
protected void runLater(Runnable runnable) {
core.runLater(() -> {
try {
runnable.run();
} catch (Throwable t) {
future.setException(t);
}
});
}
public void expectGuiClosed(Runnable onClosed) {
expectGuiClosed0(10, onClosed);
}
public void expectGuiClosed(int timeout, Runnable onClosed) {
expectGuiClosed0(timeout, onClosed);
}
private void expectGuiClosed0(int timeout, Runnable onClosed) {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
class EventHandler {
final net.minecraft.client.gui.GuiScreen currentScreen = mc.currentScreen;
int framesPassed;
@SubscribeEvent
public void onGuiOpen(TickEvent.RenderTickEvent event) {
if (event.phase != TickEvent.Phase.START) return;
if (currentScreen != mc.currentScreen) {
MinecraftForge.EVENT_BUS.unregister(this);
onClosed.run();
} else {
if (framesPassed < timeout) {
framesPassed++;
} else {
Object gui = (gui = GuiScreen.from(currentScreen)) == null ? currentScreen : gui;
Exception e = new TimeoutException("Timeout while waiting for " + gui + " to be closed.");
e.setStackTrace(Arrays.copyOfRange(stackTrace, 3, stackTrace.length));
future.setException(e);
}
}
}
}
MinecraftForge.EVENT_BUS.register(new EventHandler());
}
public void expectPopupClosed(Runnable onClosed) {
expectPopupClosed0(10, onClosed);
}
public void expectPopupClosed(int timeout, Runnable onClosed) {
expectPopupClosed0(timeout, onClosed);
}
private void expectPopupClosed0(int timeout, Runnable onClosed) {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
AbstractGuiPopup popup = getPopup(mc.currentScreen);
if (popup == null) {
throw new IllegalStateException("No popup found.");
}
class EventHandler {
int framesPassed;
@SubscribeEvent
public void onGuiOpen(TickEvent.RenderTickEvent event) {
if (event.phase != TickEvent.Phase.START) return;
if (getPopup(mc.currentScreen) != popup) {
MinecraftForge.EVENT_BUS.unregister(this);
onClosed.run();
} else {
if (framesPassed < timeout) {
framesPassed++;
} else {
Exception e = new TimeoutException("Timeout while waiting for " + popup + " to be closed.");
e.setStackTrace(Arrays.copyOfRange(stackTrace, 3, stackTrace.length));
future.setException(e);
}
}
}
}
MinecraftForge.EVENT_BUS.register(new EventHandler());
}
private AbstractGuiPopup getPopup(net.minecraft.client.gui.GuiScreen minecraft) {
GuiContainer<?> container = GuiOverlay.from(minecraft);
if (container == null) {
container = GuiScreen.from(minecraft);
}
if (container != null) {
while (container.getContainer() != null) {
container = container.getContainer();
}
GuiElement popup = Iterables.getLast(container.getChildren());
if (popup instanceof AbstractGuiPopup) {
return (AbstractGuiPopup) popup;
}
}
return null;
}
public <T> void expectGui(Class<T> guiClass, Consumer<T> onOpen) {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
class EventHandler {
net.minecraft.client.gui.GuiScreen currentScreen;
int framesPassed;
@SubscribeEvent
public void onGuiOpen(TickEvent.RenderTickEvent event) {
if (event.phase != TickEvent.Phase.START) return;
if (currentScreen != mc.currentScreen) {
currentScreen = mc.currentScreen;
framesPassed = 0;
}
if (framesPassed < 10) {
framesPassed++;
return;
}
MinecraftForge.EVENT_BUS.unregister(this);
Object foundGui = null;
if (AbstractGuiScreen.class.isAssignableFrom(guiClass)) {
AbstractGuiScreen guiScreen = GuiScreen.from(currentScreen);
if (guiClass.isInstance(guiScreen)) {
onOpen.consume(guiClass.cast(guiScreen));
return;
}
foundGui = guiScreen;
} else if (AbstractGuiOverlay.class.isAssignableFrom(guiClass)) {
AbstractGuiOverlay guiScreen = GuiOverlay.from(currentScreen);
if (guiClass.isInstance(guiScreen)) {
onOpen.consume(guiClass.cast(guiScreen));
return;
}
foundGui = guiScreen;
} else if (AbstractGuiPopup.class.isAssignableFrom(guiClass)) {
AbstractGuiPopup popup = getPopup(currentScreen);
if (guiClass.isInstance(popup)) {
onOpen.consume(guiClass.cast(popup));
return;
}
} else {
if (guiClass.isInstance(currentScreen)) {
onOpen.consume(guiClass.cast(currentScreen));
return;
}
}
class UnexpectedGuiException extends Exception {
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));
}
}
MinecraftForge.EVENT_BUS.register(new EventHandler());
}
private void clickNow(int x, int y) {
try {
Method method = net.minecraft.client.gui.GuiScreen.class
.getDeclaredMethod("mouseClicked", int.class, int.class, int.class);
method.setAccessible(true);
method.invoke(mc.currentScreen, x, y, 0);
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
future.setException(e);
}
}
public void click(int x, int y) {
runLater(() -> {
LOGGER.info("Clicking at {}/{}", x, y);
clickNow(x, y);
});
}
private void dragNow(int x, int y) {
try {
Method method = net.minecraft.client.gui.GuiScreen.class
.getDeclaredMethod("mouseClickMove", int.class, int.class, int.class, long.class);
method.setAccessible(true);
method.invoke(mc.currentScreen, x, y, 0, 0);
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
future.setException(e);
}
}
public void drag(int x, int y) {
runLater(() -> {
LOGGER.info("Dragging to {}/{}", x, y);
dragNow(x, y);
});
}
public void click(GuiButton button) {
runLater(() -> {
if (!button.isEnabled()) {
future.setException(new IllegalStateException("Button is disabled: " + button.getLabel()));
return;
}
LOGGER.info("Clicking button {}", button.getLabel());
button.onClick();
});
}
public void click(GuiTexturedButton button) {
runLater(() -> {
if (!button.isEnabled()) {
future.setException(new IllegalStateException("Button is disabled: " + button.getTexture()));
return;
}
LOGGER.info("Clicking textured button {}", button.getTexture());
button.onClick();
});
}
public void click(String buttonText) {
runLater(() -> {
LOGGER.info("Clicking button {}", buttonText);
try {
Field field = net.minecraft.client.gui.GuiScreen.class.getDeclaredField("buttonList");
field.setAccessible(true);
@SuppressWarnings("unchecked")
List<net.minecraft.client.gui.GuiButton> buttonList = (List)
field.get(mc.currentScreen);
net.minecraft.client.gui.GuiButton button = null;
for (net.minecraft.client.gui.GuiButton guiButton : buttonList) {
if (guiButton.displayString.equals(buttonText)) {
button = guiButton;
}
}
if (button == null) {
future.setException(new NoSuchElementException("No button with label: " + buttonText));
return;
}
clickNow(button.xPosition + 5, button.yPosition + 5);
} catch (IllegalAccessException | NoSuchFieldException e) {
future.setException(e);
}
});
}
public void type(String string) {
for (char c : string.toCharArray()) {
press(c, Keyboard.getKeyIndex(String.valueOf(c).toUpperCase()));
}
}
public void press(int keyCode) {
String keyName = Keyboard.getKeyName(keyCode);
char character = keyName.length() == 1 ? keyName.charAt(0) : '\0';
press(character, keyCode);
}
public void press(char character, int keyCode) {
runLater(() -> {
LOGGER.info("Pressing key {}", Keyboard.getKeyName(keyCode));
if (mc.currentScreen == null || mc.currentScreen.allowUserInput) {
KeyBinding.onTick(keyCode);
FMLCommonHandler.instance().fireKeyInput();
}
if (mc.currentScreen != null) {
try {
Method method = net.minecraft.client.gui.GuiScreen.class
.getDeclaredMethod("keyTyped", char.class, int.class);
method.setAccessible(true);
method.invoke(mc.currentScreen, character, keyCode);
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
future.setException(e);
}
}
});
}
}

View File

@@ -0,0 +1,41 @@
package com.replaymod.core;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import static com.replaymod.core.ReplayModIntegrationTest.LOGGER;
import static com.replaymod.core.utils.Utils.addCallback;
public class CompositeTask implements Task {
private SettableFuture<Void> future;
protected final Task[] children;
public CompositeTask(Task[] children) {
this.children = children;
}
@Override
public ListenableFuture<Void> execute() {
future = SettableFuture.create();
executeChild(0);
return future;
}
private void executeChild(int childIndex) {
if (future.isDone()) return;
if (childIndex < children.length) {
ReplayMod.instance.runLater(() -> {
try {
Task task = children[childIndex];
LOGGER.info("Running task {}", task);
ListenableFuture<Void> childFuture = task.execute();
addCallback(childFuture, done -> executeChild(childIndex + 1), err -> future.setException(err));
} catch (Throwable t) {
future.setException(t);
}
});
} else {
future.set(null);
}
}
}

View File

@@ -0,0 +1,73 @@
package com.replaymod.core;
import com.replaymod.core.regression.RegressionTest60;
import com.replaymod.core.regression.RegressionTest61;
import com.replaymod.core.regression.RegressionTest62;
import com.replaymod.extra.DownloadOpenEye;
import com.replaymod.online.SkipLogin;
import com.replaymod.recording.CreateSPWorld;
import com.replaymod.recording.ExitSPWorld;
import com.replaymod.replay.ExitReplay;
import com.replaymod.replay.LoadReplay;
import com.replaymod.replay.OpenReplayViewer;
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 static com.replaymod.core.AbstractTask.mc;
import static com.replaymod.core.ReplayModIntegrationTest.MOD_ID;
import static com.replaymod.core.utils.Utils.addCallback;
/**
* Helper mod that initiates the integration tests.
*/
@Mod(modid = MOD_ID)
public class ReplayModIntegrationTest {
public static final String MOD_ID = "replaymod-integration-test";
public static Logger LOGGER;
@Mod.EventHandler
public void init(FMLPreInitializationEvent event) {
LOGGER = event.getModLog();
// Make sure the game window doesn't have to remain in focus during the test
mc.gameSettings.pauseOnLostFocus = false;
runTasks(
new SkipLogin(),
new DownloadOpenEye(),
new CreateSPWorld(),
new Wait(5000),
new ExitSPWorld(),
new OpenReplayViewer(),
new LoadReplay(),
new RegressionTest60(),
new RegressionTest61(),
new RegressionTest62(),
// new AbstractTask() {}, // Uncomment to not exit on success (useful for writing more tests)
new ExitReplay()
);
}
private void runTasks(Task... tests) {
addCallback(new CompositeTask(tests).execute(), success -> {
if (!Minecraft.getMinecraft().hasCrashed) {
LOGGER.info("===================================================");
LOGGER.info("= ALL TESTS PASSED =");
LOGGER.info("===================================================");
FMLCommonHandler.instance().exitJava(0, false);
}
}, error -> {
LOGGER.error("Failed task:", error);
LOGGER.error("===================================================");
LOGGER.error("= TEST FAILED =");
LOGGER.error("===================================================");
FMLCommonHandler.instance().exitJava(1, false);
});
}
}

View File

@@ -0,0 +1,35 @@
package com.replaymod.core;
import com.google.common.util.concurrent.ListenableFuture;
public interface Task {
ListenableFuture<Void> execute();
static Task click(int x, int y) {
return AbstractTask.create(task -> {
task.click(x, y);
task.runLater(() -> task.future.set(null));
});
}
static Task drag(int x, int y) {
return AbstractTask.create(task -> {
task.drag(x, y);
task.runLater(() -> task.future.set(null));
});
}
static Task pressKey(int keyCode) {
return AbstractTask.create(task -> {
task.press(keyCode);
task.runLater(() -> task.future.set(null));
});
}
static Task pressKey(char character, int keyCode) {
return AbstractTask.create(task -> {
task.press(character, keyCode);
task.runLater(() -> task.future.set(null));
});
}
}

View File

@@ -0,0 +1,9 @@
package com.replaymod.core;
public class Utils {
public static void times(int x, Runnable runnable) {
for (int i = 0; i < x; i++) {
runnable.run();
}
}
}

View File

@@ -0,0 +1,21 @@
package com.replaymod.core;
public class Wait extends AbstractTask {
private final int duration;
public Wait(int duration) {
this.duration = duration;
}
@Override
protected void init() {
new Thread(() -> {
try {
Thread.sleep(duration);
runLater(() -> future.set(null));
} catch (InterruptedException e) {
runLater(() -> future.setException(e));
}
}).start();
}
}

View File

@@ -0,0 +1,59 @@
package com.replaymod.core.regression;
import com.replaymod.core.AbstractTask;
import com.replaymod.core.CompositeTask;
import com.replaymod.core.Task;
import com.replaymod.replay.SpectatePlayer;
import com.replaymod.replay.overlay.OverlayGui;
import com.replaymod.simplepathing.GuiPathingTasks;
import com.replaymod.simplepathing.gui.GuiEditKeyframe;
import org.lwjgl.input.Keyboard;
import static com.replaymod.core.AbstractTask.mc;
import static com.replaymod.core.Utils.times;
/**
* Regression test: #60 Crash in path preview when two spectator keyframes are closer than 50ms
*/
public class RegressionTest60 extends CompositeTask {
public RegressionTest60() {
super(new Task[]{
new SpectatePlayer(),
OverlayGui.whileOpened(
// Place first spectator keyframe
new GuiPathingTasks.ClickPositionKeyframeButton(),
// Place second spectator keyframe
Task.click(130, 50),
new GuiPathingTasks.ClickPositionKeyframeButton(),
// Move second keyframe to 20ms on the keyframe timeline
AbstractTask.create(task -> {
// Double click keyframe
task.click(130, 50);
task.click(130, 50);
task.expectGui(GuiEditKeyframe.Spectator.class, gui -> {
// Set ms field to 20
task.click(mc.currentScreen.width / 2 + 80, mc.currentScreen.height / 2);
times(4, () -> task.press(Keyboard.KEY_BACK));
task.type("20");
// Set other fields to 0
times(2, () -> {
task.press(Keyboard.KEY_TAB);
times(4, () -> task.press(Keyboard.KEY_BACK));
task.press(Keyboard.KEY_0);
});
task.click(gui.saveButton);
task.expectPopupClosed(() -> task.future.set(null));
});
}),
// Place second spectator keyframe
new GuiPathingTasks.ClickPositionKeyframeButton()
),
// Stop spectating player
new SpectatePlayer.End(),
// Enable path preview
Task.pressKey(Keyboard.KEY_H),
new GuiPathingTasks.ClearKeyframeTimeline(),
Task.pressKey(Keyboard.KEY_H),
});
}
}

View File

@@ -0,0 +1,35 @@
package com.replaymod.core.regression;
import com.replaymod.core.AbstractTask;
import com.replaymod.core.CompositeTask;
import com.replaymod.core.Task;
import com.replaymod.replay.overlay.OverlayGui;
import com.replaymod.simplepathing.GuiPathingTasks;
import com.replaymod.simplepathing.gui.GuiEditKeyframe;
/**
* Regression test: #61 NPE when saving in edit keyframe gui of last keyframe
*/
public class RegressionTest61 extends CompositeTask {
public RegressionTest61() {
super(new Task[]{
OverlayGui.whileOpened(
// Place keyframe
Task.click(130, 50),
new GuiPathingTasks.ClickPositionKeyframeButton(),
AbstractTask.create(task -> {
// Double click keyframe
task.click(130, 50);
task.click(130, 50);
task.expectGui(GuiEditKeyframe.Position.class, gui -> {
task.click(gui.saveButton);
task.expectPopupClosed(() -> task.future.set(null));
});
}),
// Place second spectator keyframe
new GuiPathingTasks.ClickPositionKeyframeButton()
),
new GuiPathingTasks.ClearKeyframeTimeline(),
});
}
}

View File

@@ -0,0 +1,28 @@
package com.replaymod.core.regression;
import com.replaymod.core.CompositeTask;
import com.replaymod.core.Task;
import com.replaymod.replay.overlay.OverlayGui;
import com.replaymod.simplepathing.GuiPathingTasks;
/**
* Regression test: #62 Swapping the only two existing keyframes causes NPE
*/
public class RegressionTest62 extends CompositeTask {
public RegressionTest62() {
super(new Task[]{
OverlayGui.whileOpened(
// Place first keyframe
Task.click(130, 50),
new GuiPathingTasks.ClickPositionKeyframeButton(),
// Place second keyframe
Task.click(150, 50),
new GuiPathingTasks.ClickPositionKeyframeButton(),
// Move first keyframe past second keyframe
Task.click(130,50),
Task.drag(170,50)
),
new GuiPathingTasks.ClearKeyframeTimeline()
});
}
}

View File

@@ -0,0 +1,25 @@
package com.replaymod.extra;
import com.replaymod.core.AbstractTask;
import com.replaymod.extras.OpenEyeExtra;
import net.minecraftforge.fml.common.Loader;
import java.io.File;
import java.nio.file.NoSuchFileException;
public class DownloadOpenEye extends AbstractTask {
@Override
protected void init() {
expectGui(OpenEyeExtra.OfferGui.class, offerGui -> {
click(offerGui.yesButton);
expectGuiClosed(20 * 1000, () -> {
File targetFile = new File(mc.mcDataDir, "mods/" + Loader.MC_VERSION + "/OpenEye.jar");
if (!targetFile.exists()) {
future.setException(new NoSuchFileException(targetFile.getAbsolutePath()));
} else {
future.set(null);
}
});
});
}
}

View File

@@ -0,0 +1,14 @@
package com.replaymod.online;
import com.replaymod.core.AbstractTask;
import com.replaymod.online.gui.GuiLoginPrompt;
public class SkipLogin extends AbstractTask {
@Override
protected void init() {
expectGui(GuiLoginPrompt.class, gui -> {
click(gui.cancelButton);
expectGuiClosed(() -> future.set(null));
});
}
}

View File

@@ -0,0 +1,32 @@
package com.replaymod.recording;
import com.replaymod.core.AbstractTask;
import net.minecraft.client.gui.GuiCreateWorld;
import net.minecraft.client.gui.GuiMainMenu;
import net.minecraft.client.gui.GuiWorldSelection;
import net.minecraftforge.client.event.RenderGameOverlayEvent;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
public class CreateSPWorld extends AbstractTask {
@Override
protected void init() {
expectGui(GuiMainMenu.class, mainMenu -> {
click("Singleplayer");
expectGui(GuiWorldSelection.class, selectWorld -> {
click("Create New World");
expectGui(GuiCreateWorld.class, createWorld -> {
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());
});
});
});
}
}

View File

@@ -0,0 +1,36 @@
package com.replaymod.recording;
import com.replaymod.core.AbstractTask;
import net.minecraft.client.gui.GuiIngameMenu;
import net.minecraft.client.gui.GuiMainMenu;
import org.apache.commons.io.filefilter.DirectoryFileFilter;
import java.io.IOException;
public class ExitSPWorld extends AbstractTask {
@Override
protected void init() {
mc.displayInGameMenu();
expectGui(GuiIngameMenu.class, ingameMenu -> {
click("Save and Quit to Title");
expectGui(GuiMainMenu.class, mainMenu -> new Thread(() -> {
try {
while (true) {
String[] dirs = core.getReplayFolder().list(DirectoryFileFilter.DIRECTORY);
if (dirs == null) {
future.setException(new NullPointerException("dirs is null"));
return;
}
if (dirs.length == 0) {
runLater(() -> future.set(null));
return;
}
Thread.sleep(10);
}
} catch (IOException | InterruptedException e) {
future.setException(e);
}
}).start());
});
}
}

View File

@@ -0,0 +1,16 @@
package com.replaymod.replay;
import com.replaymod.core.AbstractTask;
import net.minecraft.client.gui.GuiIngameMenu;
import net.minecraft.client.gui.GuiMainMenu;
public class ExitReplay extends AbstractTask {
@Override
protected void init() {
mc.displayInGameMenu();
expectGui(GuiIngameMenu.class, ingameMenu -> {
click("Exit Replay");
expectGui(GuiMainMenu.class, mainMenu -> future.set(null));
});
}
}

View File

@@ -0,0 +1,32 @@
package com.replaymod.replay;
import com.replaymod.core.AbstractTask;
import com.replaymod.replay.gui.screen.GuiReplayViewer;
import de.johni0702.minecraft.gui.function.Clickable;
import net.minecraftforge.client.event.RenderGameOverlayEvent;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import org.lwjgl.util.Point;
import org.lwjgl.util.ReadableDimension;
public class LoadReplay extends AbstractTask {
@Override
protected void init() {
expectGui(GuiReplayViewer.class, replayViewer -> runLater(() -> {
ReadableDimension size = replayViewer.getMaxSize();
// Select first entry
replayViewer.forEach(Clickable.class).mouseClick(new Point(size.getWidth() / 2, 40), 0);
// Load first replay
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());
}));
}
}

View File

@@ -0,0 +1,15 @@
package com.replaymod.replay;
import com.replaymod.core.AbstractTask;
import com.replaymod.replay.gui.screen.GuiReplayViewer;
import net.minecraft.client.gui.GuiMainMenu;
public class OpenReplayViewer extends AbstractTask {
@Override
protected void init() {
expectGui(GuiMainMenu.class, mainMenu -> {
click("Replay Viewer");
expectGui(GuiReplayViewer.class, replayViewer -> future.set(null));
});
}
}

View File

@@ -0,0 +1,42 @@
package com.replaymod.replay;
import com.replaymod.core.AbstractTask;
import com.replaymod.extras.playeroverview.PlayerOverviewGui;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
import org.lwjgl.input.Keyboard;
import java.util.concurrent.TimeoutException;
public class SpectatePlayer extends AbstractTask {
@Override
protected void init() {
press(Keyboard.KEY_B);
expectGui(PlayerOverviewGui.class, overview -> {
click(overview.getMaxSize().getWidth() / 2 - 60, 60);
press(Keyboard.KEY_ESCAPE);
expectGuiClosed(() -> future.set(null));
});
}
public static class End extends AbstractTask {
private int timeout;
@Override
protected void init() {
runLater(() -> press(Keyboard.KEY_LSHIFT));
}
@SubscribeEvent
public void onTick(TickEvent.RenderTickEvent event) {
if (event.phase != TickEvent.Phase.START) return;
if (timeout++ > 20) {
future.setException(new TimeoutException("Camera hasn't stopped spectating."));
return;
}
if (mc.getRenderViewEntity() == mc.thePlayer) {
future.set(null);
}
}
}
}

View File

@@ -0,0 +1,33 @@
package com.replaymod.replay.overlay;
import com.replaymod.core.AbstractTask;
import com.replaymod.core.CompositeTask;
import com.replaymod.core.Task;
import com.replaymod.replay.gui.overlay.GuiReplayOverlay;
import org.lwjgl.input.Keyboard;
public class OverlayGui {
public static Task whileOpened(Task...tasks) {
Task[] nTasks = new Task[tasks.length + 2];
System.arraycopy(tasks, 0, nTasks, 1, tasks.length);
nTasks[0] = new Open();
nTasks[tasks.length + 1] = new Close();
return new CompositeTask(nTasks);
}
public static class Open extends AbstractTask {
@Override
protected void init() {
press(Keyboard.KEY_T);
expectGui(GuiReplayOverlay.class, done -> future.set(null));
}
}
public static class Close extends AbstractTask {
@Override
protected void init() {
press(Keyboard.KEY_ESCAPE);
expectGuiClosed(() -> future.set(null));
}
}
}

View File

@@ -0,0 +1,35 @@
package com.replaymod.simplepathing;
import com.replaymod.core.AbstractTask;
import com.replaymod.replay.gui.overlay.GuiReplayOverlay;
import com.replaymod.simplepathing.gui.GuiPathing;
import de.johni0702.minecraft.gui.popup.GuiYesNoPopup;
import org.lwjgl.input.Keyboard;
public abstract class GuiPathingTasks extends AbstractTask {
@Override
protected void init() {
expectGui(GuiReplayOverlay.class, ign -> init0(ReplayModSimplePathing.instance.getGuiPathing()));
}
protected abstract void init0(GuiPathing guiPathing);
public static class ClickPositionKeyframeButton extends GuiPathingTasks {
@Override
protected void init0(GuiPathing guiPathing) {
click(guiPathing.positionKeyframeButton);
runLater(() -> future.set(null));
}
}
public static class ClearKeyframeTimeline extends AbstractTask {
@Override
protected void init() {
press(Keyboard.KEY_C);
expectGui(GuiYesNoPopup.class, popup -> {
click(popup.getYesButton());
expectGuiClosed(() -> future.set(null));
});
}
}
}

View File

@@ -2,7 +2,7 @@ package com.replaymod.compat;
import com.replaymod.compat.optifine.DisableFastRender; import com.replaymod.compat.optifine.DisableFastRender;
import com.replaymod.compat.shaders.ShaderBeginRender; import com.replaymod.compat.shaders.ShaderBeginRender;
import net.minecraftforge.fml.common.FMLCommonHandler; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLInitializationEvent; import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.eventhandler.EventBus; import net.minecraftforge.fml.common.eventhandler.EventBus;
@@ -16,7 +16,7 @@ public class ReplayModCompat {
@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 ShaderBeginRender());
bus.register(new DisableFastRender()); bus.register(new DisableFastRender());
} }

View File

@@ -16,7 +16,6 @@ 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>();
@@ -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

@@ -4,7 +4,7 @@ import net.minecraftforge.fml.relauncher.CoreModManager;
import net.minecraftforge.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.MixinEnvironment; import org.spongepowered.asm.mixin.Mixins;
import java.io.File; import java.io.File;
import java.net.URISyntaxException; import java.net.URISyntaxException;
@@ -16,10 +16,10 @@ public class LoadingPlugin implements IFMLLoadingPlugin {
public LoadingPlugin() { public LoadingPlugin() {
MixinBootstrap.init(); MixinBootstrap.init();
MixinEnvironment.getDefaultEnvironment().addConfiguration("mixins.recording.replaymod.json"); Mixins.addConfiguration("mixins.recording.replaymod.json");
MixinEnvironment.getDefaultEnvironment().addConfiguration("mixins.render.replaymod.json"); Mixins.addConfiguration("mixins.render.replaymod.json");
MixinEnvironment.getDefaultEnvironment().addConfiguration("mixins.replay.replaymod.json"); Mixins.addConfiguration("mixins.replay.replaymod.json");
MixinEnvironment.getDefaultEnvironment().addConfiguration("mixins.compat.shaders.replaymod.json"); Mixins.addConfiguration("mixins.compat.shaders.replaymod.json");
CodeSource codeSource = getClass().getProtectionDomain().getCodeSource(); CodeSource codeSource = getClass().getProtectionDomain().getCodeSource();
if (codeSource != null) { if (codeSource != null) {
@@ -29,7 +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());
} }
} catch (URISyntaxException e) { } catch (URISyntaxException e) {
e.printStackTrace(); e.printStackTrace();

View File

@@ -6,7 +6,6 @@ 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.replaystudio.util.I18n; import com.replaymod.replaystudio.util.I18n;
import de.johni0702.minecraft.gui.container.GuiScreen; import de.johni0702.minecraft.gui.container.GuiScreen;
import lombok.Getter; import lombok.Getter;
@@ -14,10 +13,11 @@ import net.minecraft.client.Minecraft;
import net.minecraft.client.settings.GameSettings; 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.*; 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.client.FMLClientHandler;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.Loader; import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.Mod.EventHandler; import net.minecraftforge.fml.common.Mod.EventHandler;
@@ -31,7 +31,6 @@ 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.List; import java.util.List;
import java.util.Queue;
@Mod(modid = ReplayMod.MOD_ID, @Mod(modid = ReplayMod.MOD_ID,
useMetadata = true, useMetadata = true,
@@ -68,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();
@@ -112,7 +109,7 @@ public class ReplayMod {
new MainMenuHandler().register(); new MainMenuHandler().register();
FMLCommonHandler.instance().bus().register(keyBindingRegistry); MinecraftForge.EVENT_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();
@@ -126,150 +123,7 @@ public class ReplayMod {
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
@@ -306,10 +160,8 @@ public class ReplayMod {
} }
public void runLater(Runnable runnable) { public void runLater(Runnable runnable) {
@SuppressWarnings("unchecked")
Queue<ListenableFutureTask> tasks = mc.scheduledTasks;
synchronized (mc.scheduledTasks) { synchronized (mc.scheduledTasks) {
tasks.add(ListenableFutureTask.create(runnable, null)); mc.scheduledTasks.add(ListenableFutureTask.create(runnable, null));
} }
} }
@@ -338,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

@@ -2,8 +2,8 @@ package com.replaymod.core;
import com.replaymod.core.events.SettingsChangedEvent; import com.replaymod.core.events.SettingsChangedEvent;
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 net.minecraftforge.fml.common.FMLCommonHandler;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
@@ -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

@@ -6,8 +6,6 @@ import net.minecraftforge.client.event.GuiScreenEvent;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; 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

@@ -2,7 +2,8 @@ package com.replaymod.core.utils;
import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.WorldRenderer; 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;
@@ -34,14 +35,14 @@ public class OpenGLUtils {
float f5 = 1.0F / textureHeight; float f5 = 1.0F / textureHeight;
Tessellator tessellator = Tessellator.getInstance(); Tessellator tessellator = Tessellator.getInstance();
WorldRenderer worldrenderer = tessellator.getWorldRenderer(); VertexBuffer vertexBuffer = tessellator.getBuffer();
GlStateManager.translate(x+(width/2), y+(width/2), 0); GlStateManager.translate(x+(width/2), y+(width/2), 0);
GlStateManager.rotate(rotation, 0, 0, 1); GlStateManager.rotate(rotation, 0, 0, 1);
worldrenderer.startDrawingQuads(); vertexBuffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
worldrenderer.addVertexWithUV(-width / 2, height / 2, 0.0D, (double) (u * f4), (double) ((v + (float) height) * f5)); vertexBuffer.pos(-width / 2, height / 2, 0.0D).tex((double) (u * f4), (double) ((v + (float) height) * f5)).endVertex();
worldrenderer.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 + (float)width) * f4), (double)((v + (float)height) * f5)).endVertex();
worldrenderer.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 * f5)).endVertex();
worldrenderer.addVertexWithUV(-width/2, -height/2, 0.0D, (double)(u * f4), (double)(v * f5)); vertexBuffer.pos(-width/2, -height/2, 0.0D).tex((double)(u * f4), (double)(v * f5)).endVertex();
tessellator.draw(); tessellator.draw();
GlStateManager.popMatrix(); GlStateManager.popMatrix();

View File

@@ -1,7 +1,7 @@
package com.replaymod.core.utils; package com.replaymod.core.utils;
import net.minecraft.network.PacketBuffer; import net.minecraft.network.PacketBuffer;
import net.minecraft.network.play.server.S3FPacketCustomPayload; import net.minecraft.network.play.server.SPacketCustomPayload;
/** /**
* Restrictions set by the server, * Restrictions set by the server,
@@ -14,7 +14,7 @@ public class Restrictions {
private boolean onlyFirstPerson; private boolean onlyFirstPerson;
private boolean onlyRecordingPlayer; private boolean onlyRecordingPlayer;
public String handle(S3FPacketCustomPayload packet) { public String handle(SPacketCustomPayload packet) {
PacketBuffer buffer = packet.getBufferData(); PacketBuffer buffer = packet.getBufferData();
while (buffer.isReadable()) { while (buffer.isReadable()) {
String name = buffer.readStringFromBuffer(64); String name = buffer.readStringFromBuffer(64);

View File

@@ -2,6 +2,7 @@ package com.replaymod.core.utils;
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 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.AbstractGuiScrollable; import de.johni0702.minecraft.gui.container.AbstractGuiScrollable;
@@ -25,6 +26,7 @@ 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;
import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
@@ -45,6 +47,7 @@ import java.text.SimpleDateFormat;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.UUID; import java.util.UUID;
import java.util.function.Consumer;
import static net.minecraft.client.Minecraft.getMinecraft; import static net.minecraft.client.Minecraft.getMinecraft;
@@ -135,7 +138,7 @@ public class Utils {
} }
public static ResourceLocation getResourceLocationForPlayerUUID(UUID uuid) { public static ResourceLocation getResourceLocationForPlayerUUID(UUID uuid) {
NetworkPlayerInfo info = getMinecraft().getNetHandler().getPlayerInfo(uuid); NetworkPlayerInfo info = getMinecraft().getConnection().getPlayerInfo(uuid);
ResourceLocation skinLocation; ResourceLocation skinLocation;
if (info != null && info.hasLocationSkin()) { if (info != null && info.hasLocationSkin()) {
skinLocation = info.getLocationSkin(); skinLocation = info.getLocationSkin();
@@ -149,6 +152,20 @@ public class Utils {
return Keyboard.isKeyDown(Keyboard.KEY_LCONTROL) || Keyboard.isKeyDown(Keyboard.KEY_RCONTROL); return Keyboard.isKeyDown(Keyboard.KEY_LCONTROL) || Keyboard.isKeyDown(Keyboard.KEY_RCONTROL);
} }
public static <T> void addCallback(ListenableFuture<T> future, Consumer<T> onSuccess, Consumer<Throwable> onFailure) {
Futures.addCallback(future, new FutureCallback<T>() {
@Override
public void onSuccess(@Nullable T result) {
onSuccess.accept(result);
}
@Override
public void onFailure(@Nonnull Throwable t) {
onFailure.accept(t);
}
});
}
public static GuiInfoPopup error(Logger logger, GuiContainer container, CrashReport crashReport, Runnable onClose) { public static GuiInfoPopup error(Logger logger, GuiContainer container, CrashReport crashReport, Runnable onClose) {
// Convert crash report to string // Convert crash report to string
String crashReportStr = crashReport.getCompleteReport(); String crashReportStr = crashReport.getCompleteReport();

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

@@ -59,6 +59,9 @@ public class GuiReplayEditor extends GuiScreen {
public GuiButton currentTabButton; public GuiButton currentTabButton;
public GuiPanel currentTabPanel; public GuiPanel currentTabPanel;
public final GuiLabel warningLabel = new GuiLabel(this).setColor(Colors.RED)
.setI18nText("replaymod.gui.editor.disclaimer");
public final GuiPanel tabButtons = new GuiPanel(this).setLayout(new GridLayout().setSpacingX(5)); public final GuiPanel tabButtons = new GuiPanel(this).setLayout(new GridLayout().setSpacingX(5));
public final List<GuiPanel> tabPanels = new ArrayList<>(); public final List<GuiPanel> tabPanels = new ArrayList<>();
@@ -84,15 +87,18 @@ public class GuiReplayEditor extends GuiScreen {
// Move all inactive panels aside // Move all inactive panels aside
tabPanels.forEach(e -> pos(e, Integer.MIN_VALUE, Integer.MIN_VALUE)); tabPanels.forEach(e -> pos(e, Integer.MIN_VALUE, Integer.MIN_VALUE));
pos(warningLabel, 10, 22);
size(warningLabel, width - 20, 10);
pos(tabButtons, 10, y(warningLabel) + height(warningLabel) + 2);
size(tabButtons, width - 20, 20);
pos(buttonPanel, width - 10 - width(buttonPanel), height - 10 - height(buttonPanel)); pos(buttonPanel, width - 10 - width(buttonPanel), height - 10 - height(buttonPanel));
if (currentTabPanel != null) { if (currentTabPanel != null) {
pos(currentTabPanel, 10, 50); pos(currentTabPanel, 10, y(tabButtons) + height(tabButtons) + 10);
size(currentTabPanel, width - 20, y(buttonPanel) - 10 - y(currentTabPanel)); size(currentTabPanel, width - 20, y(buttonPanel) - 10 - y(currentTabPanel));
} }
pos(tabButtons, 10, 20);
size(tabButtons, width - 20, 20);
} }
}); });
} }

View File

@@ -8,11 +8,8 @@ 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.FMLCommonHandler;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; 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

@@ -9,7 +9,7 @@ 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.fml.common.FMLCommonHandler; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent; import net.minecraftforge.fml.common.gameevent.TickEvent;
import org.lwjgl.input.Keyboard; import org.lwjgl.input.Keyboard;
@@ -40,7 +40,7 @@ public class FullBrightness implements Extra {
} }
}); });
FMLCommonHandler.instance().bus().register(this); MinecraftForge.EVENT_BUS.register(this);
} }
@SubscribeEvent @SubscribeEvent

View File

@@ -17,7 +17,7 @@ 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.fml.common.FMLCommonHandler; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; 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;
@@ -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

@@ -1,106 +1,110 @@
package com.replaymod.extras; package com.replaymod.extras;
import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.replaymod.core.ReplayMod; import com.replaymod.core.ReplayMod;
import com.replaymod.online.ReplayModOnline;
import com.replaymod.online.api.ApiClient;
import com.replaymod.online.api.ApiException;
import lombok.RequiredArgsConstructor;
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.lang3.StringEscapeUtils; import org.apache.commons.io.IOUtils;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.ConnectException; import java.net.URL;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import static com.replaymod.extras.ReplayModExtras.LOGGER;
public class LocalizationExtra implements Extra { public class LocalizationExtra implements Extra {
private ReplayModOnline module; private static final String ZIP_FILE_URL = "https://github.com/ReplayMod/Translations/archive/master.zip";
private static final String LANG_PREFIX = "Translations-master/";
@Override @Override
public void register(ReplayMod mod) throws Exception { public void register(ReplayMod mod) throws Exception {
this.module = ReplayModOnline.instance;
final Minecraft mc = mod.getMinecraft(); final Minecraft mc = mod.getMinecraft();
Thread localizedResourcePackLoader = new Thread(new Runnable() { if (Boolean.parseBoolean(System.getProperty("replaymod.offline", "false"))) {
@Override return;
public void run() { }
try { Thread localizedResourcePackLoader = new Thread(() -> {
try {
// Download zip of lang files
LOGGER.debug("Downloading languages from {}", ZIP_FILE_URL);
Map<String, byte[]> languages = new HashMap<>();
try (InputStream urlIn = new URL(ZIP_FILE_URL).openStream();
ZipInputStream in = new ZipInputStream(urlIn)) {
ZipEntry entry;
while ((entry = in.getNextEntry()) != null) {
String name = entry.getName();
if (!name.startsWith(LANG_PREFIX) || !name.endsWith(".lang")) {
continue;
}
name = name.substring(LANG_PREFIX.length());
languages.put(name, IOUtils.toByteArray(in));
LOGGER.debug("Added language file {}", name);
}
}
LOGGER.debug("Downloaded {} languages", languages.size());
// Add lang files as resource pack
mc.addScheduledTask(() -> {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
List<IResourcePack> defaultResourcePacks = mc.defaultResourcePacks; List<IResourcePack> defaultResourcePacks = mc.defaultResourcePacks;
defaultResourcePacks.add(new LocalizedResourcePack(module.getApiClient())); defaultResourcePacks.add(new LocalizedResourcePack(languages));
mc.addScheduledTask(new Runnable() { mc.getLanguageManager().onResourceManagerReload(mc.getResourceManager());
@Override LOGGER.debug("Added language files to resource packs and reloaded LanguageManager");
public void run() { });
mc.refreshResources(); } catch (Throwable t) {
} LOGGER.error("Loading localized resource pack:", t);
});
} catch(Exception e) {
e.printStackTrace();
}
} }
}, "localizedResourcePackLoader"); }, "localizedResourcePackLoader");
localizedResourcePackLoader.setDaemon(true);
localizedResourcePackLoader.start(); localizedResourcePackLoader.start();
} }
@RequiredArgsConstructor
public static class LocalizedResourcePack implements IResourcePack { public static class LocalizedResourcePack implements IResourcePack {
private final ApiClient apiClient; private final Pattern LANG_PATTERN = Pattern.compile("^lang/([.+].lang)$");
private Map<String, String> availableLanguages = new HashMap<>(); private final Map<String, byte[]> languages;
private boolean websiteAvailable = true;
public LocalizedResourcePack(Map<String, byte[]> languages) {
this.languages = languages;
}
@Override @Override
public InputStream getInputStream(ResourceLocation loc) { public InputStream getInputStream(ResourceLocation loc) {
if(!loc.getResourcePath().endsWith(".lang")) return null; if (!"replaymod".equals(loc.getResourceDomain())) return null;
String langcode = loc.getResourcePath().split("/")[1].split("\\.")[0]; Matcher matcher = LANG_PATTERN.matcher(loc.getResourcePath());
if(availableLanguages.containsKey(langcode)) return new ByteArrayInputStream(availableLanguages.get(langcode).getBytes(Charsets.UTF_8)); if (matcher.matches()) {
byte[] bytes = languages.get(matcher.group());
if (bytes != null) {
return new ByteArrayInputStream(bytes);
}
}
return null; return null;
} }
@Override @Override
public boolean resourceExists(ResourceLocation loc) { public boolean resourceExists(ResourceLocation loc) {
if(!(loc.getResourcePath().endsWith(".lang"))) return false; // Assumes that getInputStream returns a ByteArrayInputStream that doesn't need to be closed
String langcode = loc.getResourcePath().split("/")[1].split("\\.")[0]; return getInputStream(loc) != null;
if(availableLanguages.containsKey(langcode)) return true;
if(!websiteAvailable) return false;
try {
if (Boolean.parseBoolean(System.getProperty("replaymod.offline", "false"))) {
return false;
}
String lang = apiClient.getTranslation(langcode);
String prop = StringEscapeUtils.unescapeHtml4(lang);
availableLanguages.put(langcode, prop);
return true;
} catch (ApiException e) {
if (e.getError() == null || e.getError().getId() != 16) { // This language has not been translated
e.printStackTrace();
}
} catch(ConnectException ce) {
websiteAvailable = false;
ce.printStackTrace();
} catch(Exception e) {
e.printStackTrace();
}
return false;
} }
@Override @Override
public Set getResourceDomains() { public Set<String> getResourceDomains() {
return ImmutableSet.of("minecraft", "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

@@ -43,7 +43,7 @@ public class OpenEyeExtra implements Extra {
} }
} }
private class OfferGui extends AbstractGuiScreen<OfferGui> { public class OfferGui extends AbstractGuiScreen<OfferGui> {
public final GuiScreen parent; public final GuiScreen parent;
public final GuiPanel textPanel = new GuiPanel().setLayout(new VerticalLayout().setSpacing(3)) public final GuiPanel textPanel = new GuiPanel().setLayout(new VerticalLayout().setSpacing(3))
.addElements(new VerticalLayout.Data(0.5), .addElements(new VerticalLayout.Data(0.5),
@@ -69,7 +69,7 @@ public class OpenEyeExtra implements Extra {
GuiPopup popup = new GuiPopup(OfferGui.this); GuiPopup popup = new GuiPopup(OfferGui.this);
new Thread(() -> { new Thread(() -> {
try { try {
File targetFile = new File("mods/" + Loader.MC_VERSION, "OpenEye.jar"); File targetFile = new File(mod.getMinecraft().mcDataDir, "mods/" + Loader.MC_VERSION + "/OpenEye.jar");
FileUtils.forceMkdir(targetFile.getParentFile()); FileUtils.forceMkdir(targetFile.getParentFile());
HttpsURLConnection connection = (HttpsURLConnection) new URL(DOWNLOAD_URL).openConnection(); HttpsURLConnection connection = (HttpsURLConnection) new URL(DOWNLOAD_URL).openConnection();
@@ -107,7 +107,7 @@ public class OpenEyeExtra implements Extra {
} }
} }
private static final class GuiPopup extends AbstractGuiPopup<GuiPopup> { public static final class GuiPopup extends AbstractGuiPopup<GuiPopup> {
GuiPopup(GuiContainer container) { GuiPopup(GuiContainer container) {
super(container); super(container);
popup.addElements(null, new GuiIndicator().setColor(Colors.BLACK)); popup.addElements(null, new GuiIndicator().setColor(Colors.BLACK));

View File

@@ -32,11 +32,11 @@ public class ReplayModExtras {
OpenEyeExtra.class OpenEyeExtra.class
); );
private Logger logger; public static Logger LOGGER;
@Mod.EventHandler @Mod.EventHandler
public void preInit(FMLPreInitializationEvent event) { public void preInit(FMLPreInitializationEvent event) {
logger = event.getModLog(); LOGGER = event.getModLog();
} }
@Mod.EventHandler @Mod.EventHandler
@@ -46,7 +46,7 @@ public class ReplayModExtras {
Extra extra = cls.newInstance(); Extra extra = cls.newInstance();
extra.register(ReplayMod.instance); extra.register(ReplayMod.instance);
} catch (Throwable t) { } catch (Throwable t) {
logger.warn("Failed to load extra " + cls.getName() + ": ", t); LOGGER.warn("Failed to load extra " + cls.getName() + ": ", t);
} }
} }
} }

View File

@@ -15,7 +15,6 @@ 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.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.eventhandler.SubscribeEvent;
import org.lwjgl.input.Keyboard; import org.lwjgl.input.Keyboard;
@@ -58,7 +57,6 @@ 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(); RenderManager renderManager = mod.getMinecraft().getRenderManager();

View File

@@ -4,10 +4,15 @@ import com.replaymod.core.utils.Utils;
import com.replaymod.replay.ReplayModReplay; import com.replaymod.replay.ReplayModReplay;
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.*; import de.johni0702.minecraft.gui.container.GuiClickable;
import de.johni0702.minecraft.gui.container.GuiContainer;
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.GuiCheckbox; import de.johni0702.minecraft.gui.element.GuiCheckbox;
import de.johni0702.minecraft.gui.element.GuiImage; import de.johni0702.minecraft.gui.element.GuiImage;
import de.johni0702.minecraft.gui.element.GuiLabel; import de.johni0702.minecraft.gui.element.GuiLabel;
import de.johni0702.minecraft.gui.element.GuiTooltip;
import de.johni0702.minecraft.gui.element.IGuiCheckbox; import de.johni0702.minecraft.gui.element.IGuiCheckbox;
import de.johni0702.minecraft.gui.function.Closeable; import de.johni0702.minecraft.gui.function.Closeable;
import de.johni0702.minecraft.gui.layout.CustomLayout; import de.johni0702.minecraft.gui.layout.CustomLayout;
@@ -16,7 +21,8 @@ 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.entity.player.EnumPlayerModelParts; import net.minecraft.entity.player.EnumPlayerModelParts;
import net.minecraft.potion.Potion; 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;
@@ -36,21 +42,22 @@ public class PlayerOverviewGui extends GuiScreen implements Closeable {
public final GuiVerticalList playersScrollable = new GuiVerticalList(contentPanel) public final GuiVerticalList playersScrollable = new GuiVerticalList(contentPanel)
.setDrawSlider(true).setDrawShadow(true); .setDrawSlider(true).setDrawShadow(true);
public final GuiCheckbox saveCheckbox = new GuiCheckbox(contentPanel) public final GuiCheckbox saveCheckbox = new GuiCheckbox(contentPanel)
.setTooltip(new GuiTooltip().setI18nText("replaymod.gui.playeroverview.remembersettings.description"))
.setI18nLabel("replaymod.gui.playeroverview.remembersettings"); .setI18nLabel("replaymod.gui.playeroverview.remembersettings");
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.create(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); }.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.create(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); }.setLabel("").setChecked(false).setTooltip(new GuiTooltip().setI18nText("replaymod.gui.playeroverview.hideall"));
{ {
setBackground(Background.NONE); setBackground(Background.NONE);
@@ -90,7 +97,7 @@ 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);
if (p.func_175148_a(EnumPlayerModelParts.HAT)) { if (p.isWearing(EnumPlayerModelParts.HAT)) {
renderer.drawTexturedRect(0, 0, 40, 8, size.getWidth(), size.getHeight(), 8, 8, 64, 64); renderer.drawTexturedRect(0, 0, 40, 8, size.getWidth(), size.getHeight(), 8, 8, 64, 64);
} }
} }
@@ -139,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> {

View File

@@ -1,9 +1,9 @@
package com.replaymod.extras.playeroverview; package com.replaymod.extras.playeroverview;
import net.minecraft.client.entity.AbstractClientPlayer;
import net.minecraft.client.renderer.culling.ICamera; import net.minecraft.client.renderer.culling.ICamera;
import net.minecraft.client.renderer.entity.RenderManager; import net.minecraft.client.renderer.entity.RenderManager;
import net.minecraft.client.renderer.entity.RenderPlayer; import net.minecraft.client.renderer.entity.RenderPlayer;
import net.minecraft.entity.Entity;
public class PlayerRenderHook extends RenderPlayer { public class PlayerRenderHook extends RenderPlayer {
private final PlayerOverview extra; private final PlayerOverview extra;
@@ -14,7 +14,7 @@ public class PlayerRenderHook extends RenderPlayer {
} }
@Override @Override
public boolean shouldRender(Entity entity, ICamera camera, double camX, double camY, double camZ) { 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); return !extra.isHidden(entity.getUniqueID()) && super.shouldRender(entity, camera, camX, camY, camZ);
} }
} }

View File

@@ -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

@@ -12,8 +12,8 @@ import com.replaymod.replaystudio.replay.ReplayFile;
import com.replaymod.replaystudio.replay.ZipReplayFile; import com.replaymod.replaystudio.replay.ZipReplayFile;
import com.replaymod.replaystudio.studio.ReplayStudio; import com.replaymod.replaystudio.studio.ReplayStudio;
import de.johni0702.minecraft.gui.container.GuiScreen; import de.johni0702.minecraft.gui.container.GuiScreen;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.config.Configuration; import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLInitializationEvent; import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPostInitializationEvent; import net.minecraftforge.fml.common.event.FMLPostInitializationEvent;
@@ -74,7 +74,7 @@ public class ReplayModOnline {
} }
new GuiHandler(this).register(); new GuiHandler(this).register();
FMLCommonHandler.instance().bus().register(this); MinecraftForge.EVENT_BUS.register(this);
} }
@Mod.EventHandler @Mod.EventHandler
@@ -82,7 +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()) {
new GuiLoginPrompt(apiClient, null, null, false).display(); core.runLater(() -> new GuiLoginPrompt(apiClient, GuiScreen.wrap(getMinecraft().currentScreen), null, false).display());
} }
} }
} }

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

@@ -21,7 +21,7 @@ public class GuiLoginPrompt extends AbstractGuiScreen<GuiLoginPrompt> {
private GuiLabel noAccountLabel = new GuiLabel(this).setI18nText("replaymod.gui.login.noacc"); private GuiLabel noAccountLabel = new GuiLabel(this).setI18nText("replaymod.gui.login.noacc");
private GuiLabel statusLabel = new GuiLabel(this); private GuiLabel statusLabel = new GuiLabel(this);
private GuiButton loginButton = new GuiButton(this).setI18nLabel("replaymod.gui.login").setSize(150, 20).setEnabled(false); private GuiButton loginButton = new GuiButton(this).setI18nLabel("replaymod.gui.login").setSize(150, 20).setEnabled(false);
private GuiButton cancelButton = new GuiButton(this).setI18nLabel("replaymod.gui.cancel").setSize(150, 20); public GuiButton cancelButton = new GuiButton(this).setI18nLabel("replaymod.gui.cancel").setSize(150, 20);
private GuiButton registerButton = new GuiButton(this).setI18nLabel("replaymod.gui.register").setSize(150, 20); private GuiButton registerButton = new GuiButton(this).setI18nLabel("replaymod.gui.register").setSize(150, 20);
private GuiTextField username = new GuiTextField(this).setSize(145, 20).setMaxLength(16).setFocused(true); private GuiTextField username = new GuiTextField(this).setSize(145, 20).setMaxLength(16).setFocused(true);
private GuiPasswordField password = new GuiPasswordField(this).setSize(145, 20) private GuiPasswordField password = new GuiPasswordField(this).setSize(145, 20)

View File

@@ -28,6 +28,7 @@ import de.johni0702.minecraft.gui.container.GuiScreen;
import de.johni0702.minecraft.gui.element.GuiButton; import de.johni0702.minecraft.gui.element.GuiButton;
import de.johni0702.minecraft.gui.element.GuiImage; import de.johni0702.minecraft.gui.element.GuiImage;
import de.johni0702.minecraft.gui.element.GuiLabel; import de.johni0702.minecraft.gui.element.GuiLabel;
import de.johni0702.minecraft.gui.element.GuiTooltip;
import de.johni0702.minecraft.gui.element.IGuiButton; import de.johni0702.minecraft.gui.element.IGuiButton;
import de.johni0702.minecraft.gui.element.advanced.GuiResourceLoadingList; import de.johni0702.minecraft.gui.element.advanced.GuiResourceLoadingList;
import de.johni0702.minecraft.gui.layout.CustomLayout; import de.johni0702.minecraft.gui.layout.CustomLayout;
@@ -109,6 +110,7 @@ public class GuiReplayCenter extends GuiScreen {
public void run() { public void run() {
GuiReplayEntry selected = list.getSelected(); GuiReplayEntry selected = list.getSelected();
replayButtonPanel.forEach(IGuiButton.class).setEnabled(selected != null); replayButtonPanel.forEach(IGuiButton.class).setEnabled(selected != null);
replayButtonPanel.forEach(IGuiButton.class).setTooltip(null);
if (selected != null) { if (selected != null) {
int replayId = selected.fileInfo.getId(); int replayId = selected.fileInfo.getId();
boolean favorited = favoritedReplays.contains(replayId); boolean favorited = favoritedReplays.contains(replayId);
@@ -123,10 +125,22 @@ public class GuiReplayCenter extends GuiScreen {
favoriteButton.setI18nLabel("replaymod.gui.center." + (favorited ? "unfavorite" : "favorite")); favoriteButton.setI18nLabel("replaymod.gui.center." + (favorited ? "unfavorite" : "favorite"));
// Only allow button usage for either unfavorite or favorite after they've actually downloaded it // Only allow button usage for either unfavorite or favorite after they've actually downloaded it
favoriteButton.setEnabled(favorited || selected.downloaded); favoriteButton.setEnabled(favorited || selected.downloaded);
if (favoriteButton.isEnabled()) {
favoriteButton.setTooltip(null);
} else {
favoriteButton.setTooltip(new GuiTooltip().setI18nText("replaymod.gui.center.downloadrequired"));
}
// Similar for like/dislike buttons // Similar for like/dislike buttons
likeButton.setEnabled(selected.downloaded); likeButton.setEnabled(selected.downloaded);
dislikeButton.setEnabled(selected.downloaded); dislikeButton.setEnabled(selected.downloaded);
if (likeButton.isEnabled()) {
likeButton.setTooltip(null);
dislikeButton.setTooltip(null);
} else {
likeButton.setTooltip(new GuiTooltip().setI18nText("replaymod.gui.center.downloadrequired"));
dislikeButton.setTooltip(new GuiTooltip().setI18nText("replaymod.gui.center.downloadrequired"));
}
likeButton.setI18nLabel("replaymod.gui." + (liked ? "removelike" : "like")); likeButton.setI18nLabel("replaymod.gui." + (liked ? "removelike" : "like"));
dislikeButton.setI18nLabel("replaymod.gui." + (disliked ? "removedislike" : "dislike")); dislikeButton.setI18nLabel("replaymod.gui." + (disliked ? "removedislike" : "dislike"));
} }

View File

@@ -17,7 +17,12 @@ import com.replaymod.replaystudio.studio.ReplayStudio;
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.GuiScreen; import de.johni0702.minecraft.gui.container.GuiScreen;
import de.johni0702.minecraft.gui.element.*; import de.johni0702.minecraft.gui.element.GuiButton;
import de.johni0702.minecraft.gui.element.GuiCheckbox;
import de.johni0702.minecraft.gui.element.GuiImage;
import de.johni0702.minecraft.gui.element.GuiLabel;
import de.johni0702.minecraft.gui.element.GuiTextField;
import de.johni0702.minecraft.gui.element.GuiTooltip;
import de.johni0702.minecraft.gui.element.advanced.GuiDropdownMenu; import de.johni0702.minecraft.gui.element.advanced.GuiDropdownMenu;
import de.johni0702.minecraft.gui.element.advanced.GuiProgressBar; import de.johni0702.minecraft.gui.element.advanced.GuiProgressBar;
import de.johni0702.minecraft.gui.element.advanced.GuiTextArea; import de.johni0702.minecraft.gui.element.advanced.GuiTextArea;
@@ -234,6 +239,8 @@ public class GuiUploadReplay extends GuiScreen {
}); });
validateInputs(); validateInputs();
name.onTextChanged(s -> validateInputs());
tags.onTextChanged(s -> validateInputs());
} }
public void validateInputs() { public void validateInputs() {

View File

@@ -14,11 +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.FMLCommonHandler;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; 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

@@ -4,6 +4,7 @@ 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.SettableFuture; import com.google.common.util.concurrent.SettableFuture;
import com.replaymod.core.ReplayMod; import com.replaymod.core.ReplayMod;
import com.replaymod.core.utils.Utils;
import com.replaymod.replay.ReplayModReplay; import com.replaymod.replay.ReplayModReplay;
import com.replaymod.replaystudio.pathing.PathingRegistry; import com.replaymod.replaystudio.pathing.PathingRegistry;
import com.replaymod.replaystudio.pathing.path.Path; import com.replaymod.replaystudio.pathing.path.Path;
@@ -11,7 +12,11 @@ import com.replaymod.replaystudio.pathing.path.Timeline;
import com.replaymod.replaystudio.replay.ReplayFile; import com.replaymod.replaystudio.replay.ReplayFile;
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.*; import de.johni0702.minecraft.gui.container.AbstractGuiClickableContainer;
import de.johni0702.minecraft.gui.container.GuiContainer;
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.GuiButton;
import de.johni0702.minecraft.gui.element.GuiLabel; import de.johni0702.minecraft.gui.element.GuiLabel;
import de.johni0702.minecraft.gui.element.GuiTextField; import de.johni0702.minecraft.gui.element.GuiTextField;
@@ -41,9 +46,16 @@ public class GuiKeyframeRepository extends GuiScreen implements Closeable {
public final GuiButton overwriteButton = new GuiButton(buttonPanel).onClick(new Runnable() { public final GuiButton overwriteButton = new GuiButton(buttonPanel).onClick(new Runnable() {
@Override @Override
public void run() { public void run() {
timelines.put(selectedEntry.name, currentTimeline); GuiYesNoPopup popup = GuiYesNoPopup.open(GuiKeyframeRepository.this,
overwriteButton.setDisabled(); new GuiLabel().setI18nText("replaymod.gui.keyframerepo.overwrite").setColor(Colors.BLACK)
save(); ).setYesI18nLabel("gui.yes").setNoI18nLabel("gui.no");
Utils.addCallback(popup.getFuture(), doIt -> {
if (doIt) {
timelines.put(selectedEntry.name, currentTimeline);
overwriteButton.setDisabled();
save();
}
}, Throwable::printStackTrace);
} }
}).setSize(75, 20).setI18nLabel("replaymod.gui.overwrite").setDisabled(); }).setSize(75, 20).setI18nLabel("replaymod.gui.overwrite").setDisabled();
public final GuiButton saveAsButton = new GuiButton(buttonPanel).onClick(new Runnable() { public final GuiButton saveAsButton = new GuiButton(buttonPanel).onClick(new Runnable() {

View File

@@ -11,7 +11,7 @@ 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 net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraftforge.fml.common.FMLCommonHandler; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -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

@@ -2,7 +2,7 @@ package com.replaymod.pathing.player;
import com.replaymod.core.utils.WrappedTimer; import com.replaymod.core.utils.WrappedTimer;
import net.minecraft.util.Timer; import net.minecraft.util.Timer;
import net.minecraftforge.fml.common.FMLCommonHandler; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.eventhandler.Event; import net.minecraftforge.fml.common.eventhandler.Event;
/** /**
@@ -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

@@ -4,7 +4,9 @@ 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 net.minecraftforge.fml.common.FMLCommonHandler; import io.netty.channel.ChannelDuplexHandler;
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.Mod;
import net.minecraftforge.fml.common.event.FMLInitializationEvent; import net.minecraftforge.fml.common.event.FMLInitializationEvent;
@@ -51,12 +53,15 @@ 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.newSimpleChannel(Restrictions.PLUGIN_CHANNEL); NetworkRegistry.INSTANCE.newChannel(Restrictions.PLUGIN_CHANNEL, new RestrictionsChannelHandler());
} }
@ChannelHandler.Sharable
private static class RestrictionsChannelHandler extends ChannelDuplexHandler {}
public void initiateRecording(NetworkManager networkManager) { public void initiateRecording(NetworkManager networkManager) {
connectionEventHandler.onConnectedToServerEvent(networkManager); connectionEventHandler.onConnectedToServerEvent(networkManager);
} }

View File

@@ -40,7 +40,7 @@ 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);

View File

@@ -1,24 +1,22 @@
package com.replaymod.recording.handler; package com.replaymod.recording.handler;
import com.replaymod.recording.packet.PacketListener; import com.replaymod.recording.packet.PacketListener;
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.item.Item; 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.*; import net.minecraft.network.play.server.*;
import net.minecraft.network.play.server.S14PacketEntity.S17PacketEntityLookMove;
import net.minecraft.server.integrated.IntegratedServer; import net.minecraft.server.integrated.IntegratedServer;
import net.minecraft.util.BlockPos; import net.minecraft.util.EnumHand;
import net.minecraft.util.MathHelper; 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.FMLCommonHandler;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.PlayerEvent.ItemPickupEvent; import net.minecraftforge.fml.common.gameevent.PlayerEvent.ItemPickupEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent; import net.minecraftforge.fml.common.gameevent.TickEvent;
@@ -32,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) {
@@ -59,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();
} }
@@ -67,39 +65,28 @@ 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,
double x, double y, double z, float volume, float pitch) {
try { try {
S0CPacketSpawnPlayer packet = new S0CPacketSpawnPlayer(); // Send to all other players in ServerWorldEventHandler#playSoundToAllNearExcept
packetListener.save(new SPacketSoundEffect(sound, category, x, y, z, volume, pitch));
ByteBuf bb = Unpooled.buffer(); } catch(Exception e) {
PacketBuffer pb = new PacketBuffer(bb); e.printStackTrace();
}
pb.writeVarIntToBuffer(player.getEntityId()); }
pb.writeUuid(EntityPlayer.getUUID(player.getGameProfile()));
public void onClientEffect(int type, BlockPos pos, int data) {
pb.writeInt(MathHelper.floor_double(player.posX * 32.0D)); try {
pb.writeInt(MathHelper.floor_double(player.posY * 32.0D)); // Send to all other players in ServerWorldEventHandler#playEvent
pb.writeInt(MathHelper.floor_double(player.posZ * 32.0D)); packetListener.save(new SPacketEffect(type, pos, data, false));
pb.writeByte((byte) ((int) (player.rotationYaw * 256.0F / 360.0F)));
pb.writeByte((byte) ((int) (player.rotationPitch * 256.0F / 360.0F)));
ItemStack itemstack = player.inventory.getCurrentItem();
pb.writeShort(itemstack == null ? 0 : Item.getIdFromItem(itemstack.getItem()));
player.getDataWatcher().writeTo(pb);
packet.readPacketData(pb);
return packet;
} catch(Exception e) { } catch(Exception e) {
e.printStackTrace(); e.printStackTrace();
return null;
} }
} }
@@ -131,19 +118,14 @@ public class RecordingEventHandler {
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.posY * 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, e.player.onGround);
} 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, e.player.onGround); newYaw, newPitch, e.player.onGround);
} }
@@ -153,37 +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)) {
S19PacketEntityHeadLook head = new S19PacketEntityHeadLook(); packetListener.save(new SPacketEntityHeadLook(e.player, (byte) rotationYawHead));
ByteBuf bb1 = Unpooled.buffer();
PacketBuffer pb1 = new PacketBuffer(bb1);
pb1.writeVarIntToBuffer(e.player.getEntityId());
pb1.writeByte(rotationYawHead);
head.readPacketData(pb1);
packetListener.save(head);
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) {
S0BPacketAnimation pac = new S0BPacketAnimation(); packetListener.save(new SPacketAnimation(e.player, e.player.swingingHand == EnumHand.MAIN_HAND ? 0 : 3));
ByteBuf bb = Unpooled.buffer();
PacketBuffer pb = new PacketBuffer(bb);
pb.writeVarIntToBuffer(e.player.getEntityId());
pb.writeByte(0);
pac.readPacketData(pb);
packetListener.save(pac);
} }
/* /*
@@ -207,77 +168,64 @@ 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) {
S0BPacketAnimation pac = new S0BPacketAnimation(); packetListener.save(new SPacketAnimation(e.player, 2));
ByteBuf bb = Unpooled.buffer();
PacketBuffer pb = new PacketBuffer(bb);
pb.writeVarIntToBuffer(e.player.getEntityId());
pb.writeByte(2);
pac.readPacketData(pb);
packetListener.save(pac);
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();
} }
@@ -286,27 +234,7 @@ public class RecordingEventHandler {
@SubscribeEvent @SubscribeEvent
public void onPickupItem(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) {
e.printStackTrace();
}
}
@SubscribeEvent
public void onStartEating(PlayerUseItemEvent.Start event) {
try {
if(!event.entityPlayer.isEating()) return;
S0BPacketAnimation packet = new S0BPacketAnimation();
ByteBuf bb = Unpooled.buffer();
PacketBuffer pb = new PacketBuffer(bb);
pb.writeVarIntToBuffer(event.entityPlayer.getEntityId());
pb.writeByte(3);
packet.readPacketData(pb);
packetListener.save(packet);
} catch(Exception e) { } catch(Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
@@ -315,21 +243,11 @@ public class RecordingEventHandler {
@SubscribeEvent @SubscribeEvent
public void onSleep(PlayerSleepInBedEvent event) { public void onSleep(PlayerSleepInBedEvent event) {
try { try {
if(event.entityPlayer != mc.thePlayer) { if (event.getEntityPlayer() != mc.thePlayer) {
return; return;
} }
S0APacketUseBed pub = new S0APacketUseBed(); packetListener.save(new SPacketUseBed(event.getEntityPlayer(), event.getPos()));
ByteBuf buf = Unpooled.buffer();
PacketBuffer pbuf = new PacketBuffer(buf);
pbuf.writeVarIntToBuffer(event.entityPlayer.getEntityId());
pbuf.writeBlockPos(event.pos);
pub.readPacketData(pbuf);
packetListener.save(pub);
wasSleeping = true; wasSleeping = true;
@@ -341,24 +259,13 @@ public class RecordingEventHandler {
@SubscribeEvent @SubscribeEvent
public void enterMinecart(MinecartInteractEvent event) { public void enterMinecart(MinecartInteractEvent event) {
try { try {
if(event.player != mc.thePlayer) { if(event.getEntity() != mc.thePlayer) {
return; return;
} }
S1BPacketEntityAttach pea = new S1BPacketEntityAttach(); packetListener.save(new SPacketEntityAttach(event.getPlayer(), event.getMinecart()));
ByteBuf buf = Unpooled.buffer(); lastRiding = event.getMinecart().getEntityId();
PacketBuffer pbuf = new PacketBuffer(buf);
pbuf.writeInt(event.player.getEntityId());
pbuf.writeInt(event.minecart.getEntityId());
pbuf.writeBoolean(false);
pea.readPacketData(pbuf);
packetListener.save(pea);
lastRiding = event.minecart.getEntityId();
} catch(Exception e) { } catch(Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
@@ -367,7 +274,7 @@ public class RecordingEventHandler {
public void onBlockBreakAnim(int breakerId, BlockPos pos, 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, pos, progress)); packetListener.save(new SPacketBlockBreakAnim(breakerId, pos, progress));
} }
} }

View File

@@ -4,15 +4,14 @@ 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.client.network.NetworkPlayerInfo; 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.S38PacketPlayerListItem; 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.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
@@ -37,18 +36,16 @@ public abstract class MixinNetHandlerPlayClient {
* @param ci Callback info * @param ci Callback info
*/ */
@Inject(method = "handlePlayerListItem", at=@At("HEAD")) @Inject(method = "handlePlayerListItem", at=@At("HEAD"))
public void recordOwnJoin(S38PacketPlayerListItem packet, CallbackInfo ci) { public void recordOwnJoin(SPacketPlayerListItem packet, CallbackInfo ci) {
if (gameController.thePlayer == null) return; if (gameController.thePlayer == null) return;
RecordingEventHandler handler = getRecordingEventHandler(); RecordingEventHandler handler = getRecordingEventHandler();
if (handler != null && packet.func_179768_b() == S38PacketPlayerListItem.Action.ADD_PLAYER) { if (handler != null && packet.getAction() == SPacketPlayerListItem.Action.ADD_PLAYER) {
@SuppressWarnings("unchecked") for (SPacketPlayerListItem.AddPlayerData data : packet.getEntries()) {
List<S38PacketPlayerListItem.AddPlayerData> dataList = packet.func_179767_a(); if (data.getProfile() == null || data.getProfile().getId() == null) continue;
for (S38PacketPlayerListItem.AddPlayerData data : dataList) {
if (data.func_179962_a() == null || data.func_179962_a().getId() == null) continue;
// Only add spawn packet for our own player and only if he isn't known yet // Only add spawn packet for our own player and only if he isn't known yet
if (data.func_179962_a().getId().equals(Minecraft.getMinecraft().thePlayer.getGameProfile().getId()) if (data.getProfile().getId().equals(Minecraft.getMinecraft().thePlayer.getGameProfile().getId())
&& !playerInfoMap.containsKey(data.func_179962_a().getId())) { && !playerInfoMap.containsKey(data.getProfile().getId())) {
handler.onPlayerJoin(); handler.onPlayerJoin();
} }
} }
@@ -58,12 +55,12 @@ public abstract class MixinNetHandlerPlayClient {
/** /**
* 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(S38PacketPlayerListItem, 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

@@ -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,7 +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.BlockPos; 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;

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

@@ -9,21 +9,24 @@ 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.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 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;
@@ -84,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).func_179819_c(); 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()]));
@@ -151,16 +154,16 @@ 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 S48PacketResourcePackSend) { if (packet instanceof SPacketResourcePackSend) {
save(resourcePackRecorder.handleResourcePack((S48PacketResourcePackSend) packet)); save(resourcePackRecorder.handleResourcePack((SPacketResourcePackSend) packet));
return; return;
} }
@@ -173,10 +176,10 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
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.getChannelName())) { 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);
} }
} }
@@ -189,27 +192,31 @@ 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);
} }
} }
} }

View File

@@ -9,15 +9,14 @@ 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.ServerList; import net.minecraft.client.multiplayer.ServerList;
import net.minecraft.client.network.NetHandlerPlayClient; 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.NetworkManager; import net.minecraft.network.NetworkManager;
import net.minecraft.network.play.client.C19PacketResourcePackStatus; import net.minecraft.network.play.client.CPacketResourcePackStatus;
import net.minecraft.network.play.server.S48PacketResourcePackSend; import net.minecraft.network.play.server.SPacketResourcePackSend;
import net.minecraft.util.HttpUtil; import net.minecraft.util.HttpUtil;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
@@ -75,12 +74,12 @@ public class ResourcePackRecorder {
} }
} }
public synchronized S48PacketResourcePackSend handleResourcePack(S48PacketResourcePackSend packet) { public synchronized SPacketResourcePackSend handleResourcePack(SPacketResourcePackSend packet) {
final int requestId = nextRequestId++; final int requestId = nextRequestId++;
final NetHandlerPlayClient netHandler = mc.getNetHandler(); final NetHandlerPlayClient netHandler = mc.getConnection();
final NetworkManager netManager = netHandler.getNetworkManager(); final NetworkManager netManager = netHandler.getNetworkManager();
final String url = packet.func_179783_a(); final String url = packet.getURL();
final String hash = packet.func_179784_b(); final String hash = packet.getHash();
if (url.startsWith("level://")) { if (url.startsWith("level://")) {
String levelName = url.substring("level://".length()); String levelName = url.substring("level://".length());
@@ -88,65 +87,60 @@ public class ResourcePackRecorder {
final File levelDir = new File(savesDir, levelName); final File levelDir = new File(savesDir, levelName);
if (levelDir.isFile()) { if (levelDir.isFile()) {
netManager.sendPacket(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.ACCEPTED)); netManager.sendPacket(new CPacketResourcePackStatus(hash, CPacketResourcePackStatus.Action.ACCEPTED));
Futures.addCallback(mc.getResourcePackRepository().func_177319_a(levelDir), new FutureCallback() { Futures.addCallback(mc.getResourcePackRepository().setResourcePackInstance(levelDir), new FutureCallback<Object>() {
@Override @Override
public void onSuccess(Object result) { public void onSuccess(Object result) {
recordResourcePack(levelDir, requestId); recordResourcePack(levelDir, requestId);
netManager.sendPacket(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.SUCCESSFULLY_LOADED)); netManager.sendPacket(new CPacketResourcePackStatus(hash, CPacketResourcePackStatus.Action.SUCCESSFULLY_LOADED));
} }
@Override @Override
public void onFailure(@Nonnull Throwable throwable) { public void onFailure(@Nonnull Throwable throwable) {
netManager.sendPacket(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.FAILED_DOWNLOAD)); netManager.sendPacket(new CPacketResourcePackStatus(hash, CPacketResourcePackStatus.Action.FAILED_DOWNLOAD));
} }
}); });
} else { } else {
netManager.sendPacket(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.FAILED_DOWNLOAD)); netManager.sendPacket(new CPacketResourcePackStatus(hash, CPacketResourcePackStatus.Action.FAILED_DOWNLOAD));
} }
} else { } else {
final ServerData serverData = mc.getCurrentServerData(); final ServerData serverData = mc.getCurrentServerData();
if (serverData != null && serverData.getResourceMode() == ServerData.ServerResourceMode.ENABLED) { if (serverData != null && serverData.getResourceMode() == ServerData.ServerResourceMode.ENABLED) {
netManager.sendPacket(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.ACCEPTED)); netManager.sendPacket(new CPacketResourcePackStatus(hash, CPacketResourcePackStatus.Action.ACCEPTED));
downloadResourcePackFuture(requestId, url, hash); downloadResourcePackFuture(requestId, url, hash);
} else if (serverData != null && serverData.getResourceMode() != ServerData.ServerResourceMode.PROMPT) { } else if (serverData != null && serverData.getResourceMode() != ServerData.ServerResourceMode.PROMPT) {
netManager.sendPacket(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.DECLINED)); netManager.sendPacket(new CPacketResourcePackStatus(hash, CPacketResourcePackStatus.Action.DECLINED));
} else { } else {
// Lambdas MUST NOT be used with methods that need re-obfuscation in FG prior to 2.2 (will result in AbstractMethodError) mc.addScheduledTask(() -> mc.displayGuiScreen(new GuiYesNo((result, id) -> {
//noinspection Convert2Lambda if (serverData != null) {
mc.addScheduledTask(() -> mc.displayGuiScreen(new GuiYesNo(new GuiYesNoCallback() { serverData.setResourceMode(result ? ServerData.ServerResourceMode.ENABLED : ServerData.ServerResourceMode.DISABLED);
@Override
public void confirmClicked(boolean result, int id) {
if (serverData != null) {
serverData.setResourceMode(result ? ServerData.ServerResourceMode.ENABLED : ServerData.ServerResourceMode.DISABLED);
}
if (result) {
netManager.sendPacket(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.ACCEPTED));
ResourcePackRecorder.this.downloadResourcePackFuture(requestId, url, hash);
} else {
netManager.sendPacket(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.DECLINED));
}
ServerList.func_147414_b(serverData);
mc.displayGuiScreen(null);
} }
if (result) {
netManager.sendPacket(new CPacketResourcePackStatus(hash, CPacketResourcePackStatus.Action.ACCEPTED));
downloadResourcePackFuture(requestId, url, hash);
} else {
netManager.sendPacket(new CPacketResourcePackStatus(hash, CPacketResourcePackStatus.Action.DECLINED));
}
ServerList.saveSingleServer(serverData);
mc.displayGuiScreen(null);
}, I18n.format("multiplayer.texturePrompt.line1"), I18n.format("multiplayer.texturePrompt.line2"), 0))); }, I18n.format("multiplayer.texturePrompt.line1"), I18n.format("multiplayer.texturePrompt.line2"), 0)));
} }
} }
return new S48PacketResourcePackSend("replay://" + requestId, ""); return new SPacketResourcePackSend("replay://" + requestId, "");
} }
private void downloadResourcePackFuture(int requestId, String url, final String hash) { private void downloadResourcePackFuture(int requestId, String url, final String hash) {
Futures.addCallback(downloadResourcePack(requestId, url, hash), new FutureCallback() { Futures.addCallback(downloadResourcePack(requestId, url, hash), new FutureCallback() {
@Override @Override
public void onSuccess(Object result) { public void onSuccess(Object result) {
mc.getNetHandler().addToSendQueue(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.SUCCESSFULLY_LOADED)); mc.getConnection().sendPacket(new CPacketResourcePackStatus(hash, CPacketResourcePackStatus.Action.SUCCESSFULLY_LOADED));
} }
@Override @Override
public void onFailure(@Nonnull Throwable throwable) { public void onFailure(@Nonnull Throwable throwable) {
mc.getNetHandler().addToSendQueue(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.FAILED_DOWNLOAD)); mc.getConnection().sendPacket(new CPacketResourcePackStatus(hash, CPacketResourcePackStatus.Action.FAILED_DOWNLOAD));
} }
}); });
} }
@@ -171,16 +165,16 @@ public class ResourcePackRecorder {
} }
final File file = new File(repo.dirServerResourcepacks, fileName); final File file = new File(repo.dirServerResourcepacks, fileName);
repo.field_177321_h.lock(); repo.lock.lock();
try { try {
repo.func_148529_f(); repo.clearResourcePack();
if (file.exists() && hash.length() == 40) { if (file.exists() && hash.length() == 40) {
try { try {
String fileHash = Hashing.sha1().hashBytes(Files.toByteArray(file)).toString(); String fileHash = Hashing.sha1().hashBytes(Files.toByteArray(file)).toString();
if (fileHash.equals(hash)) { if (fileHash.equals(hash)) {
recordResourcePack(file, requestId); recordResourcePack(file, requestId);
return repo.func_177319_a(file); return repo.setResourcePackInstance(file);
} }
logger.warn("File " + file + " had wrong hash (expected " + hash + ", found " + fileHash + "). Deleting it."); logger.warn("File " + file + " had wrong hash (expected " + hash + ", found " + fileHash + "). Deleting it.");
@@ -196,13 +190,13 @@ public class ResourcePackRecorder {
Futures.getUnchecked(mc.addScheduledTask(() -> mc.displayGuiScreen(guiScreen))); Futures.getUnchecked(mc.addScheduledTask(() -> mc.displayGuiScreen(guiScreen)));
Map sessionInfo = Minecraft.getSessionInfo(); Map<String, String> sessionInfo = Minecraft.getSessionInfo();
repo.field_177322_i = HttpUtil.func_180192_a(file, url, sessionInfo, 50 * 1024 * 1024, guiScreen, mc.getProxy()); repo.downloadingPacks = HttpUtil.downloadResourcePack(file, url, sessionInfo, 50 * 1024 * 1024, guiScreen, mc.getProxy());
Futures.addCallback(repo.field_177322_i, new FutureCallback() { Futures.addCallback(repo.downloadingPacks, new FutureCallback<Object>() {
@Override @Override
public void onSuccess(Object value) { public void onSuccess(Object value) {
recordResourcePack(file, requestId); recordResourcePack(file, requestId);
repo.func_177319_a(file); repo.setResourcePackInstance(file);
} }
@Override @Override
@@ -210,9 +204,9 @@ public class ResourcePackRecorder {
throwable.printStackTrace(); throwable.printStackTrace();
} }
}); });
return repo.field_177322_i; return repo.downloadingPacks;
} finally { } finally {
repo.field_177321_h.unlock(); repo.lock.unlock();
} }
} }

View File

@@ -5,8 +5,8 @@ import com.replaymod.render.utils.RenderJob;
import com.replaymod.replay.events.ReplayCloseEvent; import com.replaymod.replay.events.ReplayCloseEvent;
import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReport;
import net.minecraft.util.ReportedException; import net.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.FMLCommonHandler;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
@@ -45,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

@@ -75,9 +75,9 @@ public class ODSFrameCapturer implements FrameCapturer<ODSOpenGlFrame> {
linkState(1, "lightMapEnabled"); linkState(1, "lightMapEnabled");
linkState(2, "hurtTextureEnabled"); linkState(2, "hurtTextureEnabled");
final Program.Uniform uniform = shaderProgram.getUniformVariable("fogEnabled"); final Program.Uniform uniform = shaderProgram.getUniformVariable("fogEnabled");
previousFogState = GlStateManager.fogState.field_179049_a; previousFogState = GlStateManager.fogState.fog;
uniform.set(previousFogState.currentState); uniform.set(previousFogState.currentState);
GlStateManager.fogState.field_179049_a = new BooleanState(previousFogState.capability) { GlStateManager.fogState.fog = new BooleanState(previousFogState.capability) {
@Override @Override
public void setState(boolean state) { public void setState(boolean state) {
super.setState(state); super.setState(state);
@@ -135,7 +135,7 @@ public class ODSFrameCapturer implements FrameCapturer<ODSOpenGlFrame> {
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
GlStateManager.textureState[i].texture2DState = previousStates[i]; GlStateManager.textureState[i].texture2DState = previousStates[i];
} }
GlStateManager.fogState.field_179049_a = previousFogState; GlStateManager.fogState.fog = previousFogState;
} }
private class CubicStereoFrameCapturer extends CubicPboOpenGlFrameCapturer { private class CubicStereoFrameCapturer extends CubicPboOpenGlFrameCapturer {

View File

@@ -10,13 +10,13 @@ import net.minecraft.client.renderer.chunk.RenderChunk;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.Iterator; import java.util.Iterator;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.PriorityBlockingQueue;
public class ChunkLoadingRenderGlobal { public class ChunkLoadingRenderGlobal {
private final RenderGlobal hooked; private final RenderGlobal hooked;
private final ChunkRenderDispatcher renderDispatcher; private final ChunkRenderDispatcher renderDispatcher;
private final JailingQueue<?> workerJailingQueue; private final JailingQueue<ChunkCompileTaskGenerator> workerJailingQueue;
private final CustomChunkRenderWorker renderWorker; private final CustomChunkRenderWorker renderWorker;
private int frame; private int frame;
@@ -27,10 +27,10 @@ public class ChunkLoadingRenderGlobal {
this.renderWorker = new CustomChunkRenderWorker(renderDispatcher, new RegionRenderCacheBuilder()); this.renderWorker = new CustomChunkRenderWorker(renderDispatcher, new RegionRenderCacheBuilder());
int workerThreads = renderDispatcher.listThreadedWorkers.size(); int workerThreads = renderDispatcher.listThreadedWorkers.size();
BlockingQueue<Object> queueChunkUpdates = renderDispatcher.queueChunkUpdates; PriorityBlockingQueue<ChunkCompileTaskGenerator> queueChunkUpdates = renderDispatcher.queueChunkUpdates;
workerJailingQueue = new JailingQueue<>(queueChunkUpdates); workerJailingQueue = new JailingQueue<>(queueChunkUpdates);
renderDispatcher.queueChunkUpdates = workerJailingQueue; renderDispatcher.queueChunkUpdates = workerJailingQueue;
ChunkCompileTaskGenerator element = new ChunkCompileTaskGenerator(null, null); ChunkCompileTaskGenerator element = new ChunkCompileTaskGenerator(null, null, 0);
element.finish(); element.finish();
for (int i = 0; i < workerThreads; i++) { for (int i = 0; i < workerThreads; i++) {
queueChunkUpdates.add(element); queueChunkUpdates.add(element);
@@ -51,7 +51,6 @@ public class ChunkLoadingRenderGlobal {
} }
} }
@SuppressWarnings("unchecked")
public void updateChunks() { public void updateChunks() {
while (renderDispatcher.runChunkUploads(0)) { while (renderDispatcher.runChunkUploads(0)) {
hooked.displayListEntitiesDirty = true; hooked.displayListEntitiesDirty = true;
@@ -59,7 +58,7 @@ public class ChunkLoadingRenderGlobal {
while (!renderDispatcher.queueChunkUpdates.isEmpty()) { while (!renderDispatcher.queueChunkUpdates.isEmpty()) {
try { try {
renderWorker.processTask((ChunkCompileTaskGenerator) renderDispatcher.queueChunkUpdates.poll()); renderWorker.processTask(renderDispatcher.queueChunkUpdates.poll());
} catch (InterruptedException ignored) { } } catch (InterruptedException ignored) { }
} }
@@ -69,7 +68,7 @@ public class ChunkLoadingRenderGlobal {
renderDispatcher.updateChunkNow(renderchunk); renderDispatcher.updateChunkNow(renderchunk);
renderchunk.setNeedsUpdate(false); renderchunk.clearNeedsUpdate();
iterator.remove(); iterator.remove();
} }
} }

View File

@@ -1,47 +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.WorldRenderer;
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;func_180434_a(Lnet/minecraft/client/renderer/WorldRenderer;Lnet/minecraft/entity/Entity;FFFFFF)V"))
private void renderNormalParticle(EntityFX fx, WorldRenderer worldRenderer, Entity view, float partialTicks,
float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) {
renderParticle(fx, worldRenderer, view, partialTicks, rotX, rotXZ, rotZ, rotYZ, rotXY);
}
@Redirect(method = "renderLitParticles", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/particle/EntityFX;func_180434_a(Lnet/minecraft/client/renderer/WorldRenderer;Lnet/minecraft/entity/Entity;FFFFFF)V"))
private void renderLitParticle(EntityFX fx, WorldRenderer worldRenderer, Entity view, float partialTicks,
float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) {
renderParticle(fx, worldRenderer, view, partialTicks, rotX, rotXZ, rotZ, rotYZ, rotXY);
}
private void renderParticle(EntityFX fx, WorldRenderer worldRenderer, 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 = 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.func_180434_a(worldRenderer, view, partialTicks, rotX, rotXZ, rotZ, rotYZ, rotXY);
}
}

View File

@@ -12,7 +12,7 @@ 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.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;
@@ -118,10 +118,10 @@ 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 = "renderWorldPass", 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);
} }
} }

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

@@ -7,15 +7,14 @@ 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(Render.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(Entity 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

@@ -3,16 +3,13 @@ package com.replaymod.render.mixin;
import com.replaymod.render.hooks.ChunkLoadingRenderGlobal; import com.replaymod.render.hooks.ChunkLoadingRenderGlobal;
import net.minecraft.client.renderer.RenderGlobal; import net.minecraft.client.renderer.RenderGlobal;
import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher; import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher;
import net.minecraft.client.renderer.chunk.RenderChunk;
import net.minecraft.client.renderer.culling.ICamera; import net.minecraft.client.renderer.culling.ICamera;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.util.BlockPos;
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 org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(RenderGlobal.class) @Mixin(RenderGlobal.class)
public abstract class MixinRenderGlobal { public abstract class MixinRenderGlobal {
@@ -37,6 +34,8 @@ public abstract class MixinRenderGlobal {
do { do {
setupTerrain(viewEntity, partialTicks, camera, replayModRender_hook.nextFrameId(), playerSpectator); setupTerrain(viewEntity, partialTicks, camera, replayModRender_hook.nextFrameId(), playerSpectator);
replayModRender_hook.updateChunks();
} while (displayListEntitiesDirty); } while (displayListEntitiesDirty);
displayListEntitiesDirty = true; displayListEntitiesDirty = true;
@@ -46,13 +45,6 @@ public abstract class MixinRenderGlobal {
} }
} }
@Inject(method = "isPositionInRenderChunk", at = @At("HEAD"), cancellable = true)
public void replayModRender_isPositionInRenderChunk(BlockPos pos, RenderChunk chunk, CallbackInfoReturnable<Boolean> ci) {
if (replayModRender_hook != null) {
ci.setReturnValue(true);
}
}
@Inject(method = "updateChunks", at = @At("HEAD"), cancellable = true) @Inject(method = "updateChunks", at = @At("HEAD"), cancellable = true)
public void replayModRender_updateChunks(long finishTimeNano, CallbackInfo ci) { public void replayModRender_updateChunks(long finishTimeNano, CallbackInfo ci) {
if (replayModRender_hook != null) { if (replayModRender_hook != null) {

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

@@ -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;
@@ -14,17 +13,18 @@ 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.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 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.fml.common.FMLCommonHandler; 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.util.Dimension; import org.lwjgl.util.Dimension;
@@ -54,7 +54,7 @@ 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;
@@ -90,7 +90,7 @@ public class VideoRenderer implements RenderInfo {
* @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() { public boolean renderVideo() {
FMLCommonHandler.instance().bus().post(new ReplayRenderEvent.Pre(this)); MinecraftForge.EVENT_BUS.post(new ReplayRenderEvent.Pre(this));
setup(); setup();
@@ -136,7 +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));
return !cancelled; return !cancelled;
} }
@@ -196,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();
@@ -219,9 +219,6 @@ public class VideoRenderer implements RenderInfo {
updateDisplaySize(); updateDisplaySize();
ScaledResolution scaled = new ScaledResolution(mc, displayWidth, displayHeight);
gui.toMinecraft().setWorldAndResolution(mc, scaled.getScaledWidth(), scaled.getScaledHeight());
chunkLoadingRenderGlobal = new ChunkLoadingRenderGlobal(mc.renderGlobal); chunkLoadingRenderGlobal = new ChunkLoadingRenderGlobal(mc.renderGlobal);
// Set up our own framebuffer to render the GUI to // Set up our own framebuffer to render the GUI to
@@ -242,13 +239,13 @@ 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) { if (chunkLoadingRenderGlobal != null) {
chunkLoadingRenderGlobal.uninstall(); chunkLoadingRenderGlobal.uninstall();
} }
ReplayMod.soundHandler.playRenderSuccessSound(); new SoundHandler().playRenderSuccessSound();
new GuiRenderingDone(ReplayModRender.instance, videoWriter.getVideoFile(), totalFrames, settings).display(); new GuiRenderingDone(ReplayModRender.instance, videoWriter.getVideoFile(), totalFrames, settings).display();
@@ -286,7 +283,7 @@ public class VideoRenderer implements RenderInfo {
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());
try { try {

View File

@@ -2,15 +2,18 @@ package com.replaymod.render.utils;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import java.util.*; import java.util.Collection;
import java.util.concurrent.BlockingQueue; import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
public class JailingQueue<T> extends AbstractQueue<T> implements BlockingQueue<T> { public class JailingQueue<T> extends PriorityBlockingQueue<T> {
private final BlockingQueue<T> delegate; private final PriorityBlockingQueue<T> delegate;
private final Set<Thread> jailed = new HashSet<Thread>(); private final Set<Thread> jailed = new HashSet<Thread>();
public JailingQueue(BlockingQueue<T> delegate) { public JailingQueue(PriorityBlockingQueue<T> delegate) {
this.delegate = delegate; this.delegate = delegate;
} }
@@ -59,13 +62,13 @@ public class JailingQueue<T> extends AbstractQueue<T> implements BlockingQueue<T
} }
@Override @Override
public void put(T t) throws InterruptedException { public void put(T t) {
tryAccess(); tryAccess();
delegate.put(t); delegate.put(t);
} }
@Override @Override
public boolean offer(T t, long timeout, TimeUnit unit) throws InterruptedException { public boolean offer(T t, long timeout, TimeUnit unit) {
tryAccess(); tryAccess();
return delegate.offer(t, timeout, unit); return delegate.offer(t, timeout, unit);
} }

View File

@@ -143,6 +143,7 @@ public class InputReplayTimer extends WrappedTimer {
// Following are a ton of vanilla keyboard shortcuts, some are removed as they're useless in the // Following are a ton of vanilla keyboard shortcuts, some are removed as they're useless in the
// replay viewer as of now // replay viewer as of now
// TODO Update maybe add new key bindings
// TODO: Translate magic values to Keyboard.KEY_ constants // TODO: Translate magic values to Keyboard.KEY_ constants
if (key == 32 && Keyboard.isKeyDown(61) && mc.ingameGUI != null) { if (key == 32 && Keyboard.isKeyDown(61) && mc.ingameGUI != null) {

View File

@@ -16,8 +16,6 @@ import javax.imageio.ImageIO;
import java.awt.*; import java.awt.*;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.File; import java.io.File;
import java.util.Queue;
import java.util.concurrent.FutureTask;
@RequiredArgsConstructor(access = AccessLevel.PRIVATE) @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
@Getter @Getter
@@ -45,7 +43,7 @@ public class NoGuiScreenshot {
mc.getFramebuffer().bindFramebuffer(true); mc.getFramebuffer().bindFramebuffer(true);
GlStateManager.enableTexture2D(); GlStateManager.enableTexture2D();
mc.entityRenderer.updateCameraAndRender(mc.timer.renderPartialTicks); mc.entityRenderer.updateCameraAndRender(mc.timer.renderPartialTicks, System.nanoTime());
mc.getFramebuffer().unbindFramebuffer(); mc.getFramebuffer().unbindFramebuffer();
GlStateManager.popMatrix(); GlStateManager.popMatrix();
@@ -101,9 +99,7 @@ public class NoGuiScreenshot {
// of the game loop. We cannot use the addScheduledTask method as it'll run the task if called // of the game loop. We cannot use the addScheduledTask method as it'll run the task if called
// from the minecraft thread which is exactly what we want to avoid. // from the minecraft thread which is exactly what we want to avoid.
synchronized (mc.scheduledTasks) { synchronized (mc.scheduledTasks) {
@SuppressWarnings("unchecked") mc.scheduledTasks.add(ListenableFutureTask.create(runnable, null));
Queue<FutureTask> queue = mc.scheduledTasks;
queue.add(ListenableFutureTask.create(runnable, null));
} }
return future; return future;
} }

View File

@@ -23,8 +23,8 @@ import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.network.EnumPacketDirection; import net.minecraft.network.EnumPacketDirection;
import net.minecraft.network.NetworkManager; import net.minecraft.network.NetworkManager;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.client.FMLClientHandler; import net.minecraftforge.fml.client.FMLClientHandler;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.network.handshake.NetworkDispatcher; import net.minecraftforge.fml.common.network.handshake.NetworkDispatcher;
import org.lwjgl.opengl.Display; import org.lwjgl.opengl.Display;
@@ -76,7 +76,7 @@ public class ReplayHandler {
Preconditions.checkState(mc.isCallingFromMinecraftThread(), "Must be called from Minecraft thread."); Preconditions.checkState(mc.isCallingFromMinecraftThread(), "Must be called from Minecraft thread.");
this.replayFile = replayFile; this.replayFile = replayFile;
FMLCommonHandler.instance().bus().post(new ReplayOpenEvent.Pre(this)); MinecraftForge.EVENT_BUS.post(new ReplayOpenEvent.Pre(this));
markers = new ArrayList<>(replayFile.getMarkers().or(Collections.emptySet())); markers = new ArrayList<>(replayFile.getMarkers().or(Collections.emptySet()));
@@ -87,7 +87,7 @@ public class ReplayHandler {
overlay = new GuiReplayOverlay(this); overlay = new GuiReplayOverlay(this);
overlay.setVisible(true); overlay.setVisible(true);
FMLCommonHandler.instance().bus().post(new ReplayOpenEvent.Post(this)); MinecraftForge.EVENT_BUS.post(new ReplayOpenEvent.Post(this));
} }
void restartedReplay() { void restartedReplay() {
@@ -101,7 +101,7 @@ public class ReplayHandler {
public void endReplay() throws IOException { public void endReplay() throws IOException {
Preconditions.checkState(mc.isCallingFromMinecraftThread(), "Must be called from Minecraft thread."); Preconditions.checkState(mc.isCallingFromMinecraftThread(), "Must be called from Minecraft thread.");
FMLCommonHandler.instance().bus().post(new ReplayCloseEvent.Pre(this)); MinecraftForge.EVENT_BUS.post(new ReplayCloseEvent.Pre(this));
replaySender.terminateReplay(); replaySender.terminateReplay();
@@ -124,7 +124,7 @@ public class ReplayHandler {
ReplayModReplay.instance.replayHandler = null; ReplayModReplay.instance.replayHandler = null;
FMLCommonHandler.instance().bus().post(new ReplayCloseEvent.Post(this)); MinecraftForge.EVENT_BUS.post(new ReplayCloseEvent.Post(this));
} }
private void setup() { private void setup() {
@@ -209,6 +209,9 @@ public class ReplayHandler {
*/ */
public void spectateEntity(Entity e) { public void spectateEntity(Entity e) {
CameraEntity cameraEntity = getCameraEntity(); CameraEntity cameraEntity = getCameraEntity();
if (cameraEntity == null) {
return; // Cannot spectate if we have no camera
}
if (e == null || e == cameraEntity) { if (e == null || e == cameraEntity) {
spectating = null; spectating = null;
e = cameraEntity; e = cameraEntity;
@@ -311,7 +314,7 @@ public class ReplayHandler {
mc.getFramebuffer().bindFramebuffer(true); mc.getFramebuffer().bindFramebuffer(true);
mc.entityRenderer.setupOverlayRendering(); mc.entityRenderer.setupOverlayRendering();
ScaledResolution resolution = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight); ScaledResolution resolution = new ScaledResolution(mc);
guiScreen.setWorldAndResolution(mc, resolution.getScaledWidth(), resolution.getScaledHeight()); guiScreen.setWorldAndResolution(mc, resolution.getScaledWidth(), resolution.getScaledHeight());
guiScreen.drawScreen(0, 0, 0); guiScreen.drawScreen(0, 0, 0);
@@ -328,10 +331,8 @@ public class ReplayHandler {
replaySender.setAsyncMode(true); replaySender.setAsyncMode(true);
replaySender.setReplaySpeed(0); replaySender.setReplaySpeed(0);
mc.getNetHandler().getNetworkManager().processReceivedPackets(); mc.getConnection().getNetworkManager().processReceivedPackets();
@SuppressWarnings("unchecked") for (Entity entity : mc.theWorld.loadedEntityList) {
List<Entity> entities = (List<Entity>) mc.theWorld.loadedEntityList;
for (Entity entity : entities) {
if (entity instanceof EntityOtherPlayerMP) { if (entity instanceof EntityOtherPlayerMP) {
EntityOtherPlayerMP e = (EntityOtherPlayerMP) entity; EntityOtherPlayerMP e = (EntityOtherPlayerMP) entity;
e.setPosition(e.otherPlayerMPX, e.otherPlayerMPY, e.otherPlayerMPZ); e.setPosition(e.otherPlayerMPX, e.otherPlayerMPY, e.otherPlayerMPZ);

View File

@@ -6,7 +6,11 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
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.replay.camera.*; import com.replaymod.replay.camera.CameraController;
import com.replaymod.replay.camera.CameraControllerRegistry;
import com.replaymod.replay.camera.CameraEntity;
import com.replaymod.replay.camera.ClassicCameraController;
import com.replaymod.replay.camera.VanillaCameraController;
import com.replaymod.replay.gui.overlay.GuiMarkerTimeline; import com.replaymod.replay.gui.overlay.GuiMarkerTimeline;
import com.replaymod.replay.gui.screen.GuiModCompatWarning; import com.replaymod.replay.gui.screen.GuiModCompatWarning;
import com.replaymod.replay.handler.GuiHandler; import com.replaymod.replay.handler.GuiHandler;
@@ -99,6 +103,7 @@ public class ReplayModReplay {
@Override @Override
public void onSuccess(NoGuiScreenshot result) { public void onSuccess(NoGuiScreenshot result) {
try { try {
core.printInfoToChat("replaymod.chat.savingthumb");
replayHandler.getReplayFile().writeThumb(result.getImage()); replayHandler.getReplayFile().writeThumb(result.getImage());
core.printInfoToChat("replaymod.chat.savedthumb"); core.printInfoToChat("replaymod.chat.savedthumb");
} catch (IOException e) { } catch (IOException e) {
@@ -109,6 +114,7 @@ public class ReplayModReplay {
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable t) {
t.printStackTrace(); t.printStackTrace();
core.printWarningToChat("replaymod.chat.failedthumb");
} }
}); });
} }

View File

@@ -12,17 +12,22 @@ import io.netty.channel.ChannelHandler.Sharable;
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.client.entity.EntityOtherPlayerMP;
import net.minecraft.client.gui.GuiDownloadTerrain; import net.minecraft.client.gui.GuiDownloadTerrain;
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.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.network.*; import net.minecraft.network.*;
import net.minecraft.network.play.server.*; import net.minecraft.network.play.server.*;
import net.minecraft.util.IChatComponent; import net.minecraft.util.text.ITextComponent;
import net.minecraft.world.EnumDifficulty; import net.minecraft.world.EnumDifficulty;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraft.world.WorldSettings.GameType; import net.minecraft.world.WorldSettings.GameType;
import net.minecraft.world.WorldType; import net.minecraft.world.WorldType;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
@@ -39,30 +44,22 @@ import java.util.concurrent.Callable;
*/ */
@Sharable @Sharable
public class ReplaySender extends ChannelInboundHandlerAdapter { public class ReplaySender extends ChannelInboundHandlerAdapter {
/**
* Previously packets for the client player were inserted using one fixed entity id (this one).
* This is no longer the case however to provide backwards compatibility, we have to convert
* these old packets to use the normal entity id.
* Need to punch someone? -> CrushedPixel
*/
public static final int LEGACY_ENTITY_ID = Integer.MIN_VALUE + 9001;
/** /**
* These packets are ignored completely during replay. * These packets are ignored completely during replay.
*/ */
private static final List<Class> BAD_PACKETS = Arrays.<Class>asList( private static final List<Class> BAD_PACKETS = Arrays.<Class>asList(
S06PacketUpdateHealth.class, // TODO Update possibly more?
S2DPacketOpenWindow.class, SPacketUpdateHealth.class,
S2EPacketCloseWindow.class, SPacketOpenWindow.class,
S2FPacketSetSlot.class, SPacketCloseWindow.class,
S30PacketWindowItems.class, SPacketSetSlot.class,
S36PacketSignEditorOpen.class, SPacketWindowItems.class,
S37PacketStatistics.class, SPacketSignEditorOpen.class,
S1FPacketSetExperience.class, SPacketStatistics.class,
S43PacketCamera.class, SPacketSetExperience.class,
S39PacketPlayerAbilities.class, SPacketCamera.class,
S45PacketTitle.class); SPacketPlayerAbilities.class,
SPacketTitle.class);
private static int TP_DISTANCE_LIMIT = 128; private static int TP_DISTANCE_LIMIT = 128;
@@ -171,6 +168,8 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
this.asyncMode = asyncMode; this.asyncMode = asyncMode;
this.replayLength = file.getMetaData().getDuration(); this.replayLength = file.getMetaData().getDuration();
MinecraftForge.EVENT_BUS.register(this);
if (asyncMode) { if (asyncMode) {
new Thread(asyncSender, "replaymod-async-sender").start(); new Thread(asyncSender, "replaymod-async-sender").start();
} }
@@ -239,6 +238,7 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
*/ */
public void terminateReplay() { public void terminateReplay() {
terminate = true; terminate = true;
MinecraftForge.EVENT_BUS.unregister(this);
try { try {
channelInactive(ctx); channelInactive(ctx);
ctx.channel().pipeline().close(); ctx.channel().pipeline().close();
@@ -247,6 +247,25 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
} }
} }
@SubscribeEvent
public void onWorldTick(TickEvent.ClientTickEvent event) {
// Unfortunately the WorldTickEvent doesn't seem to be emitted on the CLIENT side
if (event.phase != TickEvent.Phase.START) return;
// Spawning a player into an empty chunk (which we might do with the recording player)
// prevents it from being moved by teleport packets (it essentially gets stuck) because
// Entity#addedToChunk is not set and it is therefore not updated every tick.
// To counteract this, we need to manually update it's position if it hasn't been added
// to any chunk yet.
if (mc.theWorld != null) {
for (EntityPlayer playerEntity : mc.theWorld.playerEntities) {
if (!playerEntity.addedToChunk && playerEntity instanceof EntityOtherPlayerMP) {
playerEntity.onLivingUpdate();
}
}
}
}
@Override @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception { throws Exception {
@@ -274,21 +293,21 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
// Therefore we have to remove all loaded, dead entities manually if we are in sync mode. // Therefore we have to remove all loaded, dead entities manually if we are in sync mode.
// We do this after every SpawnX packet and after the destroy entities packet. // We do this after every SpawnX packet and after the destroy entities packet.
if (!asyncMode && mc.theWorld != null) { if (!asyncMode && mc.theWorld != null) {
if (p instanceof S0CPacketSpawnPlayer if (p instanceof SPacketSpawnPlayer
|| p instanceof S0EPacketSpawnObject || p instanceof SPacketSpawnObject
|| p instanceof S0FPacketSpawnMob || p instanceof SPacketSpawnMob
|| p instanceof S2CPacketSpawnGlobalEntity || p instanceof SPacketSpawnGlobalEntity
|| p instanceof S10PacketSpawnPainting || p instanceof SPacketSpawnPainting
|| p instanceof S11PacketSpawnExperienceOrb || p instanceof SPacketSpawnExperienceOrb
|| p instanceof S13PacketDestroyEntities) { || p instanceof SPacketDestroyEntities) {
World world = mc.theWorld; World world = mc.theWorld;
for (int i = 0; i < world.loadedEntityList.size(); ++i) { for (int i = 0; i < world.loadedEntityList.size(); ++i) {
Entity entity = (Entity) world.loadedEntityList.get(i); Entity entity = world.loadedEntityList.get(i);
if (entity.isDead) { if (entity.isDead) {
int chunkX = entity.chunkCoordX; int chunkX = entity.chunkCoordX;
int chunkY = entity.chunkCoordZ; int chunkY = entity.chunkCoordZ;
if (entity.addedToChunk && world.getChunkProvider().chunkExists(chunkX, chunkY)) { if (entity.addedToChunk && world.getChunkProvider().getLoadedChunk(chunkX, chunkY) != null) {
world.getChunkFromChunkCoords(chunkX, chunkY).removeEntity(entity); world.getChunkFromChunkCoords(chunkX, chunkY).removeEntity(entity);
} }
@@ -326,8 +345,8 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
* @return The processed packet or {@code null} if no packet shall be sent * @return The processed packet or {@code null} if no packet shall be sent
*/ */
protected Packet processPacket(Packet p) throws Exception { protected Packet processPacket(Packet p) throws Exception {
if (p instanceof S3FPacketCustomPayload) { if (p instanceof SPacketCustomPayload) {
S3FPacketCustomPayload packet = (S3FPacketCustomPayload) p; SPacketCustomPayload packet = (SPacketCustomPayload) p;
if (Restrictions.PLUGIN_CHANNEL.equals(packet.getChannelName())) { if (Restrictions.PLUGIN_CHANNEL.equals(packet.getChannelName())) {
final String unknown = replayHandler.getRestrictions().handle(packet); final String unknown = replayHandler.getRestrictions().handle(packet);
if (unknown == null) { if (unknown == null) {
@@ -353,8 +372,8 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
} }
} }
} }
if (p instanceof S40PacketDisconnect) { if (p instanceof SPacketDisconnect) {
IChatComponent reason = ((S40PacketDisconnect) p).func_149165_c(); ITextComponent reason = ((SPacketDisconnect) p).getReason();
if ("Please update to view this replay.".equals(reason.getUnformattedText())) { if ("Please update to view this replay.".equals(reason.getUnformattedText())) {
// This version of the mod supports replay restrictions so we are allowed // This version of the mod supports replay restrictions so we are allowed
// to remove this packet. // to remove this packet.
@@ -364,11 +383,9 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
if(BAD_PACKETS.contains(p.getClass())) return null; if(BAD_PACKETS.contains(p.getClass())) return null;
convertLegacyEntityIds(p); if(p instanceof SPacketResourcePackSend) {
SPacketResourcePackSend packet = (SPacketResourcePackSend) p;
if(p instanceof S48PacketResourcePackSend) { String url = packet.getURL();
S48PacketResourcePackSend packet = (S48PacketResourcePackSend) p;
String url = packet.func_179783_a();
if (url.startsWith("replay://")) { if (url.startsWith("replay://")) {
int id = Integer.parseInt(url.substring("replay://".length())); int id = Integer.parseInt(url.substring("replay://".length()));
Map<Integer, String> index = replayFile.getResourcePackIndex(); Map<Integer, String> index = replayFile.getResourcePackIndex();
@@ -379,17 +396,17 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
if (!file.exists()) { if (!file.exists()) {
IOUtils.copy(replayFile.getResourcePack(hash).get(), new FileOutputStream(file)); IOUtils.copy(replayFile.getResourcePack(hash).get(), new FileOutputStream(file));
} }
mc.getResourcePackRepository().func_177319_a(file); mc.getResourcePackRepository().setResourcePackInstance(file);
} }
} }
return null; return null;
} }
} }
if(p instanceof S01PacketJoinGame) { if(p instanceof SPacketJoinGame) {
S01PacketJoinGame packet = (S01PacketJoinGame) p; SPacketJoinGame packet = (SPacketJoinGame) p;
allowMovement = true; allowMovement = true;
int entId = packet.getEntityId(); int entId = packet.getPlayerId();
actualID = entId; actualID = entId;
entId = -1789435; // Camera entity id should be negative which is an invalid id and can't be used by servers entId = -1789435; // Camera entity id should be negative which is an invalid id and can't be used by servers
int dimension = packet.getDimension(); int dimension = packet.getDimension();
@@ -397,21 +414,21 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
int maxPlayers = packet.getMaxPlayers(); int maxPlayers = packet.getMaxPlayers();
WorldType worldType = packet.getWorldType(); WorldType worldType = packet.getWorldType();
p = new S01PacketJoinGame(entId, GameType.SPECTATOR, false, dimension, p = new SPacketJoinGame(entId, GameType.SPECTATOR, false, dimension,
difficulty, maxPlayers, worldType, false); difficulty, maxPlayers, worldType, false);
} }
if(p instanceof S07PacketRespawn) { if(p instanceof SPacketRespawn) {
S07PacketRespawn respawn = (S07PacketRespawn) p; SPacketRespawn respawn = (SPacketRespawn) p;
p = new S07PacketRespawn(respawn.func_149082_c(), p = new SPacketRespawn(respawn.getDimensionID(),
respawn.func_149081_d(), respawn.func_149080_f(), GameType.SPECTATOR); respawn.getDifficulty(), respawn.getWorldType(), GameType.SPECTATOR);
allowMovement = true; allowMovement = true;
} }
if(p instanceof S08PacketPlayerPosLook) { if(p instanceof SPacketPlayerPosLook) {
if(!hasWorldLoaded) hasWorldLoaded = true; if(!hasWorldLoaded) hasWorldLoaded = true;
final S08PacketPlayerPosLook ppl = (S08PacketPlayerPosLook) p; final SPacketPlayerPosLook ppl = (SPacketPlayerPosLook) p;
if (mc.currentScreen instanceof GuiDownloadTerrain) { if (mc.currentScreen instanceof GuiDownloadTerrain) {
// Close the world loading screen manually in case we swallow the packet // Close the world loading screen manually in case we swallow the packet
@@ -422,17 +439,17 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
CameraEntity cent = replayHandler.getCameraEntity(); CameraEntity cent = replayHandler.getCameraEntity();
for (Object relative : ppl.func_179834_f()) { for (SPacketPlayerPosLook.EnumFlags relative : ppl.getFlags()) {
if (relative == S08PacketPlayerPosLook.EnumFlags.X if (relative == SPacketPlayerPosLook.EnumFlags.X
|| relative == S08PacketPlayerPosLook.EnumFlags.Y || relative == SPacketPlayerPosLook.EnumFlags.Y
|| relative == S08PacketPlayerPosLook.EnumFlags.Z) { || relative == SPacketPlayerPosLook.EnumFlags.Z) {
return null; // At least one of the coordinates is relative, so we don't care return null; // At least one of the coordinates is relative, so we don't care
} }
} }
if(cent != null) { if(cent != null) {
if(!allowMovement && !((Math.abs(cent.posX - ppl.func_148932_c()) > TP_DISTANCE_LIMIT) || if(!allowMovement && !((Math.abs(cent.posX - ppl.getX()) > TP_DISTANCE_LIMIT) ||
(Math.abs(cent.posZ - ppl.func_148933_e()) > TP_DISTANCE_LIMIT))) { (Math.abs(cent.posZ - ppl.getZ()) > TP_DISTANCE_LIMIT))) {
return null; return null;
} else { } else {
allowMovement = false; allowMovement = false;
@@ -451,15 +468,15 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
} }
CameraEntity cent = replayHandler.getCameraEntity(); CameraEntity cent = replayHandler.getCameraEntity();
cent.setCameraPosition(ppl.func_148932_c(), ppl.func_148928_d(), ppl.func_148933_e()); cent.setCameraPosition(ppl.getX(), ppl.getY(), ppl.getZ());
return null; return null;
} }
}.call(); }.call();
} }
if(p instanceof S2BPacketChangeGameState) { if(p instanceof SPacketChangeGameState) {
S2BPacketChangeGameState pg = (S2BPacketChangeGameState)p; SPacketChangeGameState pg = (SPacketChangeGameState)p;
int reason = pg.func_149138_c(); int reason = pg.getGameState();
// only allow the following packets: // only allow the following packets:
// 1 - End raining // 1 - End raining
@@ -473,7 +490,7 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
} }
} }
if (p instanceof S02PacketChat) { if (p instanceof SPacketChat) {
if (!ReplayModReplay.instance.getCore().getSettingsRegistry().get(Setting.SHOW_CHAT)) { if (!ReplayModReplay.instance.getCore().getSettingsRegistry().get(Setting.SHOW_CHAT)) {
return null; return null;
} }
@@ -486,7 +503,7 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void channelActive(ChannelHandlerContext ctx) throws Exception { public void channelActive(ChannelHandlerContext ctx) throws Exception {
this.ctx = ctx; this.ctx = ctx;
ctx.attr(NetworkManager.attrKeyConnectionState).set(EnumConnectionState.PLAY); ctx.attr(NetworkManager.PROTOCOL_ATTRIBUTE_KEY).set(EnumConnectionState.PLAY);
super.channelActive(ctx); super.channelActive(ctx);
} }
@@ -691,11 +708,11 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
protected Packet processPacketAsync(Packet p) { protected Packet processPacketAsync(Packet p) {
//If hurrying, ignore some packets, except for short durations //If hurrying, ignore some packets, except for short durations
if(desiredTimeStamp - lastTimeStamp > 1000) { if(desiredTimeStamp - lastTimeStamp > 1000) {
if(p instanceof S2APacketParticles) return null; if(p instanceof SPacketParticles) return null;
if(p instanceof S0EPacketSpawnObject) { if(p instanceof SPacketSpawnObject) {
S0EPacketSpawnObject pso = (S0EPacketSpawnObject)p; SPacketSpawnObject pso = (SPacketSpawnObject)p;
int type = pso.func_148993_l(); int type = pso.getType();
if(type == 76) { // Firework rocket if(type == 76) { // Firework rocket
return null; return null;
} }
@@ -781,68 +798,6 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
return p; // During synchronous playback everything is sent normally return p; // During synchronous playback everything is sent normally
} }
/**
* This is necessary to convert packets from old replays to new replays.
* @param packet The packet to be transformed.
* @see #LEGACY_ENTITY_ID
*/
private void convertLegacyEntityIds(Packet packet) {
if (packet instanceof S0CPacketSpawnPlayer) {
S0CPacketSpawnPlayer p = (S0CPacketSpawnPlayer) packet;
if (p.field_148957_a == LEGACY_ENTITY_ID) {
p.field_148957_a = actualID;
}
} else if (packet instanceof S18PacketEntityTeleport) {
S18PacketEntityTeleport p = (S18PacketEntityTeleport) packet;
if (p.field_149458_a == LEGACY_ENTITY_ID) {
p.field_149458_a = actualID;
}
} else if (packet instanceof S14PacketEntity.S17PacketEntityLookMove) {
S14PacketEntity.S17PacketEntityLookMove p = (S14PacketEntity.S17PacketEntityLookMove) packet;
if (p.field_149074_a == LEGACY_ENTITY_ID) {
p.field_149074_a = actualID;
}
} else if (packet instanceof S19PacketEntityHeadLook) {
S19PacketEntityHeadLook p = (S19PacketEntityHeadLook) packet;
if (p.field_149384_a == LEGACY_ENTITY_ID) {
p.field_149384_a = actualID;
}
} else if (packet instanceof S12PacketEntityVelocity) {
S12PacketEntityVelocity p = (S12PacketEntityVelocity) packet;
if (p.field_149417_a == LEGACY_ENTITY_ID) {
p.field_149417_a = actualID;
}
} else if (packet instanceof S0BPacketAnimation) {
S0BPacketAnimation p = (S0BPacketAnimation) packet;
if (p.entityId == LEGACY_ENTITY_ID) {
p.entityId = actualID;
}
} else if (packet instanceof S04PacketEntityEquipment) {
S04PacketEntityEquipment p = (S04PacketEntityEquipment) packet;
if (p.field_149394_a == LEGACY_ENTITY_ID) {
p.field_149394_a = actualID;
}
} else if (packet instanceof S1BPacketEntityAttach) {
S1BPacketEntityAttach p = (S1BPacketEntityAttach) packet;
if (p.field_149408_a == LEGACY_ENTITY_ID) {
p.field_149408_a = actualID;
}
if (p.field_149406_b == LEGACY_ENTITY_ID) {
p.field_149406_b = actualID;
}
} else if (packet instanceof S0DPacketCollectItem) {
S0DPacketCollectItem p = (S0DPacketCollectItem) packet;
if (p.field_149356_b == LEGACY_ENTITY_ID) {
p.field_149356_b = actualID;
}
} else if (packet instanceof S13PacketDestroyEntities) {
S13PacketDestroyEntities p = (S13PacketDestroyEntities) packet;
if (p.field_149100_a.length == 1 && p.field_149100_a[0] == LEGACY_ENTITY_ID) {
p.field_149100_a[0] = actualID;
}
}
}
private static final class PacketData { private static final class PacketData {
private final int timestamp; private final int timestamp;
private final byte[] bytes; private final byte[] bytes;

View File

@@ -18,16 +18,17 @@ import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.item.EntityItemFrame; import net.minecraft.entity.item.EntityItemFrame;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.stats.StatFileWriter; import net.minecraft.inventory.EntityEquipmentSlot;
import net.minecraft.util.AxisAlignedBB; import net.minecraft.stats.StatisticsManager;
import net.minecraft.util.MovingObjectPosition; import net.minecraft.util.EnumHand;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraftforge.client.event.EntityViewRenderEvent; import net.minecraftforge.client.event.EntityViewRenderEvent;
import net.minecraftforge.client.event.RenderGameOverlayEvent; import net.minecraftforge.client.event.RenderGameOverlayEvent;
import net.minecraftforge.client.event.RenderHandEvent; import net.minecraftforge.client.event.RenderHandEvent;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.eventhandler.EventPriority; import net.minecraftforge.fml.common.eventhandler.EventPriority;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent; import net.minecraftforge.fml.common.gameevent.TickEvent;
@@ -65,9 +66,8 @@ public class CameraEntity extends EntityPlayerSP {
*/ */
private final EventHandler eventHandler = new EventHandler(); private final EventHandler eventHandler = new EventHandler();
public CameraEntity(Minecraft mcIn, World worldIn, NetHandlerPlayClient netHandlerPlayClient, StatFileWriter statFileWriter) { public CameraEntity(Minecraft mcIn, World worldIn, NetHandlerPlayClient netHandlerPlayClient, StatisticsManager statisticsManager) {
super(mcIn, worldIn, netHandlerPlayClient, statFileWriter); super(mcIn, worldIn, netHandlerPlayClient, statisticsManager);
FMLCommonHandler.instance().bus().register(eventHandler);
MinecraftForge.EVENT_BUS.register(eventHandler); MinecraftForge.EVENT_BUS.register(eventHandler);
if (ReplayModReplay.instance.getReplayHandler().getSpectatedUUID() == null) { if (ReplayModReplay.instance.getReplayHandler().getSpectatedUUID() == null) {
cameraController = ReplayModReplay.instance.createCameraController(this); cameraController = ReplayModReplay.instance.createCameraController(this);
@@ -289,12 +289,49 @@ public class CameraEntity extends EntityPlayerSP {
} }
@Override @Override
public MovingObjectPosition rayTrace(double p_174822_1_, float p_174822_3_) { public float getCooldownPeriod() {
MovingObjectPosition pos = super.rayTrace(p_174822_1_, 1f); Entity view = mc.getRenderViewEntity();
if (view != this && view instanceof EntityPlayer) {
return ((EntityPlayer) view).getCooldownPeriod();
}
return 1;
}
@Override
public float getCooledAttackStrength(float adjustTicks) {
Entity view = mc.getRenderViewEntity();
if (view != this && view instanceof EntityPlayer) {
return ((EntityPlayer) view).getCooledAttackStrength(adjustTicks);
}
// Default to 1 as to not render the cooldown indicator (renders for < 1)
return 1;
}
@Override
public EnumHand getActiveHand() {
Entity view = mc.getRenderViewEntity();
if (view != this && view instanceof EntityPlayer) {
return ((EntityPlayer) view).getActiveHand();
}
return super.getActiveHand();
}
@Override
public boolean isHandActive() {
Entity view = mc.getRenderViewEntity();
if (view != this && view instanceof EntityPlayer) {
return ((EntityPlayer) view).isHandActive();
}
return super.isHandActive();
}
@Override
public RayTraceResult rayTrace(double p_174822_1_, float p_174822_3_) {
RayTraceResult pos = super.rayTrace(p_174822_1_, 1f);
// Make sure we can never look at blocks (-> no outline) // Make sure we can never look at blocks (-> no outline)
if(pos != null && pos.typeOfHit == MovingObjectPosition.MovingObjectType.BLOCK) { if(pos != null && pos.typeOfHit == RayTraceResult.Type.BLOCK) {
pos.typeOfHit = MovingObjectPosition.MovingObjectType.MISS; pos.typeOfHit = RayTraceResult.Type.MISS;
} }
return pos; return pos;
@@ -309,7 +346,6 @@ public class CameraEntity extends EntityPlayerSP {
@Override @Override
public void setDead() { public void setDead() {
super.setDead(); super.setDead();
FMLCommonHandler.instance().bus().unregister(eventHandler);
MinecraftForge.EVENT_BUS.unregister(eventHandler); MinecraftForge.EVENT_BUS.unregister(eventHandler);
} }
@@ -367,9 +403,13 @@ public class CameraEntity extends EntityPlayerSP {
@SubscribeEvent @SubscribeEvent
public void preCrosshairRender(RenderGameOverlayEvent.Pre event) { public void preCrosshairRender(RenderGameOverlayEvent.Pre event) {
// The crosshair should only render if targeted entity can actually be spectated // The crosshair should only render if targeted entity can actually be spectated
if (event.type == RenderGameOverlayEvent.ElementType.CROSSHAIRS) { if (event.getType() == RenderGameOverlayEvent.ElementType.CROSSHAIRS) {
event.setCanceled(!canSpectate(mc.pointedEntity)); event.setCanceled(!canSpectate(mc.pointedEntity));
} }
// Hotbar should never be rendered
if (event.getType() == RenderGameOverlayEvent.ElementType.HOTBAR) {
event.setCanceled(true);
}
} }
@SubscribeEvent @SubscribeEvent
@@ -400,10 +440,13 @@ public class CameraEntity extends EntityPlayerSP {
if (lastHandRendered != player) { if (lastHandRendered != player) {
lastHandRendered = player; lastHandRendered = player;
mc.entityRenderer.itemRenderer.prevEquippedProgress = 1; mc.entityRenderer.itemRenderer.prevEquippedProgressMainHand = 1;
mc.entityRenderer.itemRenderer.equippedProgress = 1; mc.entityRenderer.itemRenderer.prevEquippedProgressOffHand = 1;
mc.entityRenderer.itemRenderer.itemToRender = player.inventory.getCurrentItem(); mc.entityRenderer.itemRenderer.equippedProgressMainHand = 1;
mc.entityRenderer.itemRenderer.equippedItemSlot = player.inventory.currentItem; mc.entityRenderer.itemRenderer.equippedProgressOffHand = 1;
mc.entityRenderer.itemRenderer.itemStackMainHand = player.getItemStackFromSlot(EntityEquipmentSlot.MAINHAND);
mc.entityRenderer.itemRenderer.itemStackOffHand = player.getItemStackFromSlot(EntityEquipmentSlot.OFFHAND);
mc.thePlayer.renderArmYaw = mc.thePlayer.prevRenderArmYaw = player.rotationYaw; mc.thePlayer.renderArmYaw = mc.thePlayer.prevRenderArmYaw = player.rotationYaw;
mc.thePlayer.renderArmPitch = mc.thePlayer.prevRenderArmPitch = player.rotationPitch; mc.thePlayer.renderArmPitch = mc.thePlayer.prevRenderArmPitch = player.rotationPitch;
@@ -414,7 +457,7 @@ public class CameraEntity extends EntityPlayerSP {
@SubscribeEvent @SubscribeEvent
public void onEntityViewRenderEvent(EntityViewRenderEvent.CameraSetup event) { public void onEntityViewRenderEvent(EntityViewRenderEvent.CameraSetup event) {
if (mc.getRenderViewEntity() == CameraEntity.this) { if (mc.getRenderViewEntity() == CameraEntity.this) {
event.roll = roll; event.setRoll(roll);
} }
} }
} }

View File

@@ -2,8 +2,8 @@ package com.replaymod.replay.camera;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.settings.KeyBinding; import net.minecraft.client.settings.KeyBinding;
import net.minecraft.util.MathHelper; import net.minecraft.util.math.MathHelper;
import net.minecraft.util.Vec3; import net.minecraft.util.math.Vec3d;
import org.lwjgl.Sys; import org.lwjgl.Sys;
// TODO: Marius is responsible for this. Please, someone clean it up. // TODO: Marius is responsible for this. Please, someone clean it up.
@@ -18,8 +18,8 @@ public class ClassicCameraController implements CameraController {
private double THRESHOLD = MAX_SPEED / 20; private double THRESHOLD = MAX_SPEED / 20;
private double DECAY = MAX_SPEED/3; private double DECAY = MAX_SPEED/3;
private Vec3 direction; private Vec3d direction;
private Vec3 dirBefore; private Vec3d dirBefore;
private double motion; private double motion;
private long lastCall = Sys.getTime(); private long lastCall = Sys.getTime();
@@ -129,7 +129,7 @@ public class ClassicCameraController implements CameraController {
return; return;
} }
Vec3 movement = direction.normalize(); Vec3d movement = direction.normalize();
double factor = motion * (frac / 1000D); double factor = motion * (frac / 1000D);
camera.moveCamera(movement.xCoord * factor, movement.yCoord * factor, movement.zCoord * factor); camera.moveCamera(movement.xCoord * factor, movement.yCoord * factor, movement.zCoord * factor);
@@ -158,7 +158,7 @@ public class ClassicCameraController implements CameraController {
break; break;
} }
Vec3 dbf = direction; Vec3d dbf = direction;
if(dirBefore != null) { if(dirBefore != null) {
direction = dirBefore.normalize().add(direction); direction = dirBefore.normalize().add(direction);
@@ -169,12 +169,12 @@ public class ClassicCameraController implements CameraController {
updateMovement(); updateMovement();
} }
private Vec3 getVectorForRotation(float pitch, float yaw) { private Vec3d getVectorForRotation(float pitch, float yaw) {
float f2 = MathHelper.cos(-yaw * 0.017453292F - (float) Math.PI); float f2 = MathHelper.cos(-yaw * 0.017453292F - (float) Math.PI);
float f3 = MathHelper.sin(-yaw * 0.017453292F - (float)Math.PI); float f3 = MathHelper.sin(-yaw * 0.017453292F - (float)Math.PI);
float f4 = -MathHelper.cos(-pitch * 0.017453292F); float f4 = -MathHelper.cos(-pitch * 0.017453292F);
float f5 = MathHelper.sin(-pitch * 0.017453292F); float f5 = MathHelper.sin(-pitch * 0.017453292F);
return new Vec3((double)(f3 * f4), (double)f5, (double)(f2 * f4)); return new Vec3d((double)(f3 * f4), (double)f5, (double)(f2 * f4));
} }
public enum MoveDirection { public enum MoveDirection {

View File

@@ -41,8 +41,9 @@ public class SpectatorCameraController implements CameraController {
if (view instanceof EntityPlayer) { if (view instanceof EntityPlayer) {
EntityPlayer viewPlayer = (EntityPlayer) view; EntityPlayer viewPlayer = (EntityPlayer) view;
camera.inventory = viewPlayer.inventory; camera.inventory = viewPlayer.inventory;
camera.itemInUse = viewPlayer.itemInUse; camera.itemStackMainHand = viewPlayer.itemStackMainHand;
camera.itemInUseCount = viewPlayer.itemInUseCount; camera.swingingHand = viewPlayer.swingingHand;
camera.activeItemStackUseCount = viewPlayer.activeItemStackUseCount;
} }
} }
} }

View File

@@ -2,14 +2,14 @@ package com.replaymod.replay.gui.overlay;
import com.replaymod.core.ReplayMod; import com.replaymod.core.ReplayMod;
import com.replaymod.replay.ReplayHandler; import com.replaymod.replay.ReplayHandler;
import com.replaymod.replaystudio.data.Marker;
import com.replaymod.replaystudio.util.Location; import com.replaymod.replaystudio.util.Location;
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.element.advanced.AbstractGuiTimeline; import de.johni0702.minecraft.gui.element.advanced.AbstractGuiTimeline;
import de.johni0702.minecraft.gui.function.Draggable; import de.johni0702.minecraft.gui.function.Draggable;
import com.replaymod.replaystudio.data.Marker;
import net.minecraft.client.resources.I18n; import net.minecraft.client.resources.I18n;
import net.minecraft.util.MathHelper; import net.minecraft.util.math.MathHelper;
import org.lwjgl.util.Point; import org.lwjgl.util.Point;
import org.lwjgl.util.ReadableDimension; import org.lwjgl.util.ReadableDimension;
import org.lwjgl.util.ReadablePoint; import org.lwjgl.util.ReadablePoint;

View File

@@ -16,7 +16,7 @@ import de.johni0702.minecraft.gui.layout.CustomLayout;
import de.johni0702.minecraft.gui.layout.HorizontalLayout; import de.johni0702.minecraft.gui.layout.HorizontalLayout;
import net.minecraft.client.resources.I18n; import net.minecraft.client.resources.I18n;
import net.minecraft.client.settings.GameSettings; import net.minecraft.client.settings.GameSettings;
import net.minecraftforge.fml.common.FMLCommonHandler; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.InputEvent; import net.minecraftforge.fml.common.gameevent.InputEvent;
import org.lwjgl.input.Keyboard; import org.lwjgl.input.Keyboard;
@@ -138,9 +138,9 @@ public class GuiReplayOverlay extends AbstractGuiOverlay<GuiReplayOverlay> {
public void setVisible(boolean visible) { public void setVisible(boolean visible) {
if (isVisible() != visible) { if (isVisible() != visible) {
if (visible) { if (visible) {
FMLCommonHandler.instance().bus().register(this); MinecraftForge.EVENT_BUS.register(this);
} else { } else {
FMLCommonHandler.instance().bus().unregister(this); MinecraftForge.EVENT_BUS.unregister(this);
} }
} }
super.setVisible(visible); super.setVisible(visible);

View File

@@ -9,7 +9,6 @@ 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.FMLCommonHandler;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import java.io.IOException; import java.io.IOException;
@@ -18,7 +17,6 @@ import java.util.List;
public class GuiHandler { public class GuiHandler {
private static final int BUTTON_EXIT_SERVER = 1; private static final int BUTTON_EXIT_SERVER = 1;
private static final int BUTTON_RETURN_TO_GAME = 4;
private static final int BUTTON_ACHIEVEMENTS = 5; private static final int BUTTON_ACHIEVEMENTS = 5;
private static final int BUTTON_STATS = 6; private static final int BUTTON_STATS = 6;
private static final int BUTTON_OPEN_TO_LAN = 7; private static final int BUTTON_OPEN_TO_LAN = 7;
@@ -35,13 +33,12 @@ 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 injectIntoIngameMenu(GuiScreenEvent.InitGuiEvent.Post event) { public void injectIntoIngameMenu(GuiScreenEvent.InitGuiEvent.Post event) {
if (!(event.gui instanceof GuiIngameMenu)) { if (!(event.getGui() instanceof GuiIngameMenu)) {
return; return;
} }
@@ -49,8 +46,8 @@ public class GuiHandler {
// Pause replay when menu is opened // Pause replay when menu is opened
mod.getReplayHandler().getReplaySender().setReplaySpeed(0); mod.getReplayHandler().getReplaySender().setReplaySpeed(0);
@SuppressWarnings("unchecked") GuiButton achievements = null, stats = null, openToLan = null;
List<GuiButton> buttonList = event.buttonList; List<GuiButton> buttonList = event.getButtonList();
for(GuiButton b : new ArrayList<>(buttonList)) { for(GuiButton b : new ArrayList<>(buttonList)) {
switch (b.id) { switch (b.id) {
// Replace "Exit Server" button with "Exit Replay" button // Replace "Exit Server" button with "Exit Replay" button
@@ -60,45 +57,67 @@ public class GuiHandler {
break; break;
// Remove "Achievements", "Stats" and "Open to LAN" buttons // Remove "Achievements", "Stats" and "Open to LAN" buttons
case BUTTON_ACHIEVEMENTS: case BUTTON_ACHIEVEMENTS:
buttonList.remove(achievements = b);
break;
case BUTTON_STATS: case BUTTON_STATS:
buttonList.remove(stats = b);
break;
case BUTTON_OPEN_TO_LAN: case BUTTON_OPEN_TO_LAN:
buttonList.remove(b); buttonList.remove(openToLan = b);
} break;
// Move all buttons except the "Return to game" button upwards
if (b.id != BUTTON_RETURN_TO_GAME) {
b.yPosition -= 48;
} }
} }
if (achievements != null && stats != null) {
moveAllButtonsDirectlyBelowUpwards(buttonList, achievements.yPosition,
achievements.xPosition, stats.xPosition + stats.width);
}
if (openToLan != null) {
moveAllButtonsDirectlyBelowUpwards(buttonList, openToLan.yPosition,
openToLan.xPosition, openToLan.xPosition + openToLan.width);
}
}
}
/**
* Moves all buttons that are within a rectangle below a certain y coordinate upwards by 24 units.
* @param buttons List of buttons
* @param belowY The Y limit
* @param xStart Left x limit of the rectangle
* @param xEnd Right x limit of the rectangle
*/
private void moveAllButtonsDirectlyBelowUpwards(List<GuiButton> buttons, int belowY, int xStart, int xEnd) {
for (GuiButton button : buttons) {
if (button.yPosition >= belowY && button.xPosition <= xEnd && button.xPosition + button.width >= xStart) {
button.yPosition -= 24;
}
} }
} }
@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_VIEWER, event.getGui().width / 2 - 100,
List<GuiButton> buttonList = event.buttonList; event.getGui().height / 4 + 10 + 3 * 24, I18n.format("replaymod.gui.replayviewer"));
GuiButton button = new GuiButton(BUTTON_REPLAY_VIEWER, event.gui.width / 2 - 100,
event.gui.height / 4 + 10 + 3 * 24, I18n.format("replaymod.gui.replayviewer"));
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_VIEWER) { if (event.getButton().id == BUTTON_REPLAY_VIEWER) {
new GuiReplayViewer(mod).display(); new GuiReplayViewer(mod).display();
} }
} }
if (event.gui instanceof GuiIngameMenu && mod.getReplayHandler() != null) { if (event.getGui() instanceof GuiIngameMenu && mod.getReplayHandler() != null) {
if (event.button.id == BUTTON_EXIT_REPLAY) { if (event.getButton().id == BUTTON_EXIT_REPLAY) {
event.button.enabled = false; event.getButton().enabled = false;
mc.displayGuiScreen(new GuiMainMenu()); mc.displayGuiScreen(new GuiMainMenu());
try { try {
mod.getReplayHandler().endReplay(); mod.getReplayHandler().endReplay();

View File

@@ -12,12 +12,12 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(GuiSpectator.class) @Mixin(GuiSpectator.class)
public abstract class MixinGuiSpectator { public abstract class MixinGuiSpectator {
@Shadow @Shadow
private Minecraft field_175268_g; private Minecraft mc;
@Inject(method = "func_175260_a", at = @At("HEAD"), cancellable = true) @Inject(method = "onHotbarSelected", at = @At("HEAD"), cancellable = true)
public void isInReplay(int i, CallbackInfo ci) { public void isInReplay(int i, CallbackInfo ci) {
// Prevent spectator gui from opening while in a replay // Prevent spectator gui from opening while in a replay
if (field_175268_g.thePlayer instanceof CameraEntity) { if (mc.thePlayer instanceof CameraEntity) {
ci.cancel(); ci.cancel();
} }
} }

View File

@@ -0,0 +1,31 @@
package com.replaymod.replay.mixin;
import net.minecraft.client.particle.Particle;
import net.minecraft.client.particle.ParticleManager;
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.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Queue;
@Mixin(ParticleManager.class)
public abstract class MixinParticleManager {
@Shadow
private Queue<Particle> queueEntityFX;
/**
* This method additionally clears the queue of particles to be added when the world is changed.
* Otherwise particles from the previous world might show up in this one if they were spawned after
* the last tick in the previous world.
*
* @param world The new world
* @param ci Callback info
*/
@Inject(method = "clearEffects", at = @At("HEAD"))
public void replayModReplay_clearParticleQueue(World world, CallbackInfo ci) {
queueEntityFX.clear();
}
}

View File

@@ -1,12 +1,12 @@
package com.replaymod.replay.mixin; package com.replaymod.replay.mixin;
import com.replaymod.replay.camera.CameraEntity;
import com.replaymod.replay.ReplayModReplay; import com.replaymod.replay.ReplayModReplay;
import com.replaymod.replay.camera.CameraEntity;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.EntityPlayerSP; import net.minecraft.client.entity.EntityPlayerSP;
import net.minecraft.client.multiplayer.PlayerControllerMP; import net.minecraft.client.multiplayer.PlayerControllerMP;
import net.minecraft.client.network.NetHandlerPlayClient; import net.minecraft.client.network.NetHandlerPlayClient;
import net.minecraft.stats.StatFileWriter; import net.minecraft.stats.StatisticsManager;
import net.minecraft.world.World; import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
@@ -21,12 +21,12 @@ public abstract class MixinPlayerControllerMP {
private Minecraft mc; private Minecraft mc;
@Shadow @Shadow
private NetHandlerPlayClient netClientHandler; private NetHandlerPlayClient connection;
@Inject(method = "func_178892_a", at=@At("HEAD"), cancellable = true) @Inject(method = "createClientPlayer", at=@At("HEAD"), cancellable = true)
private void replayModReplay_createReplayCamera(World worldIn, StatFileWriter statFileWriter, CallbackInfoReturnable<EntityPlayerSP> ci) { private void replayModReplay_createReplayCamera(World worldIn, StatisticsManager statisticsManager, CallbackInfoReturnable<EntityPlayerSP> ci) {
if (ReplayModReplay.instance.getReplayHandler() != null) { if (ReplayModReplay.instance.getReplayHandler() != null) {
ci.setReturnValue(new CameraEntity(mc, worldIn, netClientHandler, statFileWriter)); ci.setReturnValue(new CameraEntity(mc, worldIn, connection, statisticsManager));
ci.cancel(); ci.cancel();
} }
} }

View File

@@ -2,7 +2,7 @@ package com.replaymod.replay.mixin;
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.entity.ArmorStandRenderer; import net.minecraft.client.renderer.entity.RenderArmorStand;
import net.minecraft.entity.item.EntityArmorStand; import net.minecraft.entity.item.EntityArmorStand;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
@@ -10,9 +10,9 @@ 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.CallbackInfoReturnable;
@Mixin(ArmorStandRenderer.class) @Mixin(RenderArmorStand.class)
public abstract class MixinArmorStandRenderer { public abstract class MixinRenderArmorStand {
@Inject(method = "func_177099_b", at = @At("HEAD"), cancellable = true) @Inject(method = "canRenderName", at = @At("HEAD"), cancellable = true)
private void replayModReplay_canRenderInvisibleName(EntityArmorStand entity, CallbackInfoReturnable<Boolean> ci) { private void replayModReplay_canRenderInvisibleName(EntityArmorStand entity, CallbackInfoReturnable<Boolean> ci) {
EntityPlayer thePlayer = Minecraft.getMinecraft().thePlayer; EntityPlayer thePlayer = Minecraft.getMinecraft().thePlayer;
if (thePlayer instanceof CameraEntity && entity.isInvisible()) { if (thePlayer instanceof CameraEntity && entity.isInvisible()) {

View File

@@ -14,6 +14,7 @@ public abstract class MixinRenderArrow extends Render {
super(renderManager); super(renderManager);
} }
@SuppressWarnings("unchecked")
@Override @Override
public boolean shouldRender(Entity entity, ICamera camera, double camX, double camY, double camZ) { public boolean shouldRender(Entity entity, ICamera camera, double camX, double camY, double camZ) {
// Force arrows to always render, otherwise they stop rendering when you get close to them // Force arrows to always render, otherwise they stop rendering when you get close to them

View File

@@ -3,7 +3,7 @@ package com.replaymod.replay.mixin;
import com.replaymod.replay.ReplayHandler; import com.replaymod.replay.ReplayHandler;
import com.replaymod.replay.ReplayModReplay; import com.replaymod.replay.ReplayModReplay;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.entity.RenderItem; import net.minecraft.client.renderer.RenderItem;
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.Redirect; import org.spongepowered.asm.mixin.injection.Redirect;

View File

@@ -2,7 +2,7 @@ package com.replaymod.replay.mixin;
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.entity.RendererLivingEntity; import net.minecraft.client.renderer.entity.RenderLivingBase;
import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
@@ -11,8 +11,8 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(RendererLivingEntity.class) @Mixin(RenderLivingBase.class)
public abstract class MixinRendererLivingEntity { public abstract class MixinRenderLivingBase {
@Inject(method = "canRenderName", at = @At("HEAD"), cancellable = true) @Inject(method = "canRenderName", at = @At("HEAD"), cancellable = true)
private void replayModReplay_canRenderInvisibleName(EntityLivingBase entity, CallbackInfoReturnable<Boolean> ci) { private void replayModReplay_canRenderInvisibleName(EntityLivingBase entity, CallbackInfoReturnable<Boolean> ci) {
EntityPlayer thePlayer = Minecraft.getMinecraft().thePlayer; EntityPlayer thePlayer = Minecraft.getMinecraft().thePlayer;

View File

@@ -10,7 +10,7 @@ import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(TileEntityEndPortalRenderer.class) @Mixin(TileEntityEndPortalRenderer.class)
public class MixinTileEntityEndPortalRenderer { public class MixinTileEntityEndPortalRenderer {
@Redirect(method = "func_180544_a", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;getSystemTime()J")) @Redirect(method = "renderTileEntityAt", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;getSystemTime()J"))
private long replayModReplay_getEnchantmentTime() { private long replayModReplay_getEnchantmentTime() {
ReplayHandler replayHandler = ReplayModReplay.instance.getReplayHandler(); ReplayHandler replayHandler = ReplayModReplay.instance.getReplayHandler();
if (replayHandler != null) { if (replayHandler != null) {

View File

@@ -5,8 +5,8 @@ import net.minecraft.client.renderer.RenderGlobal;
import net.minecraft.client.renderer.ViewFrustum; import net.minecraft.client.renderer.ViewFrustum;
import net.minecraft.client.renderer.chunk.IRenderChunkFactory; import net.minecraft.client.renderer.chunk.IRenderChunkFactory;
import net.minecraft.client.renderer.chunk.RenderChunk; import net.minecraft.client.renderer.chunk.RenderChunk;
import net.minecraft.util.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.MathHelper; import net.minecraft.util.math.MathHelper;
import net.minecraft.world.World; import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
@@ -31,7 +31,7 @@ public abstract class MixinViewFrustum {
} }
/** /**
* Instead of calling {@link RenderChunk#setPosition(BlockPos)} we recreate the render chunk * Instead of calling {@link RenderChunk#setOrigin(int, int, int)} we recreate the render chunk
* which seems to solve the problem that chunks are invisible when you leave an area and return * which seems to solve the problem that chunks are invisible when you leave an area and return
* to it. * to it.
* Any better fixes are welcome. * Any better fixes are welcome.
@@ -48,17 +48,18 @@ public abstract class MixinViewFrustum {
int k = this.countChunksX * 16; int k = this.countChunksX * 16;
for (int l = 0; l < this.countChunksX; ++l) { for (int l = 0; l < this.countChunksX; ++l) {
int i1 = this.func_178157_a(i, k, l); int i1 = this.getBaseCoordinate(i, k, l);
for (int j1 = 0; j1 < this.countChunksZ; ++j1) { for (int j1 = 0; j1 < this.countChunksZ; ++j1) {
int k1 = this.func_178157_a(j, k, j1); int k1 = this.getBaseCoordinate(j, k, j1);
for (int l1 = 0; l1 < this.countChunksY; ++l1) { for (int l1 = 0; l1 < this.countChunksY; ++l1) {
int i2 = l1 * 16; int i2 = l1 * 16;
RenderChunk renderchunk = this.renderChunks[(j1 * this.countChunksY + l1) * this.countChunksX + l]; RenderChunk renderchunk = this.renderChunks[(j1 * this.countChunksY + l1) * this.countChunksX + l];
BlockPos blockpos = new BlockPos(i1, i2, k1); BlockPos blockpos = new BlockPos(i1, i2, k1);
if (!blockpos.equals(renderchunk.getPosition())) { if (!blockpos.equals(renderchunk.getPosition())) {
// Recreate render chunk instead of setting its position // Recreate render chunk instead of setting its position
renderChunks[(j1 * this.countChunksY + l1) * this.countChunksX + l] = (renderChunks[(j1 * this.countChunksY + l1) * this.countChunksX + l] =
renderChunkFactory.makeRenderChunk(world, renderGlobal, blockpos, 0); renderChunkFactory.create(world, renderGlobal, 0)
).setOrigin(blockpos.getX(), blockpos.getY(), blockpos.getZ());
} }
} }
} }
@@ -66,5 +67,5 @@ public abstract class MixinViewFrustum {
ci.cancel(); ci.cancel();
} }
@Shadow abstract int func_178157_a(int p_178157_1_, int p_178157_2_, int p_178157_3_); @Shadow abstract int getBaseCoordinate(int p_178157_1_, int p_178157_2_, int p_178157_3_);
} }

View File

@@ -9,11 +9,12 @@ import com.replaymod.simplepathing.SPTimeline.SPPath;
import com.replaymod.simplepathing.gui.GuiPathing; import com.replaymod.simplepathing.gui.GuiPathing;
import com.replaymod.simplepathing.preview.PathPreview; import com.replaymod.simplepathing.preview.PathPreview;
import lombok.Getter; import lombok.Getter;
import net.minecraftforge.fml.common.FMLCommonHandler; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.lwjgl.input.Keyboard;
@Mod(modid = ReplayModSimplePathing.MOD_ID, @Mod(modid = ReplayModSimplePathing.MOD_ID,
version = "@MOD_VERSION@", version = "@MOD_VERSION@",
@@ -22,6 +23,9 @@ import org.apache.logging.log4j.Logger;
public class ReplayModSimplePathing { public class ReplayModSimplePathing {
public static final String MOD_ID = "replaymod-simplepathing"; public static final String MOD_ID = "replaymod-simplepathing";
@Mod.Instance(MOD_ID)
public static ReplayModSimplePathing instance;
private ReplayMod core; private ReplayMod core;
public static Logger LOGGER; public static Logger LOGGER;
@@ -35,15 +39,28 @@ public class ReplayModSimplePathing {
core.getSettingsRegistry().register(Setting.class); core.getSettingsRegistry().register(Setting.class);
FMLCommonHandler.instance().bus().register(this); MinecraftForge.EVENT_BUS.register(this);
PathPreview pathPreview = new PathPreview(this); PathPreview pathPreview = new PathPreview(this);
pathPreview.register(); pathPreview.register();
core.getKeyBindingRegistry().registerKeyBinding("replaymod.input.keyframerepository", Keyboard.KEY_X, () -> {
if (guiPathing != null) guiPathing.keyframeRepoButtonPressed();
});
core.getKeyBindingRegistry().registerKeyBinding("replaymod.input.clearkeyframes", Keyboard.KEY_C, () -> {
if (guiPathing != null) guiPathing.clearKeyframesButtonPressed();
});
core.getKeyBindingRegistry().registerRepeatedKeyBinding("replaymod.input.synctimeline", Keyboard.KEY_V, () -> {
if (guiPathing != null) guiPathing.syncTimeButtonPressed();
});
core.getKeyBindingRegistry().registerRaw(Keyboard.KEY_DELETE, () -> {
if (guiPathing != null) guiPathing.deleteButtonPressed();
});
} }
@SubscribeEvent @SubscribeEvent
public void postReplayOpen(ReplayOpenEvent.Post event) { public void postReplayOpen(ReplayOpenEvent.Post event) {
currentTimeline = new SPTimeline(); clearCurrentTimeline();
guiPathing = new GuiPathing(core, this, event.getReplayHandler()); guiPathing = new GuiPathing(core, this, event.getReplayHandler());
} }
@@ -82,11 +99,11 @@ public class ReplayModSimplePathing {
public void setCurrentTimeline(SPTimeline newTimeline) { public void setCurrentTimeline(SPTimeline newTimeline) {
selectedPath = null; selectedPath = null;
currentTimeline = newTimeline; currentTimeline = newTimeline;
updateDefaultInterpolatorType();
} }
public void clearCurrentTimeline() { public void clearCurrentTimeline() {
setCurrentTimeline(new SPTimeline()); setCurrentTimeline(new SPTimeline());
updateDefaultInterpolatorType();
} }
public SPTimeline getCurrentTimeline() { public SPTimeline getCurrentTimeline() {

View File

@@ -71,7 +71,7 @@ public class SPTimeline implements PathingRegistry {
@Getter @Getter
private EntityPositionTracker entityTracker; private EntityPositionTracker entityTracker;
private InterpolatorType defaultInterpolatorType = InterpolatorType.fromString("invalid string returns default"); private InterpolatorType defaultInterpolatorType;
public SPTimeline() { public SPTimeline() {
this(createInitialTimeline()); this(createInitialTimeline());
@@ -340,13 +340,15 @@ public class SPTimeline implements PathingRegistry {
Optional<Interpolator> firstInterpolator = Optional<Interpolator> firstInterpolator =
path.getSegments().stream().findFirst().map(PathSegment::getInterpolator); path.getSegments().stream().findFirst().map(PathSegment::getInterpolator);
// The interpolator of the previous segment // The interpolator that will be lost once we remove the old keyframe and has to be restored afterwards
Optional<Interpolator> interpolatorBefore = Optional<Interpolator> lostInterpolator = path.getSegments().stream().filter(s -> {
path.getSegments().stream().filter(s -> s.getEndKeyframe() == keyframe).findFirst().map(PathSegment::getInterpolator); // If this is the last keyframe,
if (Iterables.getLast(path.getKeyframes()) == keyframe) {
// The interpolator of the following segment return s.getEndKeyframe() == keyframe; // the previous interpolator will be lost
Optional<Interpolator> interpolatorAfter = } else { // otherwise
path.getSegments().stream().filter(s -> s.getStartKeyframe() == keyframe).findFirst().map(PathSegment::getInterpolator); return s.getStartKeyframe() == keyframe; // the following interpolator will be lost
}
}).findFirst().map(PathSegment::getInterpolator);
// First remove the old keyframe // First remove the old keyframe
Change removeChange = create(path, keyframe); Change removeChange = create(path, keyframe);
@@ -369,18 +371,27 @@ public class SPTimeline implements PathingRegistry {
Keyframe newKf = path.getKeyframe(newTime); Keyframe newKf = path.getKeyframe(newTime);
if (Iterables.getLast(path.getKeyframes()) != newKf) { // Unless this is the last keyframe if (Iterables.getLast(path.getKeyframes()) != newKf) { // Unless this is the last keyframe
// the interpolator of the following segment has been lost and needs to be restored // the interpolator of the following segment has been lost and needs to be restored
restoreInterpolatorChange = interpolatorAfter.<Change>flatMap(interpolator -> restoreInterpolatorChange = lostInterpolator.<Change>flatMap(interpolator ->
path.getSegments().stream().filter(s -> s.getStartKeyframe() == newKf).findFirst().map(segment -> path.getSegments().stream().filter(s -> s.getStartKeyframe() == newKf).findFirst().map(segment ->
SetInterpolator.create(segment, interpolator) SetInterpolator.create(segment, interpolator)
) )
).orElseGet(CombinedChange::create); ).orElseGet(CombinedChange::create);
} else { // If it is the last keyframe however, } else { // If it is the last keyframe however,
// the interpolator of the previous segment has been lost and needs to be restored // the interpolator of the previous segment has been lost and needs to be restored
restoreInterpolatorChange = interpolatorBefore.<Change>flatMap(interpolator -> restoreInterpolatorChange = path.getSegments().stream().filter(s -> s.getEndKeyframe() == newKf)
path.getSegments().stream().filter(s -> s.getEndKeyframe() == newKf).findFirst().map(segment -> .findFirst().flatMap(segment -> lostInterpolator.map(interpolator -> {
SetInterpolator.create(segment, interpolator) // additionally, if the interpolation of this keyframe was set to explicit, that property
) // has to be transferred to the start keyframe of the new segment
).orElseGet(CombinedChange::create); if (newKf.getValue(ExplicitInterpolationProperty.PROPERTY).isPresent()) {
return CombinedChange.create(
SetInterpolator.create(segment, interpolator),
UpdateKeyframeProperties.create(path, segment.getStartKeyframe())
.setValue(ExplicitInterpolationProperty.PROPERTY, ObjectUtils.NULL).done()
);
} else {
return SetInterpolator.create(segment, interpolator);
}
})).orElseGet(CombinedChange::create);
} }
restoreInterpolatorChange.apply(timeline); restoreInterpolatorChange.apply(timeline);
@@ -647,7 +658,7 @@ public class SPTimeline implements PathingRegistry {
} catch (IOException e) { } catch (IOException e) {
CrashReport crash = CrashReport.makeCrashReport(e, "Serializing interpolator"); CrashReport crash = CrashReport.makeCrashReport(e, "Serializing interpolator");
CrashReportCategory category = crash.makeCategory("Serializing interpolator"); CrashReportCategory category = crash.makeCategory("Serializing interpolator");
category.addCrashSectionCallable("Interpolator", interpolator::toString); category.setDetail("Interpolator", interpolator::toString);
throw new ReportedException(crash); throw new ReportedException(crash);
} }

View File

@@ -109,6 +109,9 @@ public abstract class GuiEditKeyframe<T extends GuiEditKeyframe<T>> extends Abst
if (newTime != time) { if (newTime != time) {
change = CombinedChange.createFromApplied(change, change = CombinedChange.createFromApplied(change,
gui.getMod().getCurrentTimeline().moveKeyframe(path, time, newTime)); gui.getMod().getCurrentTimeline().moveKeyframe(path, time, newTime));
if (gui.getMod().getSelectedPath() == path && gui.getMod().getSelectedTime() == time) {
gui.getMod().setSelected(path, newTime);
}
} }
gui.getMod().getCurrentTimeline().getTimeline().pushChange(change); gui.getMod().getCurrentTimeline().getTimeline().pushChange(change);
close(); close();
@@ -252,6 +255,10 @@ public abstract class GuiEditKeyframe<T extends GuiEditKeyframe<T>> extends Abst
xField.getDouble(), yField.getDouble(), zField.getDouble(), xField.getDouble(), yField.getDouble(), zField.getDouble(),
yawField.getFloat(), pitchField.getFloat(), rollField.getFloat() yawField.getFloat(), pitchField.getFloat(), rollField.getFloat()
); );
if (interpolationPanel.getSettingsPanel() == null) {
// The last keyframe doesn't have interpolator settings because there is no segment following it
return positionChange;
}
Interpolator interpolator = interpolationPanel.getSettingsPanel().createInterpolator(); Interpolator interpolator = interpolationPanel.getSettingsPanel().createInterpolator();
if (interpolationPanel.getInterpolatorType() == InterpolatorType.DEFAULT) { if (interpolationPanel.getInterpolatorType() == InterpolatorType.DEFAULT) {
return CombinedChange.createFromApplied(positionChange, timeline.setInterpolatorToDefault(time), return CombinedChange.createFromApplied(positionChange, timeline.setInterpolatorToDefault(time),
@@ -303,6 +310,7 @@ public abstract class GuiEditKeyframe<T extends GuiEditKeyframe<T>> extends Abst
dropdown.setSelected(type); // trigger the callback once to display settings panel dropdown.setSelected(type); // trigger the callback once to display settings panel
} else { } else {
setSettingsPanel(InterpolatorType.DEFAULT); setSettingsPanel(InterpolatorType.DEFAULT);
type = InterpolatorType.DEFAULT;
} }
if (getInterpolatorTypeNoDefault(type).getInterpolatorClass().isInstance(interpolator)) { if (getInterpolatorTypeNoDefault(type).getInterpolatorClass().isInstance(interpolator)) {
//noinspection unchecked //noinspection unchecked

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