Compare commits

..

41 Commits

Author SHA1 Message Date
Jonas Herzig
689f897930 Merge branch 1.9.4 into 1.10.2
0aed9c3 Merge branch 1.8.9 into 1.9.4
f36ebf1 Merge branch 1.8 into 1.8.9
08170b3 Downgrade FG to 2.1 because FG 2.2 is 1.9+ only
41d2547 Update translations
00e999f Fix replay not being closed when opened via URI scheme handler (fixes #92)
767ea29 Fix book gui not being suppressed during replay (fixes #90)
5b04edb Hide ReplayMod app entry from menus on Linux
6aff99d Fix livelock during netty write to embedded channel (fixes #85)
703805f Fix infinite loop in mc.scheduledTasks (fixes #86)
7a4440c Update ReplayStudio
0b9c56c Fix resource packs not working after first time jumping back in time
933ee5f [Compat] Fix camera entity with BetterSprinting prior to 2.0.0 (fixes #78)
83b1090 Fix hotbar being visible while spectating player (fixes #83)
5c73117 [Compat] Fix invisible entities with Orange's 1.7 Animations (fixes #78)
4704b29 Replace the RenderPlayer hook (used subclassing) with a mixin (fixes #79)
0c226b0 Fix path at end of replay resulting in constant reloading (fixes #56)
1b9b13e Rename render success sound to all lowercase (required for 1.11) (fixes #66)
c2000b3 Fix only front-facing chunks visible for ODS rendering (fixes #67)
aafeecc Fix initial login gui not closing on success (fixes #68)
9292add Use percent-encoding for replay file names (fixes #71)
8499c0f Fix name of invis armor stand with CustomNameVisible not rendering (fixes #72)
b9ea572 Try to handle invalid ffmpeg arguments more gracefully (fixes #77)
a2f8c88 Fix compression packets being recorded (fixes #80)
19629c3 Replace usages of System.out with logger
fe1d9b8 Stop Forge from allowing the mod to load on other MC versions (fixes #82)
1a1e96c Fix server with RM installed refusing clients without RM (fixes #76)
35eb9ca Replace usage of FMLLog with the mod logger
2017-08-26 14:17:14 +02:00
Jonas Herzig
0aed9c3c17 Merge branch 1.8.9 into 1.9.4
f36ebf1 Merge branch 1.8 into 1.8.9
08170b3 Downgrade FG to 2.1 because FG 2.2 is 1.9+ only
41d2547 Update translations
00e999f Fix replay not being closed when opened via URI scheme handler (fixes #92)
767ea29 Fix book gui not being suppressed during replay (fixes #90)
5b04edb Hide ReplayMod app entry from menus on Linux
6aff99d Fix livelock during netty write to embedded channel (fixes #85)
703805f Fix infinite loop in mc.scheduledTasks (fixes #86)
7a4440c Update ReplayStudio
0b9c56c Fix resource packs not working after first time jumping back in time
933ee5f [Compat] Fix camera entity with BetterSprinting prior to 2.0.0 (fixes #78)
83b1090 Fix hotbar being visible while spectating player (fixes #83)
5c73117 [Compat] Fix invisible entities with Orange's 1.7 Animations (fixes #78)
4704b29 Replace the RenderPlayer hook (used subclassing) with a mixin (fixes #79)
0c226b0 Fix path at end of replay resulting in constant reloading (fixes #56)
1b9b13e Rename render success sound to all lowercase (required for 1.11) (fixes #66)
c2000b3 Fix only front-facing chunks visible for ODS rendering (fixes #67)
aafeecc Fix initial login gui not closing on success (fixes #68)
9292add Use percent-encoding for replay file names (fixes #71)
8499c0f Fix name of invis armor stand with CustomNameVisible not rendering (fixes #72)
b9ea572 Try to handle invalid ffmpeg arguments more gracefully (fixes #77)
a2f8c88 Fix compression packets being recorded (fixes #80)
19629c3 Replace usages of System.out with logger
fe1d9b8 Stop Forge from allowing the mod to load on other MC versions (fixes #82)
1a1e96c Fix server with RM installed refusing clients without RM (fixes #76)
35eb9ca Replace usage of FMLLog with the mod logger
2017-08-26 14:16:08 +02:00
Jonas Herzig
f36ebf1d0a Merge branch 1.8 into 1.8.9
41d2547 Update translations
00e999f Fix replay not being closed when opened via URI scheme handler (fixes #92)
767ea29 Fix book gui not being suppressed during replay (fixes #90)
5b04edb Hide ReplayMod app entry from menus on Linux
6aff99d Fix livelock during netty write to embedded channel (fixes #85)
703805f Fix infinite loop in mc.scheduledTasks (fixes #86)
7a4440c Update ReplayStudio
0b9c56c Fix resource packs not working after first time jumping back in time
933ee5f [Compat] Fix camera entity with BetterSprinting prior to 2.0.0 (fixes #78)
83b1090 Fix hotbar being visible while spectating player (fixes #83)
5c73117 [Compat] Fix invisible entities with Orange's 1.7 Animations (fixes #78)
4704b29 Replace the RenderPlayer hook (used subclassing) with a mixin (fixes #79)
0c226b0 Fix path at end of replay resulting in constant reloading (fixes #56)
1b9b13e Rename render success sound to all lowercase (required for 1.11) (fixes #66)
c2000b3 Fix only front-facing chunks visible for ODS rendering (fixes #67)
aafeecc Fix initial login gui not closing on success (fixes #68)
9292add Use percent-encoding for replay file names (fixes #71)
8499c0f Fix name of invis armor stand with CustomNameVisible not rendering (fixes #72)
b9ea572 Try to handle invalid ffmpeg arguments more gracefully (fixes #77)
a2f8c88 Fix compression packets being recorded (fixes #80)
19629c3 Replace usages of System.out with logger
fe1d9b8 Stop Forge from allowing the mod to load on other MC versions (fixes #82)
1a1e96c Fix server with RM installed refusing clients without RM (fixes #76)
35eb9ca Replace usage of FMLLog with the mod logger
2017-08-26 13:37:59 +02:00
Jonas Herzig
08170b38ef Downgrade FG to 2.1 because FG 2.2 is 1.9+ only 2017-08-26 13:35:14 +02:00
Jonas Herzig
7c719e03cc Update ReplayStudio/MCProtocolLib (Fixes missing ARMOR_STAND MobType) 2017-07-27 15:20:34 +02:00
Jonas Herzig
96b2904e4b Merge branch '1.9.4' into 1.10.2
6c8c779 Merge branch '1.8.9' into 1.9.4
aa16e42 Add workaround for setupCIWorkspace not being sufficient
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 21:10:35 +02:00
Jonas Herzig
6c8c779983 Merge branch '1.8.9' into 1.9.4
ed56673 Merge branch '1.8' into 1.8.9
91573d9 Update jGui, ReplayStudio and Translations
d71358b Add confirmation dialog before overwriting path presets (fixes #65)
1a55983 Move addCallback method from integration test Utils to main Utils
18c5bcd Be more cooperative with other mods on the ingame menu (fixes #42)
c054fe8 Register keys for simplepathing only once (fixes #63)
f13297c Fix default interpolator handling in keyframe gui and after loading (fixes #64)
391f304 Show error popup if entity tracker fails to load
0e0eaaa Fix entity tracker being set even if it failed loading (fixes #50)
a34bbbc Fix NPE when spectating a player without a camera entity
60879fb Change local class to be an inner class because Srg2Source breaks with it
dfafbec Load translations from github repo, only reload language not all resource packs
8a6ec51 Add missing tooltips to player overview and various missing chat messages
5677fc5 Re-add warning to replay editor
748a91e Fix missing tooltips on fav/like/dislike buttons in replay center
ee24866 Fix upload replay button not being updated when name/tags change
dba085c Move translations into separate repo
3f0e3e7 Fix NPE when receiving Replay|Restrict messages (fixes #16 GH)
40e1d85 Fix crash when saving the last keyframe in the edit gui (fixes #61)
03aada1 Update Mixin to 0.6.8 (fixes #9 GH)
0c1dc65 Fix interpolator being lost when moving keyframe to the end (fixes #62)
fcbbbc9 Add integration test. Run with ./gradlew runIntegrationTest
fe6ded0 Fix movement of keyframe via GuiEditKeyframe not updating selected keyframe
f58fa8f Fix Login GUI not respecting other GUIs opened on startup
4fc3a31 Fix OpenEye being installed into working dir instead of mcDataDir
2017-05-31 20:49:04 +02:00
Jonas Herzig
aa16e42a4f Add workaround for setupCIWorkspace not being sufficient 2017-05-31 11:19:22 +02:00
Jonas Herzig
ed566732df Merge branch '1.8' into 1.8.9
91573d9 Update jGui, ReplayStudio and Translations
d71358b Add confirmation dialog before overwriting path presets (fixes #65)
1a55983 Move addCallback method from integration test Utils to main Utils
18c5bcd Be more cooperative with other mods on the ingame menu (fixes #42)
c054fe8 Register keys for simplepathing only once (fixes #63)
f13297c Fix default interpolator handling in keyframe gui and after loading (fixes #64)
391f304 Show error popup if entity tracker fails to load
0e0eaaa Fix entity tracker being set even if it failed loading (fixes #50)
a34bbbc Fix NPE when spectating a player without a camera entity
60879fb Change local class to be an inner class because Srg2Source breaks with it
dfafbec Load translations from github repo, only reload language not all resource packs
8a6ec51 Add missing tooltips to player overview and various missing chat messages
5677fc5 Re-add warning to replay editor
748a91e Fix missing tooltips on fav/like/dislike buttons in replay center
ee24866 Fix upload replay button not being updated when name/tags change
dba085c Move translations into separate repo
3f0e3e7 Fix NPE when receiving Replay|Restrict messages (fixes #16 GH)
40e1d85 Fix crash when saving the last keyframe in the edit gui (fixes #61)
03aada1 Update Mixin to 0.6.8 (fixes #9 GH)
0c1dc65 Fix interpolator being lost when moving keyframe to the end (fixes #62)
fcbbbc9 Add integration test. Run with ./gradlew runIntegrationTest
fe6ded0 Fix movement of keyframe via GuiEditKeyframe not updating selected keyframe
f58fa8f Fix Login GUI not respecting other GUIs opened on startup
4fc3a31 Fix OpenEye being installed into working dir instead of mcDataDir
2017-05-31 09:36:46 +02:00
johni0702
325d041bd9 Merge branch '1.9.4' into 1.10.2
d9d57e2 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 13:01:09 +01: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
c6dec95a57 Merge branch '1.9.4' into 1.10.2
4fdfa9e Merge branch '1.8.9' into 1.9.4
44d4a25 Merge branch '1.8' into 1.9.4
01acbc6 Update to 1.8.9
1c60687 Update ReplayStudio and jGui
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:56:14 +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
905ae75330 Merge branch '1.9.4' into 1.10.2
72e1fe0 Merge branch '1.8' into 1.9.4
6163585 Update ReplayStudio to fix dependencies
2016-12-16 21:47:30 +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
372206fdff Merge branch '1.9.4' into 1.10.2
4a1ccea 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:54:55 +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
9f7ccb9d07 Merge branch '1.9.4' into 1.10.2
ca2bbca 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)
75c0e7b Fix particles being visible after world change when jumping in time (fixes #35)
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:49:35 +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
ce313596db Add 1.10.2 version to ReplayCenter search (fixes #38) 2016-11-12 19:30:26 +01:00
johni0702
b1661e359a Merge branch '1.9.4' into 1.10.2
e20dfa6 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:15:54 +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
7cc0ee523a Merge branch '1.9.4' into 1.10.2
54790d3 Merge branch '1.8' into 1.9.4
0ba5b0a Merge branch '1.8-shaders' into 1.8
1658617 Merge branch '1.8' into 1.9.4
0db1b9e Fix opening a replay multiple times by quickly double clicking (fixes #25)
5014eda Remove javascript comments from mcmod.info
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
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 22:00:36 +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
09f167f10e Update to 1.10.2 2016-10-17 22:23:44 +02: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
126 changed files with 1994 additions and 1821 deletions

View File

@@ -7,7 +7,6 @@ pipeline:
commands:
- 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
- ./gradlew setupCIWorkspace
- ./gradlew :build
archive:
image: plugins/s3

View File

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

4
.gitmodules vendored
View File

@@ -2,6 +2,10 @@
path = jGui
url = https://github.com/ReplayMod/jGui
[submodule "ReplayStudio"]
path = 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

1
ReplayStudio Submodule

Submodule ReplayStudio added at c6d27bf662

View File

@@ -7,17 +7,23 @@ buildscript {
name = "forge"
url = "http://files.minecraftforge.net/maven"
}
maven {
name = "sponge"
url = "https://repo.spongepowered.org/maven"
}
maven {
name = "sonatype"
url = "https://oss.sonatype.org/content/repositories/snapshots/"
}
}
dependencies {
classpath 'net.minecraftforge.gradle:ForgeGradle:1.2-SNAPSHOT'
classpath 'net.minecraftforge.gradle:ForgeGradle:2.2-SNAPSHOT'
classpath 'org.spongepowered:mixingradle:0.4-SNAPSHOT'
}
}
apply plugin: 'forge'
apply plugin: 'net.minecraftforge.gradle.forge'
apply plugin: 'org.spongepowered.mixin'
sourceCompatibility = 1.8
targetCompatibility = 1.8
@@ -26,18 +32,11 @@ version = gitDescribe()
group= "com.replaymod"
archivesBaseName = "replaymod"
ext {
mixinConfigs = [
'compat.shaders', 'extras.playeroverview', 'recording', 'render', 'replay'
].collect {"mixins.${it}.replaymod.json"}
mixinSrg = new File(project.buildDir, 'tmp/mixins/mixins.srg')
mixinRefMap = new File(project.buildDir, 'tmp/mixins/mixins.replaymod.refmap.json')
}
minecraft {
version = '1.7.10-10.13.4.1558-1.7.10'
coreMod = 'com.replaymod.core.LoadingPlugin'
version = '1.10.2-12.18.2.2099'
runDir = "eclipse"
mappings = "stable_12"
mappings = "snapshot_20160518"
replace '@MOD_VERSION@', project.version
// Includes intentional whitespace to stop Forge from declaring the mod to be compatible with
// a newer srg-compatible MC version (that may be using a different protocol version)
@@ -50,7 +49,7 @@ repositories {
url = "http://repo.spongepowered.org/maven/"
}
maven {
url 'https://jitpack.io'
url 'https://repo.spacehq.org/content/repositories/snapshots/'
}
}
@@ -72,22 +71,24 @@ dependencies {
shade 'org.aspectj:aspectjrt:1.8.2'
shade 'com.github.replaymod:ReplayStudio:181140c'
compile project(':ReplayStudio')
testCompile 'junit:junit:4.11'
}
tasks['idea'].dependsOn ':ReplayStudio:preshadowJar'
jar {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
dependsOn configurations.compile
dependsOn configurations.shade
from project.mixinRefMap
dependsOn ':ReplayStudio:shadowJar'
def shade = {files(
configurations.compile.findAll {it.name.startsWith 'mixin-'}
+ configurations.shade
+ getTasks().getByPath(':ReplayStudio:shadowJar').outputs.files
)}
def noticeDir = file("$buildDir/NOTICE")
@@ -116,8 +117,7 @@ jar {
manifest {
attributes 'TweakClass': 'org.spongepowered.asm.launch.MixinTweaker',
'TweakOrder': '0',
'MixinConfigs': project.mixinConfigs.join(','),
'FMLCorePlugin': 'com.replaymod.core.LoadingPlugin',
'FMLCorePluginContainsFMLMod': 'true',
'FMLAT': 'replaymod_at.cfg'
}
}
@@ -157,8 +157,6 @@ processResources
sourceSets {
main {
// Required so resources are loaded correctly in dev env
output.resourcesDir = output.classesDir
// Directly include jGui into main build
// This could also be done by including it as a gradle subproject, however
// doing so caused classpath issues on some IntelliJ versions.
@@ -168,6 +166,7 @@ sourceSets {
resources {
srcDir 'jGui/src/main/resources'
}
refMap = "mixins.replaymod.refmap.json"
}
integrationTest {
compileClasspath += main.runtimeClasspath + main.output
@@ -183,25 +182,61 @@ task copySrg(type: Copy, dependsOn: 'genSrgs') {
into 'build'
}
reobf.doFirst {
if (project.mixinSrg.exists()) {
addExtraSrgFile project.mixinSrg
}
}
compileJava.dependsOn copySrg
compileJava {
options.compilerArgs += [
"-AoutSrgFile=${project.mixinSrg.canonicalPath}",
"-AoutRefMapFile=${project.mixinRefMap.canonicalPath}",
"-AreobfSrgFile=${project.file('build/mcp-srg.srg').canonicalPath}"
]
}
setupDecompWorkspace.dependsOn copySrg
setupDevWorkspace.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

2
jGui

Submodule jGui updated: 042e1c6e50...e2bd90628e

View File

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

View File

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

View File

@@ -10,10 +10,10 @@ import com.replaymod.recording.ExitSPWorld;
import com.replaymod.replay.ExitReplay;
import com.replaymod.replay.LoadReplay;
import com.replaymod.replay.OpenReplayViewer;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import net.minecraft.client.Minecraft;
import net.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;

View File

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

View File

@@ -1,33 +1,32 @@
package com.replaymod.recording;
import com.replaymod.core.AbstractTask;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import net.minecraft.client.gui.GuiCreateWorld;
import net.minecraft.client.gui.GuiMainMenu;
import net.minecraft.client.gui.GuiSelectWorld;
import net.minecraft.client.gui.GuiWorldSelection;
import net.minecraftforge.client.event.RenderGameOverlayEvent;
import net.minecraftforge.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(GuiSelectWorld.class, selectWorld -> {
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());
});
});
});
}
public class EventHandler {
@SubscribeEvent
public void onRenderIngame(RenderGameOverlayEvent.Pre event) {
MinecraftForge.EVENT_BUS.unregister(this);
runLater(() -> future.set(null));
}
}
}

View File

@@ -2,10 +2,10 @@ package com.replaymod.replay;
import com.replaymod.core.AbstractTask;
import com.replaymod.replay.gui.screen.GuiReplayViewer;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
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;
@@ -19,15 +19,14 @@ public class LoadReplay extends AbstractTask {
// 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());
}));
}
public class EventHandler {
@SubscribeEvent
public void onRenderIngame(RenderGameOverlayEvent.Pre event) {
MinecraftForge.EVENT_BUS.unregister(this);
runLater(() -> future.set(null));
}
}
}

View File

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

View File

@@ -3,10 +3,11 @@ package com.replaymod.compat;
import com.replaymod.compat.bettersprinting.DisableBetterSprinting;
import com.replaymod.compat.optifine.DisableFastRender;
import com.replaymod.compat.oranges17animations.HideInvisibleEntities;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.eventhandler.EventBus;
import com.replaymod.compat.shaders.ShaderBeginRender;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.eventhandler.EventBus;
@Mod(modid = ReplayModCompat.MOD_ID,
version = "@MOD_VERSION@",
@@ -18,7 +19,8 @@ public class ReplayModCompat {
@Mod.EventHandler
public void init(FMLInitializationEvent event) {
EventBus bus = FMLCommonHandler.instance().bus();
EventBus bus = MinecraftForge.EVENT_BUS;
bus.register(new ShaderBeginRender());
bus.register(new DisableFastRender());
bus.register(new HideInvisibleEntities());
DisableBetterSprinting.register();

View File

@@ -2,21 +2,27 @@ package com.replaymod.compat.bettersprinting;
import com.replaymod.replay.ReplayModReplay;
import com.replaymod.replay.events.ReplayChatMessageEvent;
import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.ModContainer;
import cpw.mods.fml.common.eventhandler.EventPriority;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.versioning.DefaultArtifactVersion;
import cpw.mods.fml.common.versioning.Restriction;
import cpw.mods.fml.common.versioning.VersionRange;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.PlayerControllerMP;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.world.IWorldAccess;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IWorldEventListener;
import net.minecraft.world.World;
import net.minecraftforge.client.event.GuiOpenEvent;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.ModContainer;
import net.minecraftforge.fml.common.eventhandler.EventPriority;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.versioning.DefaultArtifactVersion;
import net.minecraftforge.fml.common.versioning.Restriction;
import net.minecraftforge.fml.common.versioning.VersionRange;
import javax.annotation.Nullable;
import java.util.Collections;
/**
@@ -49,14 +55,14 @@ public class DisableBetterSprinting {
if (ReplayModReplay.instance.getReplayHandler() != null && mc.theWorld != null) {
// During replay, get ready to revert BetterSprinting's overwritten playerController
originalController = mc.playerController;
mc.theWorld.addWorldAccess(worldAccessHook);
mc.theWorld.addEventListener(worldAccessHook);
}
}
@SubscribeEvent(priority = EventPriority.LOW)
public void afterGuiOpenEvent(GuiOpenEvent event) {
if (ReplayModReplay.instance.getReplayHandler() != null && mc.theWorld != null) {
mc.theWorld.removeWorldAccess(worldAccessHook);
mc.theWorld.addEventListener(worldAccessHook);
}
}
@@ -71,9 +77,9 @@ public class DisableBetterSprinting {
}
}
private class BetterSprintingWorldAccess implements IWorldAccess {
private class BetterSprintingWorldAccess implements IWorldEventListener {
@Override
public void onEntityDestroy(Entity entityIn) {
public void onEntityRemoved(Entity entityIn) {
if (mc.playerController != null && mc.playerController.getClass().getName().equals(CONTROLLER_OVERRIDE_CLASS_NAME)) {
// Someone has secretly swapped out the player controller and is about to substitute their own player entity.
// This is the right time to destroy their plan.
@@ -81,17 +87,15 @@ public class DisableBetterSprinting {
}
}
@Override public void markBlockForUpdate(int x, int y, int z) {}
@Override public void markBlockForRenderUpdate(int p_147588_1_, int p_147588_2_, int p_147588_3_) {}
@Override public void notifyBlockUpdate(World worldIn, BlockPos pos, IBlockState oldState, IBlockState newState, int flags) {}
@Override public void notifyLightSet(BlockPos pos) {}
@Override public void markBlockRangeForRenderUpdate(int x1, int y1, int z1, int x2, int y2, int z2) {}
@Override public void playSound(String soundName, double x, double y, double z, float volume, float pitch) {}
@Override public void playSoundToNearExcept(EntityPlayer except, String soundName, double x, double y, double z, float volume, float pitch) {}
@Override public void spawnParticle(String p_180442_1_, double p_180442_2_, double p_180442_3_, double p_180442_5_, double p_180442_7_, double p_180442_9_, double p_180442_11_) {}
@Override public void onEntityCreate(Entity p_72703_1_) {}
@Override public void playRecord(String recordName, int x, int y, int z) {}
@Override public void broadcastSound(int p_180440_1_, int x, int y, int z, int p_180440_3_) {}
@Override public void playAuxSFX(EntityPlayer p_72706_1_, int p_72706_2_, int p_72706_3_, int p_72706_4_, int p_72706_5_, int p_72706_6_) {}
@Override public void destroyBlockPartially(int p_147587_1_, int p_147587_2_, int p_147587_3_, int p_147587_4_, int p_147587_5_) {}
@Override public void onStaticEntitiesChanged() {}
@Override public void playSoundToAllNearExcept(@Nullable EntityPlayer player, SoundEvent soundIn, SoundCategory category, double x, double y, double z, float volume, float pitch) {}
@Override public void playRecord(SoundEvent soundIn, BlockPos pos) {}
@Override public void spawnParticle(int p_180442_1_, boolean p_180442_2_, double p_180442_3_, double p_180442_5_, double p_180442_7_, double p_180442_9_, double p_180442_11_, double p_180442_13_, int... p_180442_15_) {}
@Override public void onEntityAdded(Entity entityIn) {}
@Override public void broadcastSound(int p_180440_1_, BlockPos p_180440_2_, int p_180440_3_) {}
@Override public void playEvent(EntityPlayer player, int type, BlockPos blockPosIn, int data) {}
@Override public void sendBlockBreakProgress(int breakerId, BlockPos pos, int progress) {}
}
}

View File

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

View File

@@ -1,11 +1,11 @@
package com.replaymod.compat.oranges17animations;
import com.replaymod.replay.camera.CameraEntity;
import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.eventhandler.EventPriority;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import net.minecraft.client.Minecraft;
import net.minecraftforge.client.event.RenderLivingEvent;
import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.eventhandler.EventPriority;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
/**
* Orange seems to have copied vast parts of the RendererLivingEntity into their ArmorAnimation class which cancels the RenderLivingEvent.Pre and calls its own code instead.
@@ -20,7 +20,7 @@ public class HideInvisibleEntities {
@SubscribeEvent(priority = EventPriority.HIGH)
public void preRenderLiving(RenderLivingEvent.Pre event) {
if (modLoaded) {
if (mc.thePlayer instanceof CameraEntity && event.entity.isInvisible()) {
if (mc.thePlayer instanceof CameraEntity && event.getEntity().isInvisible()) {
event.setCanceled(true);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
package com.replaymod.core;
import cpw.mods.fml.relauncher.CoreModManager;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
import net.minecraftforge.fml.relauncher.CoreModManager;
import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin;
import org.apache.logging.log4j.LogManager;
import org.spongepowered.asm.launch.MixinBootstrap;
import org.spongepowered.asm.mixin.Mixins;
@@ -10,8 +10,6 @@ import java.io.File;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.CodeSource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class LoadingPlugin implements IFMLLoadingPlugin {
@@ -32,8 +30,7 @@ public class LoadingPlugin implements IFMLLoadingPlugin {
if (file.isFile()) {
// This forces forge to reexamine the jar file for FML mods
// Should eventually be handled by Mixin itself, maybe?
CoreModManager.getLoadedCoremods().remove(file.getName());
CoreModManager.getReparseableCoremods().add(file.getName());
CoreModManager.getIgnoredMods().remove(file.getName());
}
} catch (URISyntaxException e) {
e.printStackTrace();
@@ -46,12 +43,8 @@ public class LoadingPlugin implements IFMLLoadingPlugin {
@Override
public String[] getASMTransformerClass() {
List<String> transformers = new ArrayList<>();
if ("true".equals(System.getProperty("replaymod.glerrors", "false"))) {
transformers.add(GLErrorTransformer.class.getName());
}
transformers.add(GLStateTrackerTransformer.class.getName());
return transformers.stream().toArray(String[]::new);
return new String[]{
};
}
@Override

View File

@@ -6,47 +6,41 @@ import com.replaymod.core.gui.GuiReplaySettings;
import com.replaymod.core.gui.RestoreReplayGui;
import com.replaymod.core.handler.MainMenuHandler;
import com.replaymod.core.utils.OpenGLUtils;
import com.replaymod.render.utils.SoundHandler;
import com.replaymod.replay.InputReplayTimer;
import com.replaymod.replaystudio.util.I18n;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.EventHandler;
import cpw.mods.fml.common.Mod.Instance;
import cpw.mods.fml.common.ModContainer;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPostInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.eventhandler.EventBus;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.TickEvent;
import de.johni0702.minecraft.gui.container.GuiScreen;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import net.minecraft.client.Minecraft;
import net.minecraft.client.settings.GameSettings;
import net.minecraft.crash.CrashReport;
import net.minecraft.crash.CrashReportCategory;
import net.minecraft.util.ChatComponentText;
import net.minecraft.util.ChatComponentTranslation;
import net.minecraft.util.ChatStyle;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.IChatComponent;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.text.*;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.fml.client.FMLClientHandler;
import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.Mod.EventHandler;
import net.minecraftforge.fml.common.Mod.Instance;
import net.minecraftforge.fml.common.ModContainer;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPostInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.eventhandler.EventBus;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.util.ArrayDeque;
import java.util.List;
import java.util.Queue;
@Mod(modid = ReplayMod.MOD_ID,
useMetadata = true,
version = "@MOD_VERSION@",
acceptedMinecraftVersions = "@MC_VERSION@",
acceptableRemoteVersions = "*",
updateJSON = "https://raw.githubusercontent.com/ReplayMod/ReplayMod/master/versions.json",
guiFactory = "com.replaymod.core.gui.GuiFactory")
public class ReplayMod {
@@ -59,10 +53,10 @@ public class ReplayMod {
private static String parseMinecraftVersion() {
CrashReport crashReport = new CrashReport("", new Throwable());
@SuppressWarnings("unchecked")
List<CrashReportCategory.Entry> list = crashReport.getCategory().field_85077_c;
List<CrashReportCategory.Entry> list = crashReport.getCategory().children;
for (CrashReportCategory.Entry entry : list) {
if ("Minecraft Version".equals(entry.func_85089_a())) {
return entry.func_85090_b();
if ("Minecraft Version".equals(entry.getKey())) {
return entry.getValue();
}
}
return "Unknown";
@@ -77,8 +71,6 @@ public class ReplayMod {
@Deprecated
public static Configuration config;
@Deprecated
public static SoundHandler soundHandler = new SoundHandler();
private final KeyBindingRegistry keyBindingRegistry = new KeyBindingRegistry();
private final SettingsRegistry settingsRegistry = new SettingsRegistry();
@@ -121,8 +113,7 @@ public class ReplayMod {
new MainMenuHandler().register();
FMLCommonHandler.instance().bus().register(this);
FMLCommonHandler.instance().bus().register(keyBindingRegistry);
MinecraftForge.EVENT_BUS.register(keyBindingRegistry);
getKeyBindingRegistry().registerKeyBinding("replaymod.input.settings", 0, () -> {
new GuiReplaySettings(null, settingsRegistry).display();
@@ -133,155 +124,10 @@ public class ReplayMod {
public void postInit(FMLPostInitializationEvent event) throws IOException {
settingsRegistry.save(); // Save default values to disk
/* TODO 1.7.10: MC crashes when setting render distance to > 16
if(!FMLClientHandler.instance().hasOptifine())
GameSettings.Options.RENDER_DISTANCE.setValueMax(64f);
*/
if (System.getProperty("replaymod.render.file") != null) {
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();
}
testIfMoeshAndExitMinecraft();
runLater(() -> {
// Cleanup deleted corrupted replays
@@ -324,17 +170,22 @@ public class ReplayMod {
*/
private boolean inRunLater = false;
// 1.7.10: Cannot use MC's because it is processed only during ticks (so not at all when replay is paused)
private final Queue<ListenableFutureTask<?>> scheduledTasks = new ArrayDeque<>();
public void runLater(Runnable runnable) {
if (mc.isCallingFromMinecraftThread() && inRunLater) {
EventBus bus = FMLCommonHandler.instance().bus();
bus.register(new RunLaterHelper(runnable));
EventBus bus = MinecraftForge.EVENT_BUS;
bus.register(new Object() {
@SubscribeEvent
public void onRenderTick(TickEvent.RenderTickEvent event) {
if (event.phase == TickEvent.Phase.START) {
runLater(runnable);
bus.unregister(this);
}
}
});
return;
}
synchronized (scheduledTasks) {
scheduledTasks.add(ListenableFutureTask.create(() -> {
synchronized (mc.scheduledTasks) {
mc.scheduledTasks.add(ListenableFutureTask.create(() -> {
inRunLater = true;
try {
runnable.run();
@@ -345,28 +196,6 @@ public class ReplayMod {
}
}
// in 1.7.10 apparently events can't be delivered to anonymous classes
@RequiredArgsConstructor
public class RunLaterHelper {
private final Runnable runnable;
@SubscribeEvent
public void onRenderTick(TickEvent.RenderTickEvent event) {
if (event.phase == TickEvent.Phase.START) {
runLater(runnable);
FMLCommonHandler.instance().bus().unregister(this);
}
}
}
@SubscribeEvent
public void runScheduledTasks(InputReplayTimer.RunScheduledTasks event) {
synchronized (scheduledTasks) {
while (!scheduledTasks.isEmpty()) {
scheduledTasks.poll().run();
}
}
}
public String getVersion() {
return getContainer().getVersion();
}
@@ -392,13 +221,13 @@ public class ReplayMod {
private void printToChat(boolean warning, String message, Object... args) {
if (getSettingsRegistry().get(Setting.NOTIFICATIONS)) {
// Some nostalgia: "§8[§6Replay Mod§8]§r Your message goes here"
ChatStyle coloredDarkGray = new ChatStyle().setColor(EnumChatFormatting.DARK_GRAY);
ChatStyle coloredGold = new ChatStyle().setColor(EnumChatFormatting.GOLD);
IChatComponent text = new ChatComponentText("[").setChatStyle(coloredDarkGray)
.appendSibling(new ChatComponentTranslation("replaymod.title").setChatStyle(coloredGold))
.appendSibling(new ChatComponentText("] "))
.appendSibling(new ChatComponentTranslation(message, args).setChatStyle(new ChatStyle()
.setColor(warning ? EnumChatFormatting.RED : EnumChatFormatting.DARK_GREEN)));
Style coloredDarkGray = new Style().setColor(TextFormatting.DARK_GRAY);
Style coloredGold = new Style().setColor(TextFormatting.GOLD);
ITextComponent text = new TextComponentString("[").setStyle(coloredDarkGray)
.appendSibling(new TextComponentTranslation("replaymod.title").setStyle(coloredGold))
.appendSibling(new TextComponentString("] "))
.appendSibling(new TextComponentTranslation(message, args).setStyle(new Style()
.setColor(warning ? TextFormatting.RED : TextFormatting.DARK_GREEN)));
// Send message to chat GUI
// The ingame GUI is initialized at startup, therefore this is possible before the client is connected
mc.ingameGUI.getChatGUI().printChatMessage(text);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -18,11 +18,10 @@ import de.johni0702.minecraft.gui.layout.VerticalLayout;
import de.johni0702.minecraft.gui.popup.GuiInfoPopup;
import de.johni0702.minecraft.gui.utils.Colors;
import lombok.SneakyThrows;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.AbstractClientPlayer;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.network.NetworkPlayerInfo;
import net.minecraft.client.resources.DefaultPlayerSkin;
import net.minecraft.crash.CrashReport;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.ResourceLocation;
import org.apache.commons.io.Charsets;
import org.apache.commons.io.FilenameUtils;
@@ -161,11 +160,14 @@ public class Utils {
}
public static ResourceLocation getResourceLocationForPlayerUUID(UUID uuid) {
EntityPlayer player = Minecraft.getMinecraft().theWorld.getPlayerEntityByUUID(uuid);
if (player == null || !(player instanceof AbstractClientPlayer)) {
return AbstractClientPlayer.locationStevePng;
NetworkPlayerInfo info = getMinecraft().getConnection().getPlayerInfo(uuid);
ResourceLocation skinLocation;
if (info != null && info.hasLocationSkin()) {
skinLocation = info.getLocationSkin();
} else {
skinLocation = DefaultPlayerSkin.getDefaultSkin(uuid);
}
return ((AbstractClientPlayer) player).getLocationSkin();
return skinLocation;
}
public static boolean isCtrlDown() {

View File

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

View File

@@ -3,9 +3,9 @@ package com.replaymod.editor;
import com.replaymod.core.ReplayMod;
import com.replaymod.editor.handler.GuiHandler;
import com.replaymod.online.Setting;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import org.apache.logging.log4j.Logger;
@Mod(modid = ReplayModEditor.MOD_ID,

View File

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

View File

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

View File

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

View File

@@ -5,7 +5,7 @@ import com.replaymod.core.ReplayMod;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.IResourcePack;
import net.minecraft.client.resources.data.IMetadataSection;
import net.minecraft.client.resources.data.IMetadataSerializer;
import net.minecraft.client.resources.data.MetadataSerializer;
import net.minecraft.util.ResourceLocation;
import org.apache.commons.io.IOUtils;
@@ -99,12 +99,12 @@ public class LocalizationExtra implements Extra {
}
@Override
public Set getResourceDomains() {
public Set<String> getResourceDomains() {
return ImmutableSet.of("replaymod");
}
@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;
}

View File

@@ -2,7 +2,6 @@ package com.replaymod.extras;
import com.replaymod.core.ReplayMod;
import com.replaymod.core.Setting;
import cpw.mods.fml.common.Loader;
import de.johni0702.minecraft.gui.container.AbstractGuiScreen;
import de.johni0702.minecraft.gui.container.GuiContainer;
import de.johni0702.minecraft.gui.container.GuiPanel;
@@ -15,6 +14,7 @@ import de.johni0702.minecraft.gui.layout.HorizontalLayout;
import de.johni0702.minecraft.gui.layout.VerticalLayout;
import de.johni0702.minecraft.gui.popup.AbstractGuiPopup;
import de.johni0702.minecraft.gui.utils.Colors;
import net.minecraftforge.fml.common.Loader;
import org.apache.commons.io.FileUtils;
import javax.net.ssl.HttpsURLConnection;

View File

@@ -4,9 +4,9 @@ import com.replaymod.core.ReplayMod;
import com.replaymod.extras.playeroverview.PlayerOverview;
import com.replaymod.extras.urischeme.UriSchemeExtra;
import com.replaymod.extras.youtube.YoutubeUpload;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import org.apache.logging.log4j.Logger;
import java.util.Arrays;

View File

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

View File

@@ -20,7 +20,9 @@ import de.johni0702.minecraft.gui.layout.HorizontalLayout;
import de.johni0702.minecraft.gui.utils.Colors;
import net.minecraft.client.audio.PositionedSoundRecord;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.potion.Potion;
import net.minecraft.entity.player.EnumPlayerModelParts;
import net.minecraft.init.MobEffects;
import net.minecraft.init.SoundEvents;
import net.minecraft.util.ResourceLocation;
import org.lwjgl.util.Dimension;
import org.lwjgl.util.ReadableDimension;
@@ -45,14 +47,14 @@ public class PlayerOverviewGui extends GuiScreen implements Closeable {
public final GuiCheckbox checkAll = new GuiCheckbox(contentPanel){
@Override
public void onClick() {
getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.createPositionedSoundRecord(BUTTON_SOUND, 1.0F));
getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(SoundEvents.UI_BUTTON_CLICK, 1.0F));
playersScrollable.forEach(IGuiCheckbox.class).setChecked(true);
}
}.setLabel("").setChecked(true).setTooltip(new GuiTooltip().setI18nText("replaymod.gui.playeroverview.showall"));
public final GuiCheckbox uncheckAll = new GuiCheckbox(contentPanel){
@Override
public void onClick() {
getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.createPositionedSoundRecord(BUTTON_SOUND, 1.0F));
getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(SoundEvents.UI_BUTTON_CLICK, 1.0F));
playersScrollable.forEach(IGuiCheckbox.class).setChecked(false);
}
}.setLabel("").setChecked(false).setTooltip(new GuiTooltip().setI18nText("replaymod.gui.playeroverview.hideall"));
@@ -95,10 +97,12 @@ public class PlayerOverviewGui extends GuiScreen implements Closeable {
public void draw(GuiRenderer renderer, ReadableDimension size, RenderInfo renderInfo) {
renderer.bindTexture(texture);
renderer.drawTexturedRect(0, 0, 8, 8, 16, 16, 8, 8, 64, 64);
renderer.drawTexturedRect(0, 0, 40, 8, size.getWidth(), size.getHeight(), 8, 8, 64, 64);
if (p.isWearing(EnumPlayerModelParts.HAT)) {
renderer.drawTexturedRect(0, 0, 40, 8, size.getWidth(), size.getHeight(), 8, 8, 64, 64);
}
}
}.setSize(16, 16),
new GuiLabel().setText(p.getDisplayName()).setColor(isSpectator(p) ? Colors.DKGREY : Colors.WHITE)
new GuiLabel().setText(p.getName()).setColor(isSpectator(p) ? Colors.DKGREY : Colors.WHITE)
).onClick(new Runnable() {
@Override
public void run() {
@@ -142,7 +146,7 @@ public class PlayerOverviewGui extends GuiScreen implements Closeable {
}
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> {
@@ -150,7 +154,7 @@ public class PlayerOverviewGui extends GuiScreen implements Closeable {
public int compare(EntityPlayer o1, EntityPlayer o2) {
if (isSpectator(o1) && !isSpectator(o2)) return 1;
if (isSpectator(o2) && !isSpectator(o1)) return -1;
return o1.getDisplayName().compareToIgnoreCase(o2.getDisplayName());
return o1.getName().compareToIgnoreCase(o2.getName());
}
}
}

View File

@@ -2,13 +2,14 @@ package com.replaymod.extras.playeroverview.mixin;
import com.replaymod.extras.ReplayModExtras;
import com.replaymod.extras.playeroverview.PlayerOverview;
import net.minecraft.client.model.ModelBase;
import net.minecraft.client.renderer.entity.RenderPlayer;
import net.minecraft.client.renderer.entity.RendererLivingEntity;
import net.minecraft.client.renderer.culling.ICamera;
import net.minecraft.client.renderer.entity.Render;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import org.spongepowered.asm.mixin.Mixin;
import java.util.UUID;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
/**
* This mixin prevents players that are hidden in the PlayerOverview from being rendered.
@@ -18,22 +19,22 @@ import java.util.UUID;
*
* The previous solution was to overwrite the RenderPlayer instances which has been dropped in favor of this one
* because it is less compatible with other mods whereas this one should be fine as long as no other mod completely
* overwrites the methods we modify in this mixin.
* overwrites the shouldRender method.
* One example of the previous solution breaking is when used with VanillaEnhancements because it replaces the
* RenderManager with a new custom one which in turn will reset our registered RenderPlayer instances because
* it does so after we have already registered with the old RenderManager.
*/
@Mixin(value = RenderPlayer.class, priority = 1200)
public abstract class MixinRenderPlayer extends RendererLivingEntity {
public MixinRenderPlayer(ModelBase p_i1261_1_, float p_i1261_2_) {
super(p_i1261_1_, p_i1261_2_);
}
@Override
public void doRenderShadowAndFire(Entity entity, double d1, double d2, double d3, float f1, float f2) {
UUID uuid = entity.getUniqueID();
if (!ReplayModExtras.instance.get(PlayerOverview.class).map(po -> po.isHidden(uuid)).orElse(false)) {
super.doRenderShadowAndFire(entity, d1, d2, d3, f1, f2);
}
@Mixin(value = Render.class, priority = 1200)
public abstract class MixinRender {
@Inject(method = "shouldRender", at=@At("HEAD"), cancellable = true)
public void replayModExtras_isPlayerHidden(Entity entity, ICamera camera, double camX, double camY, double camZ, CallbackInfoReturnable<Boolean> ci) {
ReplayModExtras.instance.get(PlayerOverview.class).ifPresent(playerOverview -> {
if (entity instanceof EntityPlayer) {
EntityPlayer player = (EntityPlayer) entity;
if (playerOverview.isHidden(player.getUniqueID())) {
ci.setReturnValue(false);
}
}
});
}
}

View File

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

View File

@@ -11,14 +11,14 @@ import com.replaymod.replay.events.ReplayCloseEvent;
import com.replaymod.replaystudio.replay.ReplayFile;
import com.replaymod.replaystudio.replay.ZipReplayFile;
import com.replaymod.replaystudio.studio.ReplayStudio;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPostInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import de.johni0702.minecraft.gui.container.GuiScreen;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPostInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import org.apache.logging.log4j.Logger;
import java.io.File;
@@ -75,7 +75,7 @@ public class ReplayModOnline {
}
new GuiHandler(this).register();
FMLCommonHandler.instance().bus().register(this);
MinecraftForge.EVENT_BUS.register(this);
}
@Mod.EventHandler

View File

@@ -2,7 +2,9 @@ package com.replaymod.online.api.replay.holders;
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"),
MC_1_10_2("Minecraft 1.10.2", "1.10.2");
private String niceName, apiName;

View File

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

View File

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

View File

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

View File

@@ -1,9 +1,9 @@
package com.replaymod.pathing.player;
import com.replaymod.core.utils.WrappedTimer;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.eventhandler.Event;
import net.minecraft.util.Timer;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.eventhandler.Event;
/**
* Wrapper around the current timer that prevents the timer from advancing by itself.
@@ -20,7 +20,7 @@ public class ReplayTimer extends WrappedTimer {
copy(this, state); // Save our current state
super.updateTimer(); // Update current state
copy(state, this); // Restore our old state
FMLCommonHandler.instance().bus().post(new UpdatedEvent());
MinecraftForge.EVENT_BUS.post(new UpdatedEvent());
}
public Timer getWrapped() {

View File

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

View File

@@ -4,15 +4,15 @@ import com.replaymod.core.ReplayMod;
import com.replaymod.core.utils.Restrictions;
import com.replaymod.recording.handler.ConnectionEventHandler;
import com.replaymod.recording.packet.PacketListener;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.eventhandler.EventBus;
import cpw.mods.fml.common.network.NetworkRegistry;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandler;
import net.minecraftforge.common.MinecraftForge;
import net.minecraft.network.NetworkManager;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.eventhandler.EventBus;
import net.minecraftforge.fml.common.network.NetworkRegistry;
import org.apache.logging.log4j.Logger;
import org.lwjgl.input.Keyboard;
@@ -54,7 +54,7 @@ public class ReplayModRecording {
@Mod.EventHandler
public void init(FMLInitializationEvent event) {
EventBus bus = FMLCommonHandler.instance().bus();
EventBus bus = MinecraftForge.EVENT_BUS;
bus.register(connectionEventHandler = new ConnectionEventHandler(logger, core));
NetworkRegistry.INSTANCE.newChannel(Restrictions.PLUGIN_CHANNEL, new RestrictionsChannelHandler());

View File

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

View File

@@ -10,10 +10,11 @@ import com.replaymod.replaystudio.replay.ReplayFile;
import com.replaymod.replaystudio.replay.ReplayMetaData;
import com.replaymod.replaystudio.replay.ZipReplayFile;
import com.replaymod.replaystudio.studio.ReplayStudio;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.network.FMLNetworkEvent;
import net.minecraft.client.Minecraft;
import net.minecraft.network.NetworkManager;
import net.minecraft.world.WorldType;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.network.FMLNetworkEvent.ClientDisconnectionFromServerEvent;
import org.apache.logging.log4j.Logger;
import java.io.File;
@@ -46,6 +47,10 @@ public class ConnectionEventHandler {
try {
boolean local = networkManager.isLocalChannel();
if (local) {
if (mc.getIntegratedServer().getEntityWorld().getWorldType() == WorldType.DEBUG_WORLD) {
logger.info("Debug World recording is not supported.");
return;
}
if(!core.getSettingsRegistry().get(Setting.RECORD_SINGLEPLAYER)) {
logger.info("Singleplayer Recording is disabled");
return;
@@ -91,14 +96,14 @@ public class ConnectionEventHandler {
guiOverlay.register();
core.printInfoToChat("replaymod.chat.recordingstarted");
} catch(Throwable e) {
} catch(Exception e) {
e.printStackTrace();
core.printWarningToChat("replaymod.chat.recordingfailed");
}
}
@SubscribeEvent
public void onDisconnectedFromServerEvent(FMLNetworkEvent.ClientDisconnectionFromServerEvent event) {
public void onDisconnectedFromServerEvent(ClientDisconnectionFromServerEvent event) {
if (packetListener != null) {
guiOverlay.unregister();
guiOverlay = null;

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,11 +1,11 @@
package com.replaymod.recording.mixin;
import com.replaymod.recording.handler.FMLHandshakeFilter;
import cpw.mods.fml.common.network.handshake.FMLHandshakeCodec;
import cpw.mods.fml.common.network.handshake.NetworkDispatcher;
import cpw.mods.fml.relauncher.Side;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.embedded.EmbeddedChannel;
import net.minecraftforge.fml.common.network.handshake.FMLHandshakeCodec;
import net.minecraftforge.fml.common.network.handshake.NetworkDispatcher;
import net.minecraftforge.fml.relauncher.Side;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,13 +3,13 @@ package com.replaymod.render;
import com.replaymod.core.ReplayMod;
import com.replaymod.render.utils.RenderJob;
import com.replaymod.replay.events.ReplayCloseEvent;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import net.minecraft.crash.CrashReport;
import net.minecraft.util.ReportedException;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import org.apache.commons.io.FileUtils;
import org.apache.logging.log4j.Logger;
@@ -46,7 +46,7 @@ public class ReplayModRender {
core = ReplayMod.instance;
configuration = new Configuration(event.getSuggestedConfigurationFile());
FMLCommonHandler.instance().bus().register(this);
MinecraftForge.EVENT_BUS.register(this);
core.getSettingsRegistry().register(Setting.class);
}

View File

@@ -5,11 +5,11 @@ import com.replaymod.render.rendering.FrameConsumer;
import com.replaymod.render.rendering.VideoRenderer;
import com.replaymod.render.utils.ByteBufferPool;
import com.replaymod.render.utils.StreamPipe;
import cpw.mods.fml.common.versioning.ComparableVersion;
import net.minecraft.client.Minecraft;
import net.minecraft.crash.CrashReport;
import net.minecraft.crash.CrashReportCategory;
import net.minecraft.util.Util;
import net.minecraftforge.fml.common.versioning.ComparableVersion;
import org.apache.commons.exec.CommandLine;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,13 +2,13 @@ package com.replaymod.render.mixin;
import com.replaymod.render.hooks.EntityRendererHandler;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.culling.Frustrum;
import net.minecraft.client.renderer.culling.Frustum;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(Frustrum.class)
@Mixin(Frustum.class)
public abstract class MixinFrustum {
@Inject(method = "isBoxInFrustum", at = @At("HEAD"), cancellable = true)
public void isBoxInFrustum(double minX, double minY, double minZ, double maxX, double maxY, double maxZ, CallbackInfoReturnable<Boolean> ci) {

View File

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

View File

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

View File

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

View File

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

View File

@@ -8,7 +8,7 @@ import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
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)
public abstract class MixinRenderManager {
@@ -19,7 +19,7 @@ public abstract class MixinRenderManager {
private float playerViewY;
@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();
if (handler != null && handler.omnidirectional) {
double pitch = -Math.atan2(dy, Math.sqrt(dx * dx + dz * dz));

View File

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

View File

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

View File

@@ -1,6 +1,5 @@
package com.replaymod.render.rendering;
import com.replaymod.core.ReplayMod;
import com.replaymod.pathing.player.AbstractTimelinePlayer;
import com.replaymod.pathing.player.ReplayTimer;
import com.replaymod.pathing.properties.TimestampProperty;
@@ -12,21 +11,22 @@ import com.replaymod.render.events.ReplayRenderEvent;
import com.replaymod.render.frame.RGBFrame;
import com.replaymod.render.gui.GuiRenderingDone;
import com.replaymod.render.gui.GuiVideoRenderer;
import com.replaymod.render.hooks.ChunkLoadingRenderGlobal;
import com.replaymod.render.metadata.MetadataInjector;
import com.replaymod.render.utils.SoundHandler;
import com.replaymod.replay.ReplayHandler;
import com.replaymod.replaystudio.pathing.path.Keyframe;
import com.replaymod.replaystudio.pathing.path.Path;
import com.replaymod.replaystudio.pathing.path.Timeline;
import cpw.mods.fml.common.FMLCommonHandler;
import net.minecraft.client.Minecraft;
import net.minecraft.client.audio.SoundCategory;
import net.minecraft.client.gui.ScaledResolution;
import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.client.shader.Framebuffer;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.Timer;
import net.minecraftforge.common.MinecraftForge;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.Dimension;
import org.lwjgl.util.ReadableDimension;
@@ -40,9 +40,9 @@ import java.util.concurrent.FutureTask;
import static com.google.common.collect.Iterables.getLast;
import static com.replaymod.render.ReplayModRender.LOGGER;
import static net.minecraft.client.renderer.GlStateManager.*;
import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D;
public class VideoRenderer implements RenderInfo {
private final Minecraft mc = Minecraft.getMinecraft();
@@ -55,10 +55,11 @@ public class VideoRenderer implements RenderInfo {
private int fps;
private boolean mouseWasGrabbed;
private boolean debugInfoWasShown;
private Map originalSoundLevels;
private Map<SoundCategory, Float> originalSoundLevels;
private TimelinePlayer timelinePlayer;
private Future<Void> timelinePlayerFuture;
private ChunkLoadingRenderGlobal chunkLoadingRenderGlobal;
private int framesDone;
private int totalFrames;
@@ -91,7 +92,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)
*/
public boolean renderVideo() throws Throwable {
FMLCommonHandler.instance().bus().post(new ReplayRenderEvent.Pre(this));
MinecraftForge.EVENT_BUS.post(new ReplayRenderEvent.Pre(this));
setup();
@@ -137,7 +138,7 @@ public class VideoRenderer implements RenderInfo {
finish();
FMLCommonHandler.instance().bus().post(new ReplayRenderEvent.Post(this));
MinecraftForge.EVENT_BUS.post(new ReplayRenderEvent.Post(this));
if (failureCause != null) {
throw failureCause;
@@ -201,9 +202,9 @@ public class VideoRenderer implements RenderInfo {
for (SoundCategory category : SoundCategory.values()) {
mutedSounds.put(category, 0f);
}
originalSoundLevels = mc.gameSettings.mapSoundLevels;
mutedSounds.put(SoundCategory.MASTER, (Float) originalSoundLevels.get(SoundCategory.MASTER));
mc.gameSettings.mapSoundLevels = mutedSounds;
originalSoundLevels = mc.gameSettings.soundLevels;
mutedSounds.put(SoundCategory.MASTER, originalSoundLevels.get(SoundCategory.MASTER));
mc.gameSettings.soundLevels = mutedSounds;
fps = settings.getFramesPerSecond();
@@ -224,8 +225,7 @@ public class VideoRenderer implements RenderInfo {
updateDisplaySize();
ScaledResolution scaled = new ScaledResolution(mc, displayWidth, displayHeight);
gui.toMinecraft().setWorldAndResolution(mc, scaled.getScaledWidth(), scaled.getScaledHeight());
chunkLoadingRenderGlobal = new ChunkLoadingRenderGlobal(mc.renderGlobal);
// Set up our own framebuffer to render the GUI to
guiFramebuffer = new Framebuffer(displayWidth, displayHeight, true);
@@ -245,10 +245,13 @@ public class VideoRenderer implements RenderInfo {
if (mouseWasGrabbed) {
mc.mouseHelper.grabMouseCursor();
}
mc.gameSettings.mapSoundLevels = originalSoundLevels;
mc.gameSettings.soundLevels = originalSoundLevels;
mc.displayGuiScreen(null);
if (chunkLoadingRenderGlobal != null) {
chunkLoadingRenderGlobal.uninstall();
}
ReplayMod.soundHandler.playRenderSuccessSound();
new SoundHandler().playRenderSuccessSound();
try {
if (!hasFailed()) {
@@ -270,7 +273,11 @@ public class VideoRenderer implements RenderInfo {
}
mc.currentScreen = gui.toMinecraft();
mc.runTick();
try {
mc.runTick();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void drawGui() {
@@ -281,17 +288,23 @@ public class VideoRenderer implements RenderInfo {
guiFramebuffer.createBindFramebuffer(mc.displayWidth, mc.displayHeight);
}
GL11.glPushMatrix();
GL11.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
GL11.glEnable(GL_TEXTURE_2D);
pushMatrix();
clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
enableTexture2D();
guiFramebuffer.bindFramebuffer(true);
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().handleInput();
try {
gui.toMinecraft().handleInput();
} catch (IOException e) {
// That's a strange exception from this kind of method O_o
// It isn't actually thrown here, so we'll deal with it the easy way
throw new RuntimeException(e);
}
int mouseX = Mouse.getX() * scaled.getScaledWidth() / mc.displayWidth;
int mouseY = scaled.getScaledHeight() - Mouse.getY() * scaled.getScaledHeight() / mc.displayHeight - 1;
@@ -299,17 +312,17 @@ public class VideoRenderer implements RenderInfo {
gui.toMinecraft().drawScreen(mouseX, mouseY, 0);
guiFramebuffer.unbindFramebuffer();
GL11.glPopMatrix();
GL11.glPushMatrix();
popMatrix();
pushMatrix();
guiFramebuffer.framebufferRender(displayWidth, displayHeight);
GL11.glPopMatrix();
popMatrix();
// if not in high performance mode, update the gui size if screen size changed
// otherwise just swap the progress gui to screen
if (settings.isHighPerformance()) {
Display.update();
} else {
mc.resetSize();
mc.updateDisplay();
}
if (Mouse.isGrabbed()) {
Mouse.setGrabbed(false);

View File

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

View File

@@ -3,20 +3,20 @@ package com.replaymod.replay;
import com.replaymod.core.utils.WrappedTimer;
import com.replaymod.replay.camera.CameraController;
import com.replaymod.replay.camera.CameraEntity;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.eventhandler.Event;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.renderer.entity.RenderManager;
import net.minecraft.client.settings.GameSettings;
import net.minecraft.client.settings.KeyBinding;
import net.minecraft.crash.CrashReport;
import net.minecraft.util.ReportedException;
import net.minecraft.util.Timer;
import net.minecraftforge.client.ForgeHooksClient;
import net.minecraftforge.fml.common.FMLCommonHandler;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import java.io.IOException;
public class InputReplayTimer extends WrappedTimer {
private final ReplayModReplay mod;
private final Minecraft mc;
@@ -31,9 +31,6 @@ public class InputReplayTimer extends WrappedTimer {
public void updateTimer() {
super.updateTimer();
// 1.7.10: We have to run the scheduled executables (ours only) because MC would only run them every tick
FMLCommonHandler.instance().bus().post(new RunScheduledTasks());
// If we are in a replay, we have to manually process key and mouse events as the
// tick speed may vary or there may not be any ticks at all (when the replay is paused)
if (mod.getReplayHandler() != null) {
@@ -46,7 +43,11 @@ public class InputReplayTimer extends WrappedTimer {
handleKeyEvent();
}
} else {
mc.currentScreen.handleInput();
try {
mc.currentScreen.handleInput();
} catch (IOException e) { // *SIGH*
e.printStackTrace();
}
}
}
}
@@ -88,14 +89,17 @@ public class InputReplayTimer extends WrappedTimer {
mc.setIngameFocus();
}
} else {
mc.currentScreen.handleMouseInput();
try {
mc.currentScreen.handleMouseInput();
} catch (IOException e) { // WHO IS RESPONSIBLE FOR THIS MESS?!?
e.printStackTrace();
}
}
FMLCommonHandler.instance().fireMouseInput();
}
protected void handleKeyEvent() {
// TODO 1.7.10: This might be missing some 1.7.10-only key bindings or implement some of them incorrectly
int key = Keyboard.getEventKey() == 0 ? Keyboard.getEventCharacter() + 256 : Keyboard.getEventKey();
boolean pressed = Keyboard.getEventKeyState();
@@ -123,11 +127,15 @@ public class InputReplayTimer extends WrappedTimer {
if (pressed) {
// This might be subject to change as vanilla shaders are still kinda unused in 1.8
if (key == Keyboard.KEY_F4 && mc.entityRenderer != null) {
mc.entityRenderer.activateNextShader();
mc.entityRenderer.switchUseShader();
}
if (mc.currentScreen != null) {
mc.currentScreen.handleKeyboardInput();
try {
mc.currentScreen.handleKeyboardInput();
} catch (IOException e) { // AND WHO THOUGHT THIS WAS A GREAT IDEA?
e.printStackTrace();
}
} else {
if (key == Keyboard.KEY_ESCAPE) {
mc.displayInGameMenu();
@@ -135,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
// replay viewer as of now
// TODO Update maybe add new key bindings
// TODO: Translate magic values to Keyboard.KEY_ constants
if (key == 32 && Keyboard.isKeyDown(61) && mc.ingameGUI != null) {
@@ -159,7 +168,7 @@ public class InputReplayTimer extends WrappedTimer {
}
if (key == 48 && Keyboard.isKeyDown(61)) {
RenderManager.debugBoundingBox = !RenderManager.debugBoundingBox;
mc.getRenderManager().setDebugBoundingBox(!mc.getRenderManager().isDebugBoundingBox());
}
if (key == 25 && Keyboard.isKeyDown(61)) {
@@ -178,6 +187,14 @@ public class InputReplayTimer extends WrappedTimer {
if (mc.gameSettings.keyBindTogglePerspective.isPressed()) {
mc.gameSettings.thirdPersonView = (mc.gameSettings.thirdPersonView + 1) % 3;
if (mc.entityRenderer != null) { // Extra check, not in vanilla code
if (mc.gameSettings.thirdPersonView == 0) {
mc.entityRenderer.loadEntityShader(mc.getRenderViewEntity());
} else if (mc.gameSettings.thirdPersonView == 1) {
mc.entityRenderer.loadEntityShader(null);
}
}
}
}
@@ -197,6 +214,4 @@ public class InputReplayTimer extends WrappedTimer {
FMLCommonHandler.instance().fireKeyInput();
}
public static class RunScheduledTasks extends Event {}
}

View File

@@ -8,16 +8,14 @@ import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.util.ScreenShotHelper;
import org.apache.commons.io.FileUtils;
import org.lwjgl.opengl.GL11;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.Queue;
import java.util.concurrent.FutureTask;
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
@Getter
@@ -40,18 +38,18 @@ public class NoGuiScreenshot {
mc.gameSettings.hideGUI = true;
// Render frame without GUI
GL11.glPushMatrix();
GL11.glClear(16640);
GlStateManager.pushMatrix();
GlStateManager.clear(16640);
mc.getFramebuffer().bindFramebuffer(true);
GL11.glEnable(GL11.GL_TEXTURE_2D);
GlStateManager.enableTexture2D();
mc.entityRenderer.updateCameraAndRender(mc.timer.renderPartialTicks);
mc.entityRenderer.updateCameraAndRender(mc.timer.renderPartialTicks, System.nanoTime());
mc.getFramebuffer().unbindFramebuffer();
GL11.glPopMatrix();
GL11.glPushMatrix();
GlStateManager.popMatrix();
GlStateManager.pushMatrix();
mc.getFramebuffer().framebufferRender(mc.displayWidth, mc.displayHeight);
GL11.glPopMatrix();
GlStateManager.popMatrix();
} catch (Throwable t) {
future.setException(t);
return;
@@ -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
// from the minecraft thread which is exactly what we want to avoid.
synchronized (mc.scheduledTasks) {
@SuppressWarnings("unchecked")
Queue<FutureTask> queue = mc.scheduledTasks;
queue.add(ListenableFutureTask.create(runnable, null));
mc.scheduledTasks.add(ListenableFutureTask.create(runnable, null));
}
return future;
}

View File

@@ -1,41 +1,39 @@
package com.replaymod.replay;
import com.google.common.base.Preconditions;
import com.mojang.authlib.GameProfile;
import com.replaymod.core.utils.Restrictions;
import com.replaymod.replay.camera.CameraEntity;
import com.replaymod.replay.camera.SpectatorCameraController;
import com.replaymod.replay.events.ReplayCloseEvent;
import com.replaymod.replay.events.ReplayOpenEvent;
import com.replaymod.replay.gui.overlay.GuiReplayOverlay;
import com.replaymod.replay.gui.screen.GuiOpeningReplay;
import com.replaymod.replaystudio.data.Marker;
import com.replaymod.replaystudio.replay.ReplayFile;
import com.replaymod.replaystudio.util.Location;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.network.internal.FMLNetworkHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.embedded.EmbeddedChannel;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.EntityOtherPlayerMP;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.ScaledResolution;
import net.minecraft.client.network.NetHandlerLoginClient;
import net.minecraft.client.network.NetHandlerPlayClient;
import net.minecraft.client.resources.I18n;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.network.EnumConnectionState;
import net.minecraft.network.EnumPacketDirection;
import net.minecraft.network.NetworkManager;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.client.FMLClientHandler;
import net.minecraftforge.fml.common.network.handshake.NetworkDispatcher;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.GL11;
import java.io.IOException;
import java.util.*;
import static net.minecraft.client.renderer.GlStateManager.*;
import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D;
public class ReplayHandler {
@@ -78,7 +76,7 @@ public class ReplayHandler {
Preconditions.checkState(mc.isCallingFromMinecraftThread(), "Must be called from Minecraft thread.");
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()));
@@ -89,7 +87,7 @@ public class ReplayHandler {
overlay = new GuiReplayOverlay(this);
overlay.setVisible(true);
FMLCommonHandler.instance().bus().post(new ReplayOpenEvent.Post(this));
MinecraftForge.EVENT_BUS.post(new ReplayOpenEvent.Post(this));
}
void restartedReplay() {
@@ -103,7 +101,7 @@ public class ReplayHandler {
public void endReplay() throws IOException {
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();
@@ -128,35 +126,30 @@ public class ReplayHandler {
mc.displayGuiScreen(null);
FMLCommonHandler.instance().bus().post(new ReplayCloseEvent.Post(this));
MinecraftForge.EVENT_BUS.post(new ReplayCloseEvent.Post(this));
}
private void setup() {
mc.ingameGUI.getChatGUI().clearChatMessages();
NetworkManager networkManager = new NetworkManager(true) {
NetworkManager networkManager = new NetworkManager(EnumPacketDirection.CLIENTBOUND) {
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable t) {
t.printStackTrace();
}
};
networkManager.setNetHandler(new NetHandlerLoginClient(networkManager, mc, null));
NetHandlerPlayClient netHandlerPlayClient =
new NetHandlerPlayClient(mc, null, networkManager, new GameProfile(UUID.randomUUID(), "Player"));
networkManager.setNetHandler(netHandlerPlayClient);
FMLClientHandler.instance().setPlayClient(netHandlerPlayClient);
mc.displayGuiScreen(new GuiOpeningReplay(networkManager));
channel = new EmbeddedChannel(networkManager);
NetworkDispatcher networkDispatcher = new NetworkDispatcher(networkManager);
channel.attr(NetworkDispatcher.FML_DISPATCHER).set(networkDispatcher);
ChannelOutboundHandlerAdapter dummyHandler = new ChannelOutboundHandlerAdapter();
channel = new EmbeddedChannel(dummyHandler);
channel.pipeline().remove(dummyHandler);
channel.pipeline().addFirst("ReplayModReplay_replaySender", replaySender);
channel.pipeline().addAfter("ReplayModReplay_replaySender", "packet_handler", networkManager);
channel.pipeline().addAfter("ReplayModReplay_replaySender", "fml:packet_handler", networkDispatcher);
channel.pipeline().fireChannelActive();
// Call twice to force-overwrite the NetworkManager's internal state
networkManager.setConnectionState(EnumConnectionState.PLAY);
networkManager.getNetHandler().onConnectionStateTransition(EnumConnectionState.LOGIN, EnumConnectionState.PLAY);
networkManager.setConnectionState(EnumConnectionState.PLAY);
FMLNetworkHandler.fmlClientHandshake(networkManager);
}
public ReplayFile getReplayFile() {
@@ -216,7 +209,7 @@ public class ReplayHandler {
* When the entity is {@code null} or the camera entity, the camera becomes the view entity.
* @param e The entity to spectate
*/
public void spectateEntity(EntityLivingBase e) {
public void spectateEntity(Entity e) {
CameraEntity cameraEntity = getCameraEntity();
if (cameraEntity == null) {
return; // Cannot spectate if we have no camera
@@ -234,15 +227,15 @@ public class ReplayHandler {
cameraEntity.setCameraController(new SpectatorCameraController(cameraEntity));
}
if (mc.renderViewEntity != e) {
mc.renderViewEntity = e;
if (mc.getRenderViewEntity() != e) {
mc.setRenderViewEntity(e);
cameraEntity.setCameraPosRot(e);
}
}
/**
* Set the camera as the view entity.
* This is equivalent to {@link #spectateEntity(EntityLivingBase) spectateEntity(null)}.
* This is equivalent to {@link #spectateEntity(Entity) spectateEntity(null)}.
*/
public void spectateCamera() {
spectateEntity(null);
@@ -253,7 +246,7 @@ public class ReplayHandler {
* @return {@code true} if the camera is the view entity, {@code false} otherwise
*/
public boolean isCameraView() {
return mc.thePlayer instanceof CameraEntity && mc.thePlayer == mc.renderViewEntity;
return mc.thePlayer instanceof CameraEntity && mc.thePlayer == mc.getRenderViewEntity();
}
/**
@@ -317,21 +310,21 @@ public class ReplayHandler {
replaySender.setSyncModeAndWait();
// Perform the rendering using OpenGL
GL11.glPushMatrix();
GL11.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
GL11.glEnable(GL_TEXTURE_2D);
pushMatrix();
clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
enableTexture2D();
mc.getFramebuffer().bindFramebuffer(true);
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.drawScreen(0, 0, 0);
mc.getFramebuffer().unbindFramebuffer();
GL11.glPopMatrix();
GL11.glPushMatrix();
popMatrix();
pushMatrix();
mc.getFramebuffer().framebufferRender(mc.displayWidth, mc.displayHeight);
GL11.glPopMatrix();
popMatrix();
Display.update();
@@ -340,10 +333,8 @@ public class ReplayHandler {
replaySender.setAsyncMode(true);
replaySender.setReplaySpeed(0);
mc.getNetHandler().getNetworkManager().processReceivedPackets();
@SuppressWarnings("unchecked")
List<Entity> entities = (List<Entity>) mc.theWorld.loadedEntityList;
for (Entity entity : entities) {
mc.getConnection().getNetworkManager().processReceivedPackets();
for (Entity entity : mc.theWorld.loadedEntityList) {
if (entity instanceof EntityOtherPlayerMP) {
EntityOtherPlayerMP e = (EntityOtherPlayerMP) entity;
e.setPosition(e.otherPlayerMPX, e.otherPlayerMPY, e.otherPlayerMPZ);
@@ -356,7 +347,11 @@ public class ReplayHandler {
entity.prevRotationYaw = entity.rotationYaw;
entity.prevRotationPitch = entity.rotationPitch;
}
mc.runTick();
try {
mc.runTick();
} catch (IOException e) {
e.printStackTrace(); // This should never be thrown but whatever
}
//finally, updating the camera's position (which is not done by the sync jumping)
moveCameraToTargetPosition();

View File

@@ -18,11 +18,11 @@ import com.replaymod.replaystudio.data.Marker;
import com.replaymod.replaystudio.replay.ReplayFile;
import com.replaymod.replaystudio.replay.ZipReplayFile;
import com.replaymod.replaystudio.studio.ReplayStudio;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPostInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import net.minecraft.client.Minecraft;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPostInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import org.apache.logging.log4j.Logger;
import org.lwjgl.input.Keyboard;

View File

@@ -13,60 +13,27 @@ import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.EntityOtherPlayerMP;
import net.minecraft.client.gui.GuiDownloadTerrain;
import net.minecraft.client.gui.GuiErrorScreen;
import net.minecraft.client.resources.FileResourcePack;
import net.minecraft.client.resources.I18n;
import net.minecraft.client.resources.ResourcePackRepository;
import net.minecraft.entity.Entity;
import net.minecraft.network.EnumConnectionState;
import net.minecraft.network.Packet;
import net.minecraft.network.PacketBuffer;
import net.minecraft.network.play.server.S01PacketJoinGame;
import net.minecraft.network.play.server.S02PacketChat;
import net.minecraft.network.play.server.S06PacketUpdateHealth;
import net.minecraft.network.play.server.S07PacketRespawn;
import net.minecraft.network.play.server.S08PacketPlayerPosLook;
import net.minecraft.network.play.server.S0CPacketSpawnPlayer;
import net.minecraft.network.play.server.S0EPacketSpawnObject;
import net.minecraft.network.play.server.S0FPacketSpawnMob;
import net.minecraft.network.play.server.S10PacketSpawnPainting;
import net.minecraft.network.play.server.S11PacketSpawnExperienceOrb;
import net.minecraft.network.play.server.S13PacketDestroyEntities;
import net.minecraft.network.play.server.S1FPacketSetExperience;
import net.minecraft.network.play.server.S21PacketChunkData;
import net.minecraft.network.play.server.S2APacketParticles;
import net.minecraft.network.play.server.S2BPacketChangeGameState;
import net.minecraft.network.play.server.S2CPacketSpawnGlobalEntity;
import net.minecraft.network.play.server.S2DPacketOpenWindow;
import net.minecraft.network.play.server.S2EPacketCloseWindow;
import net.minecraft.network.play.server.S2FPacketSetSlot;
import net.minecraft.network.play.server.S30PacketWindowItems;
import net.minecraft.network.play.server.S36PacketSignEditorOpen;
import net.minecraft.network.play.server.S37PacketStatistics;
import net.minecraft.network.play.server.S39PacketPlayerAbilities;
import net.minecraft.network.play.server.S3FPacketCustomPayload;
import net.minecraft.network.play.server.S40PacketDisconnect;
import net.minecraft.util.IChatComponent;
import net.minecraft.util.MathHelper;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.network.*;
import net.minecraft.network.play.server.*;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.world.EnumDifficulty;
import net.minecraft.world.GameType;
import net.minecraft.world.World;
import net.minecraft.world.WorldSettings.GameType;
import net.minecraft.world.WorldType;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.IChunkProvider;
import org.apache.commons.io.Charsets;
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.IOUtils;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.io.*;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@@ -77,20 +44,22 @@ import java.util.Map;
*/
@Sharable
public class ReplaySender extends ChannelDuplexHandler {
/**
* These packets are ignored completely during replay.
*/
private static final List<Class> BAD_PACKETS = Arrays.<Class>asList(
S06PacketUpdateHealth.class,
S2DPacketOpenWindow.class,
S2EPacketCloseWindow.class,
S2FPacketSetSlot.class,
S30PacketWindowItems.class,
S36PacketSignEditorOpen.class,
S37PacketStatistics.class,
S1FPacketSetExperience.class,
S39PacketPlayerAbilities.class);
// TODO Update possibly more?
SPacketUpdateHealth.class,
SPacketOpenWindow.class,
SPacketCloseWindow.class,
SPacketSetSlot.class,
SPacketWindowItems.class,
SPacketSignEditorOpen.class,
SPacketStatistics.class,
SPacketSetExperience.class,
SPacketCamera.class,
SPacketPlayerAbilities.class,
SPacketTitle.class);
private static int TP_DISTANCE_LIMIT = 128;
@@ -145,7 +114,7 @@ public class ReplaySender extends ChannelDuplexHandler {
/**
* Whether we need to restart the current replay. E.g. when jumping backwards in time
*/
protected boolean startFromBeginning;
protected boolean startFromBeginning = true;
/**
* Whether to terminate the replay. This only has an effect on the async mode and is {@code true} during sync mode.
@@ -199,6 +168,8 @@ public class ReplaySender extends ChannelDuplexHandler {
this.asyncMode = asyncMode;
this.replayLength = file.getMetaData().getDuration();
MinecraftForge.EVENT_BUS.register(this);
if (asyncMode) {
new Thread(asyncSender, "replaymod-async-sender").start();
}
@@ -267,6 +238,7 @@ public class ReplaySender extends ChannelDuplexHandler {
*/
public void terminateReplay() {
terminate = true;
MinecraftForge.EVENT_BUS.unregister(this);
try {
channelInactive(ctx);
ctx.channel().pipeline().close();
@@ -276,6 +248,25 @@ public class ReplaySender extends ChannelDuplexHandler {
}
}
@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
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
@@ -303,21 +294,21 @@ public class ReplaySender extends ChannelDuplexHandler {
// 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.
if (!asyncMode && mc.theWorld != null) {
if (p instanceof S0CPacketSpawnPlayer
|| p instanceof S0EPacketSpawnObject
|| p instanceof S0FPacketSpawnMob
|| p instanceof S2CPacketSpawnGlobalEntity
|| p instanceof S10PacketSpawnPainting
|| p instanceof S11PacketSpawnExperienceOrb
|| p instanceof S13PacketDestroyEntities) {
if (p instanceof SPacketSpawnPlayer
|| p instanceof SPacketSpawnObject
|| p instanceof SPacketSpawnMob
|| p instanceof SPacketSpawnGlobalEntity
|| p instanceof SPacketSpawnPainting
|| p instanceof SPacketSpawnExperienceOrb
|| p instanceof SPacketDestroyEntities) {
World world = mc.theWorld;
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) {
int chunkX = entity.chunkCoordX;
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);
}
@@ -343,7 +334,7 @@ public class ReplaySender extends ChannelDuplexHandler {
int i = pb.readVarIntFromBuffer();
Packet p = Packet.generatePacket(EnumConnectionState.PLAY.func_150755_b(), i);
Packet p = EnumConnectionState.PLAY.getPacket(EnumPacketDirection.CLIENTBOUND, i);
p.readPacketData(pb);
return p;
@@ -355,9 +346,9 @@ public class ReplaySender extends ChannelDuplexHandler {
* @return The processed packet or {@code null} if no packet shall be sent
*/
protected Packet processPacket(Packet p) throws Exception {
if (p instanceof S3FPacketCustomPayload) {
S3FPacketCustomPayload packet = (S3FPacketCustomPayload) p;
if (Restrictions.PLUGIN_CHANNEL.equals(packet.func_149169_c())) {
if (p instanceof SPacketCustomPayload) {
SPacketCustomPayload packet = (SPacketCustomPayload) p;
if (Restrictions.PLUGIN_CHANNEL.equals(packet.getChannelName())) {
final String unknown = replayHandler.getRestrictions().handle(packet);
if (unknown == null) {
return null;
@@ -382,8 +373,8 @@ public class ReplaySender extends ChannelDuplexHandler {
}
}
}
if (p instanceof S40PacketDisconnect) {
IChatComponent reason = ((S40PacketDisconnect) p).func_149165_c();
if (p instanceof SPacketDisconnect) {
ITextComponent reason = ((SPacketDisconnect) p).getReason();
if ("Please update to view this replay.".equals(reason.getUnformattedText())) {
// This version of the mod supports replay restrictions so we are allowed
// to remove this packet.
@@ -393,60 +384,59 @@ public class ReplaySender extends ChannelDuplexHandler {
if(BAD_PACKETS.contains(p.getClass())) return null;
if (p instanceof S3FPacketCustomPayload) {
S3FPacketCustomPayload packet = (S3FPacketCustomPayload) p;
if ("MC|BOpen".equals(packet.func_149169_c())) {
if (p instanceof SPacketCustomPayload) {
SPacketCustomPayload packet = (SPacketCustomPayload) p;
if ("MC|BOpen".equals(packet.getChannelName())) {
return null;
}
if ("MC|RPack".equals(packet.func_149169_c())) {
String url = new String(packet.func_149168_d(), Charsets.UTF_8);
if (url.startsWith("replay://")) {
int id = Integer.parseInt(url.substring("replay://".length()));
Map<Integer, String> index = replayFile.getResourcePackIndex();
if (index != null) {
String hash = index.get(id);
if (hash != null) {
File file = new File(tempResourcePackFolder, hash + ".zip");
if (!file.exists()) {
IOUtils.copy(replayFile.getResourcePack(hash).get(), new FileOutputStream(file));
}
ResourcePackRepository repo = mc.getResourcePackRepository();
repo.field_148533_g = false;
repo.field_148532_f = new FileResourcePack(file);
Minecraft.getMinecraft().scheduleResourcesRefresh();
}
if(p instanceof SPacketResourcePackSend) {
SPacketResourcePackSend packet = (SPacketResourcePackSend) p;
String url = packet.getURL();
if (url.startsWith("replay://")) {
int id = Integer.parseInt(url.substring("replay://".length()));
Map<Integer, String> index = replayFile.getResourcePackIndex();
if (index != null) {
String hash = index.get(id);
if (hash != null) {
File file = new File(tempResourcePackFolder, hash + ".zip");
if (!file.exists()) {
IOUtils.copy(replayFile.getResourcePack(hash).get(), new FileOutputStream(file));
}
mc.getResourcePackRepository().setResourcePackInstance(file);
}
return null;
}
return null;
}
}
if(p instanceof S01PacketJoinGame) {
S01PacketJoinGame packet = (S01PacketJoinGame) p;
if(p instanceof SPacketJoinGame) {
SPacketJoinGame packet = (SPacketJoinGame) p;
allowMovement = true;
int entId = packet.func_149197_c();
int entId = packet.getPlayerId();
actualID = entId;
entId = -1789435; // Camera entity id should be negative which is an invalid id and can't be used by servers
int dimension = packet.func_149194_f();
EnumDifficulty difficulty = packet.func_149192_g();
int maxPlayers = packet.func_149193_h();
WorldType worldType = packet.func_149196_i();
int dimension = packet.getDimension();
EnumDifficulty difficulty = packet.getDifficulty();
int maxPlayers = packet.getMaxPlayers();
WorldType worldType = packet.getWorldType();
p = new S01PacketJoinGame(entId, GameType.ADVENTURE, false, dimension,
difficulty, maxPlayers, worldType);
p = new SPacketJoinGame(entId, GameType.SPECTATOR, false, dimension,
difficulty, maxPlayers, worldType, false);
}
if(p instanceof S07PacketRespawn) {
S07PacketRespawn respawn = (S07PacketRespawn) p;
p = new S07PacketRespawn(respawn.func_149082_c(),
respawn.func_149081_d(), respawn.func_149080_f(), GameType.ADVENTURE);
if(p instanceof SPacketRespawn) {
SPacketRespawn respawn = (SPacketRespawn) p;
p = new SPacketRespawn(respawn.getDimensionID(),
respawn.getDifficulty(), respawn.getWorldType(), GameType.SPECTATOR);
allowMovement = true;
}
if(p instanceof S08PacketPlayerPosLook) {
if(p instanceof SPacketPlayerPosLook) {
if(!hasWorldLoaded) hasWorldLoaded = true;
final S08PacketPlayerPosLook ppl = (S08PacketPlayerPosLook) p;
final SPacketPlayerPosLook ppl = (SPacketPlayerPosLook) p;
if (mc.currentScreen instanceof GuiDownloadTerrain) {
// Close the world loading screen manually in case we swallow the packet
@@ -457,9 +447,17 @@ public class ReplaySender extends ChannelDuplexHandler {
CameraEntity cent = replayHandler.getCameraEntity();
for (SPacketPlayerPosLook.EnumFlags relative : ppl.getFlags()) {
if (relative == SPacketPlayerPosLook.EnumFlags.X
|| relative == SPacketPlayerPosLook.EnumFlags.Y
|| relative == SPacketPlayerPosLook.EnumFlags.Z) {
return null; // At least one of the coordinates is relative, so we don't care
}
}
if(cent != null) {
if(!allowMovement && !((Math.abs(cent.posX - ppl.func_148932_c()) > TP_DISTANCE_LIMIT) ||
(Math.abs(cent.posZ - ppl.func_148933_e()) > TP_DISTANCE_LIMIT))) {
if(!allowMovement && !((Math.abs(cent.posX - ppl.getX()) > TP_DISTANCE_LIMIT) ||
(Math.abs(cent.posZ - ppl.getZ()) > TP_DISTANCE_LIMIT))) {
return null;
} else {
allowMovement = false;
@@ -476,14 +474,14 @@ public class ReplaySender extends ChannelDuplexHandler {
}
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());
}
}.run();
}
if(p instanceof S2BPacketChangeGameState) {
S2BPacketChangeGameState pg = (S2BPacketChangeGameState)p;
int reason = pg.func_149138_c();
if(p instanceof SPacketChangeGameState) {
SPacketChangeGameState pg = (SPacketChangeGameState)p;
int reason = pg.getGameState();
// only allow the following packets:
// 1 - End raining
@@ -497,7 +495,7 @@ public class ReplaySender extends ChannelDuplexHandler {
}
}
if (p instanceof S02PacketChat) {
if (p instanceof SPacketChat) {
if (!ReplayModReplay.instance.getCore().getSettingsRegistry().get(Setting.SHOW_CHAT)) {
return null;
}
@@ -510,6 +508,7 @@ public class ReplaySender extends ChannelDuplexHandler {
@SuppressWarnings("unchecked")
public void channelActive(ChannelHandlerContext ctx) throws Exception {
this.ctx = ctx;
ctx.attr(NetworkManager.PROTOCOL_ATTRIBUTE_KEY).set(EnumConnectionState.PLAY);
super.channelActive(ctx);
}
@@ -727,11 +726,11 @@ public class ReplaySender extends ChannelDuplexHandler {
protected Packet processPacketAsync(Packet p) {
//If hurrying, ignore some packets, except for short durations
if(desiredTimeStamp - lastTimeStamp > 1000) {
if(p instanceof S2APacketParticles) return null;
if(p instanceof SPacketParticles) return null;
if(p instanceof S0EPacketSpawnObject) {
S0EPacketSpawnObject pso = (S0EPacketSpawnObject)p;
int type = pso.func_148993_l();
if(p instanceof SPacketSpawnObject) {
SPacketSpawnObject pso = (SPacketSpawnObject)p;
int type = pso.getType();
if(type == 76) { // Firework rocket
return null;
}
@@ -817,49 +816,6 @@ public class ReplaySender extends ChannelDuplexHandler {
}
protected Packet processPacketSync(Packet p) {
if (p instanceof S21PacketChunkData) {
S21PacketChunkData packet = (S21PacketChunkData) p;
if (packet.func_149276_g() == 0) {
// If the chunk is getting unloaded, we will have to forcefully update the position of all entities
// within. Otherwise, if there wasn't a game tick recently, there may be entities that have moved
// out of the chunk by now but are still registered in it. If we do not update those, they will get
// unloaded even though they shouldn't.
// To make things worse, it seems like players were never supposed to be unloaded this way because
// they will remain glitched in the World#playerEntities list.
World world = mc.theWorld;
IChunkProvider chunkProvider = world.getChunkProvider();
// Get the chunk that will be unloaded
Chunk chunk = chunkProvider.provideChunk(packet.func_149273_e(), packet.func_149271_f());
if (!chunk.isEmpty()) {
List<Entity> entitiesInChunk = new ArrayList<>();
// Gather all entities in that chunk
for (List entityList : chunk.entityLists) {
@SuppressWarnings("unchecked")
Collection<Entity> typedEntityList = entityList;
entitiesInChunk.addAll(typedEntityList);
}
for (Entity entity : entitiesInChunk) {
// Skip interpolation of position updates coming from server
// (See: newX in EntityLivingBase or otherPlayerMPX in EntityOtherPlayerMP)
entity.onUpdate();
// Check whether the entity has left the chunk
int chunkX = MathHelper.floor_double(entity.posX / 16);
int chunkZ = MathHelper.floor_double(entity.posZ / 16);
if (entity.chunkCoordX != chunkX || entity.chunkCoordZ != chunkZ) {
// Entity has left the chunk
chunk.removeEntityAtIndex(entity, entity.chunkCoordY);
if (chunkProvider.chunkExists(chunkX, chunkZ)) {
chunkProvider.provideChunk(chunkX, chunkZ).addEntity(entity);
} else {
// Entity has left all loaded chunks
entity.addedToChunk = false;
}
}
}
}
}
}
return p; // During synchronous playback everything is sent normally
}

View File

@@ -7,29 +7,33 @@ import com.replaymod.replay.ReplayModReplay;
import com.replaymod.replay.Setting;
import com.replaymod.replay.events.ReplayChatMessageEvent;
import com.replaymod.replaystudio.util.Location;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.eventhandler.EventPriority;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.TickEvent;
import lombok.Getter;
import lombok.Setter;
import net.minecraft.block.material.Material;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.EntityClientPlayerMP;
import net.minecraft.client.entity.AbstractClientPlayer;
import net.minecraft.client.entity.EntityPlayerSP;
import net.minecraft.client.network.NetHandlerPlayClient;
import net.minecraft.client.settings.KeyBinding;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.item.EntityItemFrame;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.stats.StatFileWriter;
import net.minecraft.util.IChatComponent;
import net.minecraft.util.MovingObjectPosition;
import net.minecraft.inventory.EntityEquipmentSlot;
import net.minecraft.stats.StatisticsManager;
import net.minecraft.util.EnumHand;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.Session;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.world.World;
import net.minecraftforge.client.event.EntityViewRenderEvent;
import net.minecraftforge.client.event.RenderGameOverlayEvent;
import net.minecraftforge.client.event.RenderHandEvent;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.eventhandler.EventPriority;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
import java.util.Map;
import java.util.UUID;
@@ -40,7 +44,7 @@ import java.util.function.Function;
* During a replay {@link Minecraft#thePlayer} should be an instance of this class.
* Camera movement is controlled by a separate {@link CameraController}.
*/
public class CameraEntity extends EntityClientPlayerMP {
public class CameraEntity extends EntityPlayerSP {
/**
* Roll of this camera in degrees.
*/
@@ -64,10 +68,8 @@ public class CameraEntity extends EntityClientPlayerMP {
*/
private final EventHandler eventHandler = new EventHandler();
public CameraEntity(Minecraft mcIn, World worldIn, Session session, NetHandlerPlayClient netHandlerPlayClient, StatFileWriter statFileWriter) {
super(mcIn, worldIn, session, netHandlerPlayClient, statFileWriter);
entityUniqueID = UUID.randomUUID(); // Need to not have the same UUID as the player who recorded the replay
FMLCommonHandler.instance().bus().register(eventHandler);
public CameraEntity(Minecraft mcIn, World worldIn, NetHandlerPlayClient netHandlerPlayClient, StatisticsManager statisticsManager) {
super(mcIn, worldIn, netHandlerPlayClient, statisticsManager);
MinecraftForge.EVENT_BUS.register(eventHandler);
if (ReplayModReplay.instance.getReplayHandler().getSpectatedUUID() == null) {
cameraController = ReplayModReplay.instance.createCameraController(this);
@@ -125,33 +127,31 @@ public class CameraEntity extends EntityClientPlayerMP {
* @param to The entity whose position to copy
*/
public void setCameraPosRot(Entity to) {
if (to == this) return;
float yOffset = 1.62f; // Magic value (eye height) from EntityRenderer#orientCamera
prevPosX = to.prevPosX;
prevPosY = to.prevPosY + yOffset;
prevPosY = to.prevPosY;
prevPosZ = to.prevPosZ;
prevRotationYaw = to.prevRotationYaw;
prevRotationPitch = to.prevRotationPitch;
posX = to.posX;
posY = to.posY + yOffset;
posY = to.posY;
posZ = to.posZ;
rotationYaw = to.rotationYaw;
rotationPitch = to.rotationPitch;
lastTickPosX = to.lastTickPosX;
lastTickPosY = to.lastTickPosY + yOffset;
lastTickPosY = to.lastTickPosY;
lastTickPosZ = to.lastTickPosZ;
updateBoundingBox();
}
private void updateBoundingBox() {
this.boundingBox.setBounds(
setEntityBoundingBox(new AxisAlignedBB(
posX - width / 2, posY, posZ - width / 2,
posX + width / 2, posY + height, posZ + width / 2);
posX + width / 2, posY + height, posZ + width / 2));
}
@Override
public void onUpdate() {
EntityLivingBase view = mc.renderViewEntity;
Entity view = mc.getRenderViewEntity();
if (view != null) {
// Make sure we're always spectating the right entity
// This is important if the spectated player respawns as their
@@ -160,16 +160,11 @@ public class CameraEntity extends EntityClientPlayerMP {
if (spectating != null && (view.getUniqueID() != spectating
|| view.worldObj != worldObj)
|| worldObj.getEntityByID(view.getEntityId()) != view) {
if (spectating == null) {
// Entity (non-player) died, stop spectating
ReplayModReplay.instance.getReplayHandler().spectateEntity(this);
return;
}
view = worldObj.getPlayerEntityByUUID(spectating);
if (view != null) {
mc.renderViewEntity = view;
mc.setRenderViewEntity(view);
} else {
mc.renderViewEntity = this;
mc.setRenderViewEntity(this);
return;
}
}
@@ -192,7 +187,7 @@ public class CameraEntity extends EntityClientPlayerMP {
@Override
public void setAngles(float yaw, float pitch) {
if (mc.renderViewEntity == this) {
if (mc.getRenderViewEntity() == this) {
// Only update camera rotation when the camera is the view
super.setAngles(yaw, pitch);
}
@@ -209,8 +204,8 @@ public class CameraEntity extends EntityClientPlayerMP {
}
@Override
public boolean handleLavaMovement() {
return falseUnlessSpectating(Entity::handleLavaMovement); // Make sure no lava overlay is rendered
public boolean isInLava() {
return falseUnlessSpectating(Entity::isInLava); // Make sure no lava overlay is rendered
}
@Override
@@ -224,7 +219,7 @@ public class CameraEntity extends EntityClientPlayerMP {
}
private boolean falseUnlessSpectating(Function<Entity, Boolean> property) {
Entity view = mc.renderViewEntity;
Entity view = mc.getRenderViewEntity();
if (view != null && view != this) {
return property.apply(view);
}
@@ -236,17 +231,20 @@ public class CameraEntity extends EntityClientPlayerMP {
return false; // We are in full control of ourselves
}
@Override
protected void createRunningParticles() {
// We do not produce any particles, we are a camera
}
@Override
public boolean canBeCollidedWith() {
return false; // We are a camera, we cannot collide
}
/* TODO 1.7.10: isSpectator has been added in 1.8, this is probably going to require lots of manual changes
@Override
public boolean isSpectator() {
return ReplayModReplay.instance.getReplayHandler().isCameraView(); // Make sure we're treated as spectator
}
*/
@Override
public boolean shouldRenderInPass(int pass) {
@@ -258,7 +256,7 @@ public class CameraEntity extends EntityClientPlayerMP {
@Override
public boolean isInvisible() {
Entity view = mc.renderViewEntity;
Entity view = mc.getRenderViewEntity();
if (view != this) {
return view.isInvisible();
}
@@ -267,16 +265,25 @@ public class CameraEntity extends EntityClientPlayerMP {
@Override
public ResourceLocation getLocationSkin() {
Entity view = mc.renderViewEntity;
Entity view = mc.getRenderViewEntity();
if (view != this && view instanceof EntityPlayer) {
return Utils.getResourceLocationForPlayerUUID(view.getUniqueID());
}
return super.getLocationSkin();
}
@Override
public String getSkinType() {
Entity view = mc.getRenderViewEntity();
if (view != this && view instanceof AbstractClientPlayer) {
return ((AbstractClientPlayer) view).getSkinType();
}
return super.getSkinType();
}
@Override
public float getSwingProgress(float renderPartialTicks) {
Entity view = mc.renderViewEntity;
Entity view = mc.getRenderViewEntity();
if (view != this && view instanceof EntityPlayer) {
return ((EntityPlayer) view).getSwingProgress(renderPartialTicks);
}
@@ -284,12 +291,49 @@ public class CameraEntity extends EntityClientPlayerMP {
}
@Override
public MovingObjectPosition rayTrace(double p_174822_1_, float p_174822_3_) {
MovingObjectPosition pos = super.rayTrace(p_174822_1_, 1f);
public float getCooldownPeriod() {
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)
if(pos != null && pos.typeOfHit == MovingObjectPosition.MovingObjectType.BLOCK) {
pos.typeOfHit = MovingObjectPosition.MovingObjectType.MISS;
if(pos != null && pos.typeOfHit == RayTraceResult.Type.BLOCK) {
pos.typeOfHit = RayTraceResult.Type.MISS;
}
return pos;
@@ -304,7 +348,6 @@ public class CameraEntity extends EntityClientPlayerMP {
@Override
public void setDead() {
super.setDead();
FMLCommonHandler.instance().bus().unregister(eventHandler);
MinecraftForge.EVENT_BUS.unregister(eventHandler);
}
@@ -316,17 +359,17 @@ public class CameraEntity extends EntityClientPlayerMP {
if (mc.gameSettings.keyBindAttack.isPressed() || mc.gameSettings.keyBindUseItem.isPressed()) {
if (canSpectate(mc.pointedEntity)) {
ReplayModReplay.instance.getReplayHandler().spectateEntity((EntityLivingBase) mc.pointedEntity);
ReplayModReplay.instance.getReplayHandler().spectateEntity(mc.pointedEntity);
// Make sure we don't exit right away
mc.gameSettings.keyBindSneak.pressTime = 0;
}
}
Map<String, KeyBinding> keyBindings = ReplayMod.instance.getKeyBindingRegistry().getKeyBindings();
if (keyBindings.get("replaymod.input.rollclockwise").getIsKeyPressed()) {
if (keyBindings.get("replaymod.input.rollclockwise").isKeyDown()) {
roll += Utils.isCtrlDown() ? 0.2 : 1;
}
if (keyBindings.get("replaymod.input.rollcounterclockwise").getIsKeyPressed()) {
if (keyBindings.get("replaymod.input.rollcounterclockwise").isKeyDown()) {
roll -= Utils.isCtrlDown() ? 0.2 : 1;
}
}
@@ -340,19 +383,16 @@ public class CameraEntity extends EntityClientPlayerMP {
public boolean canSpectate(Entity e) {
return e != null && !e.isInvisible()
&& e instanceof EntityPlayer; // 1.7.10 has no general concept of eye height
&& (e instanceof EntityPlayer || e instanceof EntityLiving || e instanceof EntityItemFrame);
}
@Override
public void addChatMessage(IChatComponent message) {
public void addChatMessage(ITextComponent message) {
if (MinecraftForge.EVENT_BUS.post(new ReplayChatMessageEvent(this))) return;
super.addChatMessage(message);
}
// All event handlers need to be public in 1.7.10
public class EventHandler {
private EventHandler() {}
private class EventHandler {
@SubscribeEvent
public void onPreClientTick(TickEvent.ClientTickEvent event) {
if (event.phase == TickEvent.Phase.START) {
@@ -371,9 +411,13 @@ public class CameraEntity extends EntityClientPlayerMP {
@SubscribeEvent
public void preCrosshairRender(RenderGameOverlayEvent.Pre event) {
// 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));
}
// Hotbar should never be rendered
if (event.getType() == RenderGameOverlayEvent.ElementType.HOTBAR) {
event.setCanceled(true);
}
}
@SubscribeEvent
@@ -390,24 +434,27 @@ public class CameraEntity extends EntityClientPlayerMP {
@SubscribeEvent
public void onRenderHand(RenderHandEvent event) {
// Unless we are spectating another player, don't render our hand
if (mc.renderViewEntity == CameraEntity.this || !(mc.renderViewEntity instanceof EntityPlayer)) {
if (mc.getRenderViewEntity() == CameraEntity.this || !(mc.getRenderViewEntity() instanceof EntityPlayer)) {
event.setCanceled(true);
}
}
@SubscribeEvent(priority = EventPriority.LOWEST)
public void onRenderHandMonitor(RenderHandEvent event) {
Entity view = mc.renderViewEntity;
Entity view = mc.getRenderViewEntity();
if (view instanceof EntityPlayer) {
EntityPlayer player = (EntityPlayer) view;
// When the spectated player has changed, force equip their items to prevent the equip animation
if (lastHandRendered != player) {
lastHandRendered = player;
mc.entityRenderer.itemRenderer.prevEquippedProgress = 1;
mc.entityRenderer.itemRenderer.equippedProgress = 1;
mc.entityRenderer.itemRenderer.itemToRender = player.inventory.getCurrentItem();
mc.entityRenderer.itemRenderer.equippedItemSlot = player.inventory.currentItem;
mc.entityRenderer.itemRenderer.prevEquippedProgressMainHand = 1;
mc.entityRenderer.itemRenderer.prevEquippedProgressOffHand = 1;
mc.entityRenderer.itemRenderer.equippedProgressMainHand = 1;
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.renderArmPitch = mc.thePlayer.prevRenderArmPitch = player.rotationPitch;
@@ -415,20 +462,18 @@ public class CameraEntity extends EntityClientPlayerMP {
}
}
/* TODO 1.7.10: This event has been added in 1.8, has to be replaced with mixin
@SubscribeEvent
public void onEntityViewRenderEvent(EntityViewRenderEvent.CameraSetup event) {
if (mc.getRenderViewEntity() == CameraEntity.this) {
event.roll = roll;
event.setRoll(roll);
}
}
*/
private boolean heldItemTooltipsWasTrue;
@SubscribeEvent
public void preRenderGameOverlay(RenderGameOverlayEvent.Pre event) {
switch (event.type) {
switch (event.getType()) {
case ALL:
heldItemTooltipsWasTrue = mc.gameSettings.heldItemTooltips;
mc.gameSettings.heldItemTooltips = false;
@@ -441,12 +486,15 @@ public class CameraEntity extends EntityClientPlayerMP {
case EXPERIENCE:
case HEALTHMOUNT:
case JUMPBAR:
case POTION_ICONS:
event.setCanceled(true);
break;
case HELMET:
case PORTAL:
case CROSSHAIRS:
case BOSSHEALTH:
case BOSSINFO:
case SUBTITLES:
case TEXT:
case CHAT:
case PLAYER_LIST:
@@ -457,7 +505,7 @@ public class CameraEntity extends EntityClientPlayerMP {
@SubscribeEvent
public void postRenderGameOverlay(RenderGameOverlayEvent.Post event) {
if (event.type != RenderGameOverlayEvent.ElementType.ALL) return;
if (event.getType() != RenderGameOverlayEvent.ElementType.ALL) return;
mc.gameSettings.heldItemTooltips = heldItemTooltipsWasTrue;
}
}

View File

@@ -2,8 +2,8 @@ package com.replaymod.replay.camera;
import net.minecraft.client.Minecraft;
import net.minecraft.client.settings.KeyBinding;
import net.minecraft.util.MathHelper;
import net.minecraft.util.Vec3;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import org.lwjgl.Sys;
// 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 DECAY = MAX_SPEED/3;
private Vec3 direction;
private Vec3 dirBefore;
private Vec3d direction;
private Vec3d dirBefore;
private double motion;
private long lastCall = Sys.getTime();
@@ -34,7 +34,7 @@ public class ClassicCameraController implements CameraController {
boolean forward = false, backward = false, left = false, right = false, up = false, down = false;
speedup = false;
for(KeyBinding kb : Minecraft.getMinecraft().gameSettings.keyBindings) {
if(!kb.getIsKeyPressed()) continue;
if(!kb.isKeyDown()) continue;
if(kb.getKeyDescription().equals("key.forward")) {
forward = true;
speedup = true;
@@ -129,7 +129,7 @@ public class ClassicCameraController implements CameraController {
return;
}
Vec3 movement = direction.normalize();
Vec3d movement = direction.normalize();
double factor = motion * (frac / 1000D);
camera.moveCamera(movement.xCoord * factor, movement.yCoord * factor, movement.zCoord * factor);
@@ -158,10 +158,10 @@ public class ClassicCameraController implements CameraController {
break;
}
Vec3 dbf = direction;
Vec3d dbf = direction;
if(dirBefore != null) {
direction = dirBefore.normalize().addVector(direction.xCoord, direction.yCoord, direction.zCoord);
direction = dirBefore.normalize().add(direction);
}
dirBefore = dbf;
@@ -169,12 +169,12 @@ public class ClassicCameraController implements CameraController {
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 f3 = MathHelper.sin(-yaw * 0.017453292F - (float)Math.PI);
float f4 = -MathHelper.cos(-pitch * 0.017453292F);
float f5 = MathHelper.sin(-pitch * 0.017453292F);
return Vec3.createVectorHelper((double)(f3 * f4), (double)f5, (double)(f2 * f4));
return new Vec3d((double)(f3 * f4), (double)f5, (double)(f2 * f4));
}
public enum MoveDirection {

View File

@@ -34,15 +34,16 @@ public class SpectatorCameraController implements CameraController {
// Always make sure the camera is in the exact same spot as the spectated entity
// This is necessary as some rendering code for the hand doesn't respect the view entity
// and always uses mc.thePlayer
Entity view = mc.renderViewEntity;
Entity view = mc.getRenderViewEntity();
if (view != null && view != camera) {
camera.setCameraPosRot(mc.renderViewEntity);
camera.setCameraPosRot(mc.getRenderViewEntity());
// If it's a player, also 'steal' its inventory so the rendering code knows what item to render
if (view instanceof EntityPlayer) {
EntityPlayer viewPlayer = (EntityPlayer) view;
camera.inventory = viewPlayer.inventory;
camera.itemInUse = viewPlayer.itemInUse;
camera.itemInUseCount = viewPlayer.itemInUseCount;
camera.itemStackMainHand = viewPlayer.itemStackMainHand;
camera.swingingHand = viewPlayer.swingingHand;
camera.activeItemStackUseCount = viewPlayer.activeItemStackUseCount;
}
}
}

View File

@@ -39,7 +39,7 @@ public class VanillaCameraController implements CameraController {
if (partialTicksPassed == 0) return;
Vector3f direction = new Vector3f(0, 0, 0);
for (int i = 0; i < 6; i++) { // First, get movement direction depending on keys pressed
if (bindings[i].getIsKeyPressed()) {
if (bindings[i].isKeyDown()) {
Vector3f.add(direction, DIRECTIONS[i], direction);
}
}

View File

@@ -1,10 +1,10 @@
package com.replaymod.replay.events;
import com.replaymod.replay.camera.CameraEntity;
import cpw.mods.fml.common.eventhandler.Cancelable;
import cpw.mods.fml.common.eventhandler.Event;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import net.minecraftforge.fml.common.eventhandler.Cancelable;
import net.minecraftforge.fml.common.eventhandler.Event;
@Cancelable
@RequiredArgsConstructor

View File

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

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