Compare commits

..

23 Commits

Author SHA1 Message Date
johni0702
2428c01c9c Merge branch '1.10.2' into 1.11
372206f 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 20:28:23 +01:00
johni0702
260720d5ba Update Forge to latest recommended build (2188) 2016-12-10 20:25:33 +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
58b7c44a33 Update to 1.11 2016-12-03 21:12:08 +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
105 changed files with 865 additions and 2376 deletions

View File

@@ -3,11 +3,9 @@ pipeline:
image: plugins/git
tags: true
build:
image: maven:3.3.9-jdk-8
image: openjdk:8
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
- JAVA_OPTS=-Xmx1516M ./gradlew setupDecompWorkspace
- ./gradlew :build
archive:
image: plugins/s3

View File

@@ -1 +1 @@
eyJhbGciOiJIUzI1NiJ9.cGlwZWxpbmU6CiAgY2xvbmU6CiAgICBpbWFnZTogcGx1Z2lucy9naXQKICAgIHRhZ3M6IHRydWUKICBidWlsZDoKICAgIGltYWdlOiBtYXZlbjozLjMuOS1qZGstOAogICAgY29tbWFuZHM6CiAgICAgIC0gY3VybCAtLWZhaWwgLS1jcmVhdGUtZGlycyAtbyAkSE9NRS8uZ3JhZGxlL2luaXQuZC9taXJyb3IuZ3JhZGxlIGh0dHBzOi8vbWF2ZW4uam9obmkwNzAyLmRlL2NvbmZpZy9ncmFkbGUKICAgICAgLSBjdXJsIC0tZmFpbCAtLWNyZWF0ZS1kaXJzIC1vICRIT01FLy5tMi9zZXR0aW5ncy54bWwgaHR0cHM6Ly9tYXZlbi5qb2huaTA3MDIuZGUvY29uZmlnL21hdmVuCiAgICAgIC0gLi9ncmFkbGV3IHNldHVwQ0lXb3Jrc3BhY2UKICAgICAgLSAuL2dyYWRsZXcgOmJ1aWxkCiAgYXJjaGl2ZToKICAgIGltYWdlOiBwbHVnaW5zL3MzCiAgICBhY2w6IHB1YmxpYy1yZWFkCiAgICByZWdpb246ICJ1cy1lYXN0LTEiCiAgICBidWNrZXQ6ICJyZXBsYXltb2QiCiAgICBwYXRoX3N0eWxlOiB0cnVlCiAgICBzb3VyY2U6IGJ1aWxkL2xpYnMvKgogICAgc3RyaXBfcHJlZml4OiBidWlsZC9saWJzLwogICAgdGFyZ2V0OiAvCg.riE6lhwfdNUhI7LO2SxEKFOjzjaX40ne7Ae9gyP75KE
eyJhbGciOiJIUzI1NiJ9.cGlwZWxpbmU6CiAgY2xvbmU6CiAgICBpbWFnZTogcGx1Z2lucy9naXQKICAgIHRhZ3M6IHRydWUKICBidWlsZDoKICAgIGltYWdlOiBvcGVuamRrOjgKICAgIGNvbW1hbmRzOgogICAgICAtIEpBVkFfT1BUUz0tWG14MTUxNk0gLi9ncmFkbGV3IHNldHVwRGVjb21wV29ya3NwYWNlCiAgICAgIC0gLi9ncmFkbGV3IDpidWlsZAogIGFyY2hpdmU6CiAgICBpbWFnZTogcGx1Z2lucy9zMwogICAgYWNsOiBwdWJsaWMtcmVhZAogICAgcmVnaW9uOiAidXMtZWFzdC0xIgogICAgYnVja2V0OiAicmVwbGF5bW9kIgogICAgcGF0aF9zdHlsZTogdHJ1ZQogICAgc291cmNlOiBidWlsZC9saWJzLyoKICAgIHN0cmlwX3ByZWZpeDogYnVpbGQvbGlicy8KICAgIHRhcmdldDogLwo.Q_jQxd73CuNh8ZTGTw31yMmBIzWprIagmaddFYovDrc

View File

@@ -19,8 +19,8 @@ buildscript {
}
}
dependencies {
classpath 'net.minecraftforge.gradle:ForgeGradle:2.0-SNAPSHOT'
classpath 'org.spongepowered:mixingradle:0.3-SNAPSHOT'
classpath 'net.minecraftforge.gradle:ForgeGradle:2.2-SNAPSHOT'
classpath 'org.spongepowered:mixingradle:0.4-SNAPSHOT'
}
}
@@ -36,11 +36,9 @@ archivesBaseName = "replaymod"
minecraft {
coreMod = 'com.replaymod.core.LoadingPlugin'
version = '1.8-11.14.4.1563'
version = '1.11-13.19.1.2188'
runDir = "eclipse"
mappings = "snapshot_nodoc_20141130"
replace '@MOD_VERSION@', project.version
replace '@MC_VERSION@', project.minecraft.version
mappings = "snapshot_20161111"
}
repositories {
@@ -61,7 +59,7 @@ configurations {
dependencies {
compile 'org.projectlombok:lombok:1.16.4'
compile 'org.spongepowered:mixin:0.6.4-SNAPSHOT'
compile 'org.spongepowered:mixin:0.5.9-SNAPSHOT'
shade 'com.googlecode.mp4parser:isoparser:1.1.7'
shade 'org.apache.commons:commons-exec:1.3'
shade 'com.google.apis:google-api-services-youtube:v3-rev178-1.22.0'
@@ -138,6 +136,7 @@ jar {
manifest {
attributes 'TweakClass': 'org.spongepowered.asm.launch.MixinTweaker',
'TweakOrder': '0',
'FMLCorePluginContainsFMLMod': 'true',
'FMLAT': 'replaymod_at.cfg'
}
}

View File

@@ -2,14 +2,7 @@
## Installing the Replay Mod [replaymod]
The **ReplayMod** requires **Minecraft Forge** to be installed.
Depending on your **Replay Mod** version we recommend the following **Forge** versions:
- Replay Mod 2.0.0 or later for Minecraft 1.11: Forge [1.11-13.19.1.2189](https://files.minecraftforge.net/maven/net/minecraftforge/forge/index_1.11.html)
- Replay Mod 2.0.0 or later for Minecraft 1.10.2: Forge [1.10.2-12.18.2.2099](https://files.minecraftforge.net/maven/net/minecraftforge/forge/index_1.10.2.html)
- Replay Mod 2.0.0 or later for Minecraft 1.9.4: Forge [1.9.4-12.17.0.1976](https://files.minecraftforge.net/maven/net/minecraftforge/forge/index_1.9.4.html)
- Replay Mod 2.0.0 or later for Minecraft 1.8: Forge [1.8-11.14.4.1563](https://files.minecraftforge.net/maven/net/minecraftforge/forge/index_1.8.html)
- Replay Mod 1.0.8 or older for Minecraft 1.8: Forge [1.8-11.14.3.1450](http://files.minecraftforge.net/maven/net/minecraftforge/forge/index_1.8.html)
For the use with the **Replay Mod**, we recommend using Forge Version `1.8-11.14.3.1450`, which can be downloaded [here](http://files.minecraftforge.net/maven/net/minecraftforge/forge/index_1.8.html)
Other Forge Versions might, but don't necessarily work.
If you don't know how to install Forge, follow [this tutorial](https://www.youtube.com/watch?v=4i7-RystzC4).
@@ -32,10 +25,6 @@ Next, download this **Batch Script** to quickly install FFmpeg: <https://replaym
Put the downloaded script into the folder where you extracted FFmpeg and run itt by double-clicking it.
If you see a success message in the console, you've sucessfully installed **FFmpeg**!
Alternatively, starting with ReplayMod 2.0.0-b5, you can also extract the downloaded `.7z` file into a `ffmpeg` folder
(you have to create it, it doesn't exist by default) in your `.minecraft` folder. No need to run any **Batch Script**.
The FFmpeg executable should end up at `.minecraft/ffmpeg/bin/ffmpeg.exe`.
### Mac OSX [mac]
On OSX, you can install **FFmpeg** with **[Homebrew](http://brew.sh/)** using `brew install ffmpeg`.
@@ -48,35 +37,26 @@ If in doubt, consult the documentation of your distribution.
## Compatibility with other Mods [compatibility]
### General information [general]
In General, the Replay Mod _should_ be compatible with most Forge Mods.
However, due to the way that recording works, many Mods that add new Blocks, Mobs or Game Mechanics are not supported.
Mods that simply modify the client (like the [PixelCam Mod](http://www.minecraftforum.net/forums/mapping-and-modding/minecraft-mods/2327429-pixelcam-camera-studio-for-minecraft-1-8)
or the [CommandGroups Mod](http://www.minecraftforum.net/forums/mapping-and-modding/minecraft-mods/2443430-command-groups-mod-order-label-and-color-command)) usually work.
### Optifine [optifine]
The **Replay Mod** is compatible with `Optifine 1.8.0 HD U D5`, which can be downloaded at <https://optifine.net/downloads>.
Other Optifine versions for Minecraft 1.8.0 might, but don't have to be compatible as well.
> **Warning:** If you have Optifine installed, you **can not render videos** from within the Replay Mod.
This might change in future Replay Mod versions.
### Shaders Mod [shaders]
[Karyonix' Shaders Mod](http://www.minecraftforum.net/forums/mapping-and-modding/minecraft-mods/1286604-shaders-mod-updated-by-karyonix)
is no longer compatible with Minecraft Forge starting with 1.9.4. As such it is not compatible with the Replay Mod either.
Please use [Optifine](https://optifine.net/) instead.
### Custom Main Menu [custom-main-menu]
The [Custom Main Menu](https://mods.curse.com/mc-mods/minecraft/226406-custom-main-menu) mod is often used in mod packs to customize their Main Menu with a button layout fitting the background image, links to their website / bug tracker and similar.
If you are familiar with it, the button ids for the Replay Mod are: **17890234** (text: `replaymod.gui.replayviewer`), **17890235** (text: `replaymod.gui.replayviewer`) and **17890236** (text: `replaymod.gui.replaycenter`)
Due to the nature of this Custom Main Menu mod, buttons added to the Main Menu by 3rd party mods like the **Replay Mod** will not show up by default.
Thus, to access the Replay Viewer/Editor/Center, you need to manually configure the position for those buttons.
To do so, you need to modify the Custom Main Menu configuration file.
Usually, you can find it at `.minecraft/config/CustomMainMenu/mainmenu.json` (if you're using a mod pack launcher, it normally has its own .minecraft folder).
For an explanation of this config file, have a look at Custom Main Menu's page.
You can find a list of already modified config files [here](https://gist.github.com/Johni0702/3f3fab81dbf7ada83d045d9fe8f345aa).
You can use the Replay Mod together with [karyonix' Shaders Mod](http://www.minecraftforum.net/forums/mapping-and-modding/minecraft-mods/1286604-shaders-mod-updated-by-karyonix),
but rendering videos results in **possible crashes** and **malformed videos**.
Therefore, you have to **uninstall the Shaders Mod before rendering**.
## Troubleshooting [troubleshooting]
If you need help installing the **Replay Mod** or **FFmpeg**, please read [this forum thread](https://www.replaymod.com/forum/thread/220)
by [bela333](https://www.replaymod.com/user/bela333) - it covers most of the problems that users encountered so far.
## Settings [settings]
To access the **Replay Mod Settings** from the Main Menu click the **"Mods"** button, select the **Replay Mod** from the list and
the click the **"Config"** button.
When in a Replay, you can either bind a hotkey to the **Replay Mod Settings** in Minecraft's Control settings
or use the hotkey GUI by clicking on the arrow button in the lower left corner.
# Recording [recording]
![](img/recording-indicator.jpg)
The **Recording Indicator** which is displayed while recording
@@ -108,8 +88,6 @@ You can move through block similar to Spectator Mode.
Using the **Mouse Wheel**, you can modify your flying speed. Scrolling **up** causes you to fly faster, while scrolling **down** slows you down.
If you prefer the camera to be controlled similar to vanilla Creative Mode, you can change that in the **Replay Settings**.
### Camera Roll [roll]
![](img/camera-tilted.jpg)
A Screenshot taken with a **tilted camera**
@@ -273,15 +251,13 @@ The **Keyframe Repository** with two **Path Presets**
Using the `X` key, you can open the **Keyframe Repository**. Every Replay File has a separate Keyframe Repository which can contain several **Keyframe Presets**.
To save the Keyframes currently on your Timeline, click the **"Save as..."** button.
You can change a Preset's name, select it from the list and click the **"Rename"** button.
To load a Keyframe Preset, select a Preset from the list and click the **"Load"** button.
To save the Keyframes currently on your Timeline, click the **"Save current Path"** button.
You can change the Preset's name using the Text Input Field in the upper right corner.
To load a Keyframe Preset, select a Preset from the list on the left side and click the **"Load"** button.
Using the **"Remove"** button, you can permanently delete a Keyframe Preset from the **Keyframe Repository**.
## Custom Objects [objects]
> Custom Objects are not available in the 2.0.0 versions. They are expected to return eventually.
[YouTube](_Xdpg828fbE)
### Introduction [introduction]
@@ -440,10 +416,12 @@ There are **7 Encoding Presets** you can choose from:
### Advanced Settings [advanced]
![](img/rendersettings-advanced.jpg)
The **Advanced Render Settings**
The **Advanced Render Settings** Screen
#### Render Nametags [nametags]
If you don't want to see Entity's Nametags in the rendered video, simply un-tick the checkbox. Hidden Nametags also include Nametags of **Armor Stands** and **Mobs**, **not only Players**!
By clicking the **"Advanced Settings" Button** in the lower right corner, you can access more settings.
#### Hide Nametags [nametags]
If you don't want to see Entity's Nametags in the rendered video, simply tick the checkbox. Hidden Nametags also include Nametags of **Armor Stands** and **Mobs**, **not only Players**!
#### Stabilize Camera [stabilize]
This setting is only applicable for **Cubic Rendering** and **Equirectangular Rendering**.
@@ -465,14 +443,14 @@ Using a **Video Editing Software** like **Adobe After Effects** or **Sony Vegas*
### Command Line Settings [commandline]
![](img/rendersettings-commandline.jpg)
The **Command Line Render Settings**
The **Command Line Render Settings** Screen
> **Note**: This is for advanced users only. If you do not know what your're doing, leave these settings as they are.
The **Replay Mod** runs [FFmpeg](http://ffmpeg.org/) via the **Command Line** to encode videos.
You can customize both the **executed Command** and the **Command Line Arguments** in the **Command Line Settings** part of the **Render Settings Screen**.
You can customize both the **executed Command** and the **Command Line Arguments** in the **Command Line Settings** Tag of the **Render Settings Screen**.
#### Custom Command [command]
If you leave the left input field blank, `ffmpeg` will be used as **command**. If you haven't set your **PATH variable** to link to your FFmpeg distribution, simply enter the full path to your FFmpeg executable (e.g. `C:/ffmpeg/ffmpeg.exe` or `/usr/local/bin/ffmpeg`).
@@ -554,7 +532,7 @@ By clicking on a player's **Name or Head**, you can **spectate** this player.
Next to each player, there's a **checkbox** using which you can toggle the player's **visibility**.
This way, you can hide certain players from the Replay.
Below the individual checkboxes, there are two checkbox buttons - one to **hide all players** and one to **show all players**.
Above the individual checkboxes, there are two checkbox buttons - one to **hide all players** and one to **show all players**.
If you want to **save the player visibility settings**, check **"Remember Hidden Players" checkbox**.
If checked, invisible players will stay invisible after closing the Replay (this can of course be reverted),

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 147 KiB

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 140 KiB

2
jGui

Submodule jGui updated: 94e4b4c388...5c0ca7d7d8

View File

@@ -2,21 +2,22 @@ package com.replaymod.compat;
import com.replaymod.compat.optifine.DisableFastRender;
import com.replaymod.compat.shaders.ShaderBeginRender;
import net.minecraftforge.fml.common.FMLCommonHandler;
import com.replaymod.core.ReplayMod;
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@",
acceptedMinecraftVersions = "@MC_VERSION@",
useMetadata = true)
@Mod(modid = ReplayModCompat.MOD_ID, useMetadata = true)
public class ReplayModCompat {
public static final String MOD_ID = "replaymod-compat";
@Mod.Instance(ReplayMod.MOD_ID)
private static ReplayMod core;
@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());
}

View File

@@ -1,22 +0,0 @@
package com.replaymod.compat.shaders.mixin;
import net.minecraft.client.renderer.EntityRenderer;
import net.minecraftforge.client.ForgeHooksClient;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Pseudo;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Pseudo
@Mixin(targets = "shadersmod/client/ShadersRender", remap = false)
public abstract class MixinShadersRender {
@Inject(method = "renderHand0", at = @At("HEAD"), cancellable = true)
private static void replayModCompat_disableRenderHand0(EntityRenderer er, float partialTicks, int renderPass, CallbackInfo ci) {
if (ForgeHooksClient.renderFirstPersonHand(er.mc.renderGlobal, partialTicks, renderPass)) {
ci.cancel();
}
}
}

View File

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

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

View File

@@ -6,7 +6,6 @@ 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.replaystudio.util.I18n;
import de.johni0702.minecraft.gui.container.GuiScreen;
import lombok.Getter;
@@ -14,10 +13,11 @@ 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.*;
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.FMLCommonHandler;
import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.Mod.EventHandler;
@@ -31,12 +31,9 @@ import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Queue;
@Mod(modid = ReplayMod.MOD_ID,
useMetadata = true,
version = "@MOD_VERSION@",
acceptedMinecraftVersions = "@MC_VERSION@",
updateJSON = "https://raw.githubusercontent.com/ReplayMod/ReplayMod/master/versions.json",
guiFactory = "com.replaymod.core.gui.GuiFactory")
public class ReplayMod {
@@ -68,8 +65,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();
@@ -112,7 +107,7 @@ public class ReplayMod {
new MainMenuHandler().register();
FMLCommonHandler.instance().bus().register(keyBindingRegistry);
MinecraftForge.EVENT_BUS.register(keyBindingRegistry);
getKeyBindingRegistry().registerKeyBinding("replaymod.input.settings", 0, () -> {
new GuiReplaySettings(null, settingsRegistry).display();
@@ -126,150 +121,7 @@ public class ReplayMod {
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
@@ -306,10 +158,8 @@ public class ReplayMod {
}
public void runLater(Runnable runnable) {
@SuppressWarnings("unchecked")
Queue<ListenableFutureTask> tasks = mc.scheduledTasks;
synchronized (mc.scheduledTasks) {
tasks.add(ListenableFutureTask.create(runnable, null));
mc.scheduledTasks.add(ListenableFutureTask.create(runnable, null));
}
}
@@ -338,13 +188,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

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

@@ -6,8 +6,6 @@ import net.minecraftforge.client.event.GuiScreenEvent;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import java.util.List;
/**
* Moves certain buttons on the main menu upwards so we can inject our own.
*/
@@ -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

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

View File

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

View File

@@ -1,41 +1,17 @@
package com.replaymod.core.utils;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import de.johni0702.minecraft.gui.GuiRenderer;
import de.johni0702.minecraft.gui.RenderInfo;
import de.johni0702.minecraft.gui.container.AbstractGuiScrollable;
import de.johni0702.minecraft.gui.container.GuiContainer;
import de.johni0702.minecraft.gui.container.GuiPanel;
import de.johni0702.minecraft.gui.container.GuiScrollable;
import de.johni0702.minecraft.gui.element.GuiButton;
import de.johni0702.minecraft.gui.element.GuiElement;
import de.johni0702.minecraft.gui.element.GuiLabel;
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
import de.johni0702.minecraft.gui.layout.VerticalLayout;
import de.johni0702.minecraft.gui.popup.GuiInfoPopup;
import de.johni0702.minecraft.gui.utils.Colors;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.Minecraft;
import net.minecraft.client.network.NetworkPlayerInfo;
import net.minecraft.client.resources.DefaultPlayerSkin;
import net.minecraft.crash.CrashReport;
import net.minecraft.util.ResourceLocation;
import org.apache.logging.log4j.Logger;
import org.lwjgl.input.Keyboard;
import org.lwjgl.util.Dimension;
import org.lwjgl.util.ReadableDimension;
import javax.annotation.Nullable;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.UUID;
import static net.minecraft.client.Minecraft.getMinecraft;
public class Utils {
public static final BufferedImage DEFAULT_THUMBNAIL;
@@ -87,7 +63,7 @@ public class Utils {
}
public static ResourceLocation getResourceLocationForPlayerUUID(UUID uuid) {
NetworkPlayerInfo info = getMinecraft().getNetHandler().getPlayerInfo(uuid);
NetworkPlayerInfo info = Minecraft.getMinecraft().getConnection().getPlayerInfo(uuid);
ResourceLocation skinLocation;
if (info != null && info.hasLocationSkin()) {
skinLocation = info.getLocationSkin();
@@ -100,80 +76,4 @@ public class Utils {
public static boolean isCtrlDown() {
return Keyboard.isKeyDown(Keyboard.KEY_LCONTROL) || Keyboard.isKeyDown(Keyboard.KEY_RCONTROL);
}
public static GuiInfoPopup error(Logger logger, GuiContainer container, CrashReport crashReport, Runnable onClose) {
// Convert crash report to string
String crashReportStr = crashReport.getCompleteReport();
// Log via logger
logger.error(crashReportStr);
// Try to save the crash report
if (crashReport.getFile() == null) {
try {
File folder = new File(getMinecraft().mcDataDir, "crash-reports");
File file = new File(folder, "crash-" + (new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss")).format(new Date()) + "-client.txt");
logger.debug("Saving crash report to file: {}", file);
crashReport.saveToFile(file);
} catch (Throwable t) {
logger.error("Saving crash report file:", t);
}
} else {
logger.debug("Not saving crash report as file already exists: {}", crashReport.getFile());
}
logger.trace("Opening crash report popup GUI");
GuiCrashReportPopup popup = new GuiCrashReportPopup(container, crashReportStr);
Futures.addCallback(popup.getFuture(), new FutureCallback<Void>() {
@Override
public void onSuccess(@Nullable Void result) {
logger.trace("Crash report popup closed");
if (onClose != null) {
onClose.run();
}
}
@Override
public void onFailure(Throwable t) {
logger.error("During error popup:", t);
}
});
return popup;
}
private static class GuiCrashReportPopup extends GuiInfoPopup {
private final GuiScrollable scrollable;
public GuiCrashReportPopup(GuiContainer container, String crashReport) {
super(container);
setBackgroundColor(Colors.DARK_TRANSPARENT);
// Add crash report to scrollable info
getInfo().addElements(new VerticalLayout.Data(0.5),
new GuiLabel().setColor(Colors.BLACK).setI18nText("replaymod.gui.unknownerror"),
scrollable = new GuiScrollable().setScrollDirection(AbstractGuiScrollable.Direction.VERTICAL)
.setLayout(new VerticalLayout().setSpacing(2))
.addElements(null,Arrays.stream(crashReport.replace("\t", " ").split("\n")).map(
l -> new GuiLabel().setText(l).setColor(Colors.BLACK)).toArray(GuiElement[]::new)));
// Replace close button with panel containing close and copy buttons
GuiButton copyToClipboardButton = new GuiButton().setI18nLabel("chat.copy").onClick(() ->
GuiScreen.setClipboardString(crashReport)).setSize(150, 20);
GuiButton closeButton = getCloseButton();
popup.removeElement(closeButton);
popup.addElements(new VerticalLayout.Data(1),
new GuiPanel().setLayout(new HorizontalLayout().setSpacing(5)).setSize(305, 20)
.addElements(null, copyToClipboardButton, closeButton));
// And open it
open();
}
@Override
public void draw(GuiRenderer renderer, ReadableDimension size, RenderInfo renderInfo) {
// Re-size the scrollable containing the crash report to 3/4 of the screen
scrollable.setSize(size.getWidth() * 3 / 4, size.getHeight() * 3 / 4);
super.draw(renderer, size, renderInfo);
}
}
}

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

@@ -1,41 +0,0 @@
package com.replaymod.editor;
import com.replaymod.core.ReplayMod;
import com.replaymod.editor.handler.GuiHandler;
import com.replaymod.online.Setting;
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,
version = "@MOD_VERSION@",
acceptedMinecraftVersions = "@MC_VERSION@",
useMetadata = true)
public class ReplayModEditor {
public static final String MOD_ID = "replaymod-editor";
@Mod.Instance(MOD_ID)
public static ReplayModEditor instance;
private ReplayMod core;
public static Logger LOGGER;
@Mod.EventHandler
public void preInit(FMLPreInitializationEvent event) {
ReplayModEditor.LOGGER = event.getModLog();
core = ReplayMod.instance;
core.getSettingsRegistry().register(Setting.class);
}
@Mod.EventHandler
public void init(FMLInitializationEvent event) {
new GuiHandler(this).register();
}
public ReplayMod getCore() {
return core;
}
}

View File

@@ -1,360 +0,0 @@
package com.replaymod.editor.gui;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.gson.JsonObject;
import com.replaymod.core.ReplayMod;
import com.replaymod.core.utils.Utils;
import com.replaymod.replaystudio.PacketData;
import com.replaymod.replaystudio.Studio;
import com.replaymod.replaystudio.filter.StreamFilter;
import com.replaymod.replaystudio.io.ReplayInputStream;
import com.replaymod.replaystudio.io.ReplayOutputStream;
import com.replaymod.replaystudio.replay.ReplayFile;
import com.replaymod.replaystudio.replay.ReplayMetaData;
import com.replaymod.replaystudio.replay.ZipReplayFile;
import com.replaymod.replaystudio.stream.PacketStream;
import com.replaymod.replaystudio.studio.ReplayStudio;
import de.johni0702.minecraft.gui.GuiRenderer;
import de.johni0702.minecraft.gui.RenderInfo;
import de.johni0702.minecraft.gui.container.GuiContainer;
import de.johni0702.minecraft.gui.container.GuiPanel;
import de.johni0702.minecraft.gui.container.GuiScreen;
import de.johni0702.minecraft.gui.element.GuiButton;
import de.johni0702.minecraft.gui.element.GuiLabel;
import de.johni0702.minecraft.gui.element.GuiTextField;
import de.johni0702.minecraft.gui.element.GuiTooltip;
import de.johni0702.minecraft.gui.element.advanced.GuiProgressBar;
import de.johni0702.minecraft.gui.layout.CustomLayout;
import de.johni0702.minecraft.gui.layout.GridLayout;
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.popup.GuiInfoPopup;
import de.johni0702.minecraft.gui.popup.GuiYesNoPopup;
import de.johni0702.minecraft.gui.utils.Colors;
import net.minecraft.crash.CrashReport;
import org.apache.commons.io.FilenameUtils;
import org.lwjgl.util.ReadableDimension;
import javax.annotation.Nullable;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Supplier;
import static com.replaymod.editor.ReplayModEditor.LOGGER;
public class GuiReplayEditor extends GuiScreen {
private final ReplayMod mod;
private volatile ProcessingStage processingStage;
private volatile double progress;
private GuiProcessingProgressPopup popup;
public GuiButton currentTabButton;
public GuiPanel currentTabPanel;
public final GuiPanel tabButtons = new GuiPanel(this).setLayout(new GridLayout().setSpacingX(5));
public final List<GuiPanel> tabPanels = new ArrayList<>();
public final GuiButton saveButton = new GuiButton().setI18nLabel("replaymod.gui.save").setSize(100, 20);
public final GuiButton backButton = new GuiButton().setI18nLabel("replaymod.gui.back").setSize(100, 20);
public final GuiPanel buttonPanel = new GuiPanel(this).setLayout(new HorizontalLayout().setSpacing(5))
.addElements(null, saveButton, backButton);
public GuiReplayEditor(GuiScreen parent, ReplayMod mod) {
this.mod = mod;
backButton.onClick(parent::display);
setTitle(new GuiLabel().setI18nText("replaymod.gui.replayeditor"));
makeTab("trim", () -> new GuiTrimPanel(this));
makeTab("connect", GuiPanel::new).setDisabled(); // Not yet implemented
makeTab("modify", GuiPanel::new).setDisabled(); // Not yet implemented
setLayout(new CustomLayout<GuiScreen>() {
@Override
protected void layout(GuiScreen container, int width, int height) {
// Move all inactive panels aside
tabPanels.forEach(e -> pos(e, Integer.MIN_VALUE, Integer.MIN_VALUE));
pos(buttonPanel, width - 10 - width(buttonPanel), height - 10 - height(buttonPanel));
if (currentTabPanel != null) {
pos(currentTabPanel, 10, 50);
size(currentTabPanel, width - 20, y(buttonPanel) - 10 - y(currentTabPanel));
}
pos(tabButtons, 10, 20);
size(tabButtons, width - 20, 20);
}
});
}
public GuiButton makeTab(String name, Supplier<GuiPanel> panel) {
GuiButton button = new GuiButton().setI18nLabel("replaymod.gui.editor." + name + ".title")
.setTooltip(new GuiTooltip().setI18nText("replaymod.gui.editor." + name + ".description"))
.setSize(Integer.MAX_VALUE, Integer.MAX_VALUE);
button.onClick(() -> {
// Remove previous tab
if (currentTabButton != null) {
currentTabButton.setEnabled();
removeElement(currentTabPanel);
}
// Activate new tab
currentTabButton = button.setDisabled();
addElements(null, currentTabPanel = panel.get());
});
// Add new tab button to tabs panel
tabButtons.addElements(null, button);
((GridLayout) tabButtons.getLayout()).setColumns(tabButtons.getChildren().size());
return button;
}
public void save(File inputFile, PacketStream.FilterInfo...filters) {
save(FilenameUtils.getBaseName(inputFile.getName()), (outputFile) -> {
Studio studio = new ReplayStudio();
try (ReplayFile outputReplay = new ZipReplayFile(studio, inputFile, outputFile);
ReplayOutputStream out = outputReplay.writePacketData()) {
// The input replay file MUST be closed before saving the output file
try (ReplayFile inputReplay = new ZipReplayFile(studio, inputFile);
ReplayInputStream in = inputReplay.getPacketData()) {
ReplayMetaData metaData = inputReplay.getMetaData();
PacketStream stream = studio.createReplayStream(in, true);
stream.addFilter(new ProgressFilter(metaData.getDuration()));
for (PacketStream.FilterInfo info : filters) {
stream.addFilter(info.getFilter(), info.getFrom(), info.getTo());
LOGGER.debug("Added filter {}", info);
}
LOGGER.info("Built pipeline: {}", stream);
stream.start();
long lastTimestamp = 0;
PacketData data;
while ((data = stream.next()) != null) {
out.write(data);
lastTimestamp = data.getTime();
}
for (PacketData d : stream.end()) {
out.write(d);
lastTimestamp = d.getTime();
}
updateProgress(ProcessingStage.FINALIZING, 0);
// Update duration of new replay
metaData.setDuration((int) lastTimestamp);
outputReplay.writeMetaData(metaData);
}
out.close();
outputReplay.save();
} catch (IOException e) {
throw new RuntimeException(e);
}
});
}
public void save(String sourceName, Consumer<File> runnable) {
GuiTextField nameField = new GuiTextField().setSize(200, 20).setFocused(true).setText(sourceName);
GuiYesNoPopup popup = GuiYesNoPopup.open(this,
nameField
).setYesI18nLabel("replaymod.gui.editor.savemode.override").setNoI18nLabel("replaymod.gui.cancel");
nameField.onEnter(() -> {
if (popup.getYesButton().isEnabled()) {
popup.getYesButton().onClick();
}
}).onTextChanged(obj -> {
popup.getYesButton().setEnabled(!nameField.getText().isEmpty());
popup.setYesI18nLabel("replaymod.gui.editor.savemode." +
(nameField.getText().equals(sourceName) ? "override" : "newfile"));
});
Futures.addCallback(popup.getFuture(), new FutureCallback<Boolean>() {
@Override
public void onSuccess(@Nullable Boolean result) {
if (result == Boolean.TRUE) {
// Sanitize their input
String name = nameField.getText().trim().replace("[^a-zA-Z0-9\\.\\- ]", "_");
// This file is what they want
File replayFolder;
try {
replayFolder = mod.getReplayFolder();
} catch (IOException e) {
Utils.error(LOGGER, GuiReplayEditor.this, CrashReport.makeCrashReport(e, "Getting replay folder"), null);
return;
}
File targetFile = new File(replayFolder, name + ".mcpr");
if (targetFile.exists()) {
LOGGER.trace("Selected file already exists, asking for confirmation");
Futures.addCallback(GuiYesNoPopup.open(GuiReplayEditor.this,
new GuiLabel().setColor(Colors.BLACK)
.setI18nText("replaymod.gui.replaymodified.warning1", name),
new GuiLabel().setColor(Colors.BLACK)
.setI18nText("replaymod.gui.replaymodified.warning2"))
.setYesI18nLabel("gui.yes").setNoI18nLabel("gui.no")
.getFuture(), new FutureCallback<Boolean>() {
@Override
public void onSuccess(@Nullable Boolean result) {
if (result == Boolean.TRUE) {
LOGGER.trace("Saving output in {}", targetFile);
runEdit(() -> runnable.accept(targetFile));
} else {
LOGGER.trace("Not overwriting file {}, reopening naming dialog", targetFile);
save(sourceName, runnable);
}
}
@Override
public void onFailure(Throwable t) {
LOGGER.error("During save file confirmation dialog:", t);
}
});
} else {
LOGGER.trace("Saving output in {}", targetFile);
runEdit(() -> runnable.accept(targetFile));
}
} else {
LOGGER.trace("Saving cancelled");
}
}
@Override
public void onFailure(Throwable t) {
LOGGER.error("During save file dialog:", t);
}
});
}
public void runEdit(Runnable runnable) {
popup = new GuiProcessingProgressPopup(this);
processingStage = ProcessingStage.INITIALIZING;
progress = 0;
new Thread(() -> {
LOGGER.info("Starting editing of replay");
try {
runnable.run();
} catch (Throwable t) {
CrashReport crashReport = CrashReport.makeCrashReport(t, "Editing replay file");
mod.runLater(() -> Utils.error(LOGGER, this, crashReport, popup::close));
return;
}
LOGGER.info("Successfully finished editing");
mod.runLater(() -> {
popup.close();
GuiInfoPopup.open(this, "replaymod.gui.editor.progress.status.finished");
});
}).start();
}
public void updateProgress(ProcessingStage stage, double progress) {
if (this.processingStage != stage) {
LOGGER.trace("Changing state from {} to {}", this.processingStage, stage);
}
this.processingStage = stage;
this.progress = progress;
}
public ReplayMod getMod() {
return mod;
}
public enum ProcessingStage {
INITIALIZING("initializing", 0.01),
WORKING("writing.raw", 0.89),
FINALIZING("writing.final", 0.1),
DONE("finished", 0);
private final String i18nKey;
private final double progressFraction;
ProcessingStage(String i18nKey, double progressFraction) {
this.i18nKey = "replaymod.gui.editor.progress.status." + i18nKey;
this.progressFraction = progressFraction;
}
public double getOverallProgress(double progress) {
progress *= progressFraction;
for (ProcessingStage processingStage : values()) {
if (processingStage == this) break;
progress += processingStage.progressFraction;
}
return progress;
}
}
private class ProgressFilter implements StreamFilter {
private final long total;
public ProgressFilter(long total) {
this.total = total;
}
@Override
public String getName() {
return "progress";
}
@Override
public void init(Studio studio, JsonObject config) {
}
@Override
public void onStart(PacketStream stream) {
updateProgress(ProcessingStage.WORKING, 0);
}
@Override
public boolean onPacket(PacketStream stream, PacketData data) {
updateProgress(ProcessingStage.WORKING, (double) data.getTime() / total);
return true;
}
@Override
public void onEnd(PacketStream stream, long timestamp) {}
}
private class GuiProcessingProgressPopup extends AbstractGuiPopup<GuiProcessingProgressPopup> {
private final GuiProgressBar progressBar = new GuiProgressBar().setSize(300, 20);
{
popup.setLayout(new VerticalLayout().setSpacing(10));
popup.addElements(new VerticalLayout.Data(0.5),
new GuiLabel().setI18nText("replaymod.gui.editor.progress.title").setColor(Colors.BLACK),
new GuiLabel().setI18nText("replaymod.gui.editor.progress.pleasewait").setColor(Colors.BLACK),
progressBar);
}
public GuiProcessingProgressPopup(GuiContainer container) {
super(container);
open();
}
@Override
public void draw(GuiRenderer renderer, ReadableDimension size, RenderInfo renderInfo) {
// Update progress bar
progressBar.setI18nLabel(processingStage.i18nKey);
progressBar.setProgress((float) processingStage.getOverallProgress(progress));
super.draw(renderer, size, renderInfo);
}
@Override
public void close() {
super.close();
}
@Override
protected GuiProcessingProgressPopup getThis() {
return this;
}
}
}

View File

@@ -1,198 +0,0 @@
package com.replaymod.editor.gui;
import com.google.gson.JsonObject;
import com.replaymod.core.utils.Utils;
import com.replaymod.replaystudio.data.Marker;
import com.replaymod.replaystudio.filter.ChangeTimestampFilter;
import com.replaymod.replaystudio.filter.RemoveFilter;
import com.replaymod.replaystudio.filter.SquashFilter;
import com.replaymod.replaystudio.replay.ReplayFile;
import com.replaymod.replaystudio.replay.ReplayMetaData;
import com.replaymod.replaystudio.replay.ZipReplayFile;
import com.replaymod.replaystudio.stream.PacketStream;
import com.replaymod.replaystudio.studio.ReplayStudio;
import de.johni0702.minecraft.gui.container.GuiPanel;
import de.johni0702.minecraft.gui.element.GuiLabel;
import de.johni0702.minecraft.gui.element.GuiNumberField;
import de.johni0702.minecraft.gui.element.advanced.GuiDropdownMenu;
import de.johni0702.minecraft.gui.layout.GridLayout;
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
import de.johni0702.minecraft.gui.layout.VerticalLayout;
import net.minecraft.client.resources.I18n;
import net.minecraft.crash.CrashReport;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOCase;
import org.apache.commons.io.filefilter.SuffixFileFilter;
import org.lwjgl.util.Dimension;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import static com.replaymod.editor.ReplayModEditor.LOGGER;
import static java.util.Optional.ofNullable;
public class GuiTrimPanel extends GuiPanel {
// Special value indicating no replay files were found
private static final File NO_REPLAY = new File(".");
// Special value for the initial "Select Marker Keyframe" entry
private static final Marker NO_MARKER = new Marker(); static {NO_MARKER.setTime(-1);}
private final GuiReplayEditor gui;
public final GuiDropdownMenu<File> inputReplays = new GuiDropdownMenu<File>(this)
.setMinSize(new Dimension(200, 20)).onSelection(i -> updateSelectedReplay())
.setToString(f -> f == NO_REPLAY ? "" : FilenameUtils.getBaseName(f.getName()));
public final GuiNumberField startHour = new GuiNumberField().setSize(20, 20).setMaxLength(2);
public final GuiNumberField startMin = new GuiNumberField().setSize(20, 20).setMaxLength(2);
public final GuiNumberField startSec = new GuiNumberField().setSize(20, 20).setMaxLength(2);
public final GuiNumberField startMilli = new GuiNumberField().setSize(40, 20).setMaxLength(4);
public final GuiDropdownMenu<Marker> startMarker = new GuiDropdownMenu<>();
public final GuiNumberField endHour = new GuiNumberField().setSize(20, 20).setMaxLength(2);
public final GuiNumberField endMin = new GuiNumberField().setSize(20, 20).setMaxLength(2);
public final GuiNumberField endSec = new GuiNumberField().setSize(20, 20).setMaxLength(2);
public final GuiNumberField endMilli = new GuiNumberField().setSize(40, 20).setMaxLength(4);
public final GuiDropdownMenu<Marker> endMarker = new GuiDropdownMenu<>();
public final GuiPanel timePanel = new GuiPanel(this)
.setLayout(new GridLayout().setCellsEqualSize(false).setSpacingX(20).setSpacingY(5).setColumns(3))
.addElements(new GridLayout.Data(0, 0.5),
new GuiLabel().setI18nText("replaymod.gui.start"),
new GuiPanel().setLayout(new HorizontalLayout().setSpacing(2)).addElements(
new HorizontalLayout.Data(0.5),
startHour, new GuiLabel().setI18nText("replaymod.gui.hours"),
startMin, new GuiLabel().setI18nText("replaymod.gui.minutes"),
startSec, new GuiLabel().setI18nText("replaymod.gui.seconds"),
startMilli, new GuiLabel().setI18nText("replaymod.gui.milliseconds")
), startMarker,
new GuiLabel().setI18nText("replaymod.gui.end"),
new GuiPanel().setLayout(new HorizontalLayout().setSpacing(2)).addElements(
new HorizontalLayout.Data(0.5),
endHour, new GuiLabel().setI18nText("replaymod.gui.hours"),
endMin, new GuiLabel().setI18nText("replaymod.gui.minutes"),
endSec, new GuiLabel().setI18nText("replaymod.gui.seconds"),
endMilli, new GuiLabel().setI18nText("replaymod.gui.milliseconds")
), endMarker);
public GuiTrimPanel(GuiReplayEditor gui) {
this.gui = gui;
setLayout(new VerticalLayout().setSpacing(10));
Function<Marker, String> toString = m -> {
if (m == NO_MARKER) {
return I18n.format("replaymod.gui.editor.trim.marker");
} else {
return ofNullable(m.getName()).orElse(I18n.format("replaymod.gui.ingame.unnamedmarker"))
+ " (" + Utils.convertSecondsToShortString(m.getTime() / 1000) + ")";
}
};
startMarker.setToString(toString).setMinSize(new Dimension(100, 20)).onSelection(i ->
onSelectedMarkerChanged(startMarker, startHour, startMin, startSec, startMilli));
endMarker.setToString(toString).setMinSize(new Dimension(100, 20)).onSelection(i ->
onSelectedMarkerChanged(endMarker, endHour, endMin, endSec, endMilli));
File[] files = null;
try {
File folder = gui.getMod().getReplayFolder();
files = folder.listFiles((FileFilter) new SuffixFileFilter(".mcpr", IOCase.INSENSITIVE));
} catch (IOException e) {
Utils.error(LOGGER, gui, CrashReport.makeCrashReport(e, "Listing available replays"), gui.backButton::onClick);
}
if (files == null) {
LOGGER.warn("Replay file list is null, has the replay folder been deleted?");
files = new File[0];
} else {
LOGGER.debug("Found {} replays in the replay folder", files.length);
}
if (files.length == 0) {
inputReplays.setValues(NO_REPLAY);
} else {
inputReplays.setValues(files);
}
updateSelectedReplay();
updateReadyState();
gui.saveButton.onClick(() -> {
// Read start and end times
int start = ((startHour.getInteger() * 60 + startMin.getInteger()) * 60 + startSec.getInteger()) * 1000 + startMilli.getInteger();
int end = ((endHour.getInteger() * 60 + endMin.getInteger()) * 60 + endSec.getInteger()) * 1000 + endMilli.getInteger();
// Configure offset of change timestamp filter
ChangeTimestampFilter ctf = new ChangeTimestampFilter();
JsonObject config = new JsonObject();
config.addProperty("offset", -start);
ctf.init(null, config);
// Pass filters to save dialog
gui.save(inputReplays.getSelectedValue(),
new PacketStream.FilterInfo(new SquashFilter(), -1, start),
new PacketStream.FilterInfo(ctf, start, end),
new PacketStream.FilterInfo(new RemoveFilter(), end, -1)
);
});
}
private void onSelectedMarkerChanged(GuiDropdownMenu<Marker> dropdown, GuiNumberField hour, GuiNumberField min,
GuiNumberField sec, GuiNumberField milli) {
Marker marker = dropdown.getSelectedValue();
if (marker != NO_MARKER) {
int time = marker.getTime();
setTime(time, hour, min, sec, milli);
}
}
private void setTime(int time, GuiNumberField hour, GuiNumberField min,
GuiNumberField sec, GuiNumberField milli) {
milli.setValue(time % 1000); time /= 1000;
sec.setValue(time % 60); time /= 60;
min.setValue(time % 60); time /= 60;
hour.setValue(time);
}
private void updateSelectedReplay() {
File file = inputReplays.getSelectedValue();
// Load markers and meta data from replay file
ReplayMetaData metaData;
Set<Marker> markers;
if (file == NO_REPLAY) {
metaData = new ReplayMetaData();
markers = Collections.emptySet();
} else {
try (ReplayFile replayFile = new ZipReplayFile(new ReplayStudio(), file)) {
metaData = replayFile.getMetaData();
markers = replayFile.getMarkers().or(Collections.emptySet());
LOGGER.debug("Loaded {} markers from replay {}", markers.size(), file);
} catch (Exception e) {
Utils.error(LOGGER, gui, CrashReport.makeCrashReport(e, "Reading markers from replay"), null);
metaData = new ReplayMetaData();
markers = Collections.emptySet();
}
}
// Sort and add initial "Select Event Marker" element
List<Marker> markerList = new ArrayList<>(markers);
markerList.sort(Comparator.comparing(Marker::getTime));
markerList.add(0, NO_MARKER);
// Set marker dropdown values
Marker[] markerArray = markerList.stream().toArray(Marker[]::new);
startMarker.setValues(markerArray).setSelected(0);
endMarker.setValues(markerArray).setSelected(0);
// Set start and end time values
setTime(0, startHour, startMin, startSec, startMilli);
setTime(metaData.getDuration(), endHour, endMin, endSec, endMilli);
}
private void updateReadyState() {
gui.saveButton.setEnabled(inputReplays.getSelectedValue() != NO_REPLAY);
}
}

View File

@@ -1,54 +0,0 @@
package com.replaymod.editor.handler;
import com.replaymod.editor.ReplayModEditor;
import com.replaymod.editor.gui.GuiReplayEditor;
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 net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import java.util.List;
public class GuiHandler {
private static final int BUTTON_REPLAY_EDITOR = 17890237;
private final ReplayModEditor mod;
public GuiHandler(ReplayModEditor mod) {
this.mod = mod;
}
public void register() {
FMLCommonHandler.instance().bus().register(this);
MinecraftForge.EVENT_BUS.register(this);
}
@SubscribeEvent
public void injectIntoMainMenu(GuiScreenEvent.InitGuiEvent event) {
if (!(event.gui 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"));
button.width = button.width / 2 - 2;
buttonList.add(button);
}
@SubscribeEvent
public void onButton(GuiScreenEvent.ActionPerformedEvent.Pre event) {
if(!event.button.enabled) return;
if (event.gui instanceof GuiMainMenu) {
if (event.button.id == BUTTON_REPLAY_EDITOR) {
new GuiReplayEditor(GuiScreen.wrap(event.gui), mod.getCore()).display();
}
}
}
}

View File

@@ -9,13 +9,15 @@ 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.fml.common.FMLCommonHandler;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
import org.lwjgl.input.Keyboard;
public class FullBrightness implements Extra {
private ReplayModReplay module;
@Mod.Instance(ReplayModReplay.MOD_ID)
private static ReplayModReplay module;
private final IGuiImage indicator = new GuiImage().setTexture(ReplayMod.TEXTURE, 90, 20, 19, 13).setSize(19, 13);
@@ -25,7 +27,6 @@ public class FullBrightness implements Extra {
@Override
public void register(final ReplayMod mod) throws Exception {
this.module = ReplayModReplay.instance;
this.gameSettings = mod.getMinecraft().gameSettings;
mod.getKeyBindingRegistry().registerKeyBinding("replaymod.input.lighting", Keyboard.KEY_Z, new Runnable() {
@@ -40,7 +41,7 @@ public class FullBrightness implements Extra {
}
});
FMLCommonHandler.instance().bus().register(this);
MinecraftForge.EVENT_BUS.register(this);
}
@SubscribeEvent

View File

@@ -17,7 +17,7 @@ import de.johni0702.minecraft.gui.layout.GridLayout;
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
import de.johni0702.minecraft.gui.layout.LayoutData;
import net.minecraft.client.resources.I18n;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import org.lwjgl.input.Keyboard;
import org.lwjgl.util.Dimension;
@@ -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

@@ -10,8 +10,9 @@ import lombok.RequiredArgsConstructor;
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 net.minecraftforge.fml.common.Mod;
import org.apache.commons.lang3.StringEscapeUtils;
import java.awt.image.BufferedImage;
@@ -25,12 +26,11 @@ import java.util.Map;
import java.util.Set;
public class LocalizationExtra implements Extra {
private ReplayModOnline module;
@Mod.Instance(ReplayModOnline.MOD_ID)
private static ReplayModOnline module;
@Override
public void register(ReplayMod mod) throws Exception {
this.module = ReplayModOnline.instance;
final Minecraft mc = mod.getMinecraft();
Thread localizedResourcePackLoader = new Thread(new Runnable() {
@Override
@@ -95,12 +95,12 @@ public class LocalizationExtra implements Extra {
}
@Override
public Set getResourceDomains() {
public Set<String> getResourceDomains() {
return ImmutableSet.of("minecraft", "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

@@ -12,16 +12,16 @@ import org.apache.logging.log4j.Logger;
import java.util.Arrays;
import java.util.List;
@Mod(modid = ReplayModExtras.MOD_ID,
version = "@MOD_VERSION@",
acceptedMinecraftVersions = "@MC_VERSION@",
useMetadata = true)
@Mod(modid = ReplayModExtras.MOD_ID, useMetadata = true)
public class ReplayModExtras {
public static final String MOD_ID = "replaymod-extras";
@Mod.Instance(MOD_ID)
public static ReplayModExtras instance;
@Mod.Instance(ReplayMod.MOD_ID)
private static ReplayMod core;
private static final List<Class<? extends Extra>> builtin = Arrays.asList(
PlayerOverview.class,
UriSchemeExtra.class,
@@ -44,7 +44,7 @@ public class ReplayModExtras {
for (Class<? extends Extra> cls : builtin) {
try {
Extra extra = cls.newInstance();
extra.register(ReplayMod.instance);
extra.register(core);
} catch (Throwable t) {
logger.warn("Failed to load extra " + cls.getName() + ": ", t);
}

View File

@@ -12,7 +12,8 @@ import com.replaymod.replay.events.ReplayOpenEvent;
import net.minecraft.client.renderer.entity.RenderManager;
import net.minecraft.client.renderer.entity.RenderPlayer;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import org.lwjgl.input.Keyboard;
@@ -20,15 +21,14 @@ import java.io.IOException;
import java.util.*;
public class PlayerOverview implements Extra {
private ReplayModReplay module;
@Mod.Instance(ReplayModReplay.MOD_ID)
private static ReplayModReplay module;
private final Set<UUID> hiddenPlayers = new HashSet<>();
private boolean savingEnabled;
@Override
public void register(final ReplayMod mod) throws Exception {
this.module = ReplayModReplay.instance;
mod.getKeyBindingRegistry().registerKeyBinding("replaymod.input.playeroverview", Keyboard.KEY_B, new Runnable() {
@Override
public void run() {
@@ -55,7 +55,7 @@ public class PlayerOverview implements Extra {
}
});
FMLCommonHandler.instance().bus().register(this);
MinecraftForge.EVENT_BUS.register(this);
RenderManager renderManager = mod.getMinecraft().getRenderManager();
@SuppressWarnings("unchecked")

View File

@@ -16,7 +16,8 @@ import de.johni0702.minecraft.gui.utils.Colors;
import net.minecraft.client.audio.PositionedSoundRecord;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EnumPlayerModelParts;
import net.minecraft.potion.Potion;
import net.minecraft.init.MobEffects;
import net.minecraft.init.SoundEvents;
import net.minecraft.util.ResourceLocation;
import org.lwjgl.util.Dimension;
import org.lwjgl.util.ReadableDimension;
@@ -40,14 +41,14 @@ public class PlayerOverviewGui extends GuiScreen implements Closeable {
public final GuiCheckbox checkAll = new GuiCheckbox(contentPanel){
@Override
public void onClick() {
getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.create(BUTTON_SOUND, 1.0F));
getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(SoundEvents.UI_BUTTON_CLICK, 1.0F));
playersScrollable.forEach(IGuiCheckbox.class).setChecked(true);
}
}.setLabel("").setChecked(true);
public final GuiCheckbox uncheckAll = new GuiCheckbox(contentPanel){
@Override
public void onClick() {
getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.create(BUTTON_SOUND, 1.0F));
getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(SoundEvents.UI_BUTTON_CLICK, 1.0F));
playersScrollable.forEach(IGuiCheckbox.class).setChecked(false);
}
}.setLabel("").setChecked(false);
@@ -90,7 +91,7 @@ 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);
if (p.func_175148_a(EnumPlayerModelParts.HAT)) {
if (p.isWearing(EnumPlayerModelParts.HAT)) {
renderer.drawTexturedRect(0, 0, 40, 8, size.getWidth(), size.getHeight(), 8, 8, 64, 64);
}
}
@@ -139,7 +140,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> {

View File

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

View File

@@ -4,6 +4,7 @@ import com.replaymod.core.ReplayMod;
import com.replaymod.extras.Extra;
import com.replaymod.online.ReplayModOnline;
import de.johni0702.minecraft.gui.container.GuiScreen;
import net.minecraftforge.fml.common.Mod;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
@@ -12,14 +13,14 @@ import java.net.ServerSocket;
import java.net.Socket;
public class UriSchemeExtra implements Extra {
private ReplayModOnline module;
@Mod.Instance(ReplayModOnline.MOD_ID)
private static ReplayModOnline module;
private ReplayMod mod;
@Override
public void register(final ReplayMod mod) throws Exception {
this.mod = mod;
this.module = ReplayModOnline.instance;
UriScheme uriScheme = UriScheme.create();
if (uriScheme == null) {

View File

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

@@ -12,8 +12,8 @@ import com.replaymod.replaystudio.replay.ReplayFile;
import com.replaymod.replaystudio.replay.ZipReplayFile;
import com.replaymod.replaystudio.studio.ReplayStudio;
import de.johni0702.minecraft.gui.container.GuiScreen;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPostInitializationEvent;
@@ -26,19 +26,18 @@ import java.io.IOException;
import static net.minecraft.client.Minecraft.getMinecraft;
@Mod(modid = ReplayModOnline.MOD_ID,
version = "@MOD_VERSION@",
acceptedMinecraftVersions = "@MC_VERSION@",
useMetadata = true)
@Mod(modid = ReplayModOnline.MOD_ID, useMetadata = true)
public class ReplayModOnline {
public static final String MOD_ID = "replaymod-online";
@Mod.Instance(MOD_ID)
public static ReplayModOnline instance;
private ReplayMod core;
@Mod.Instance(ReplayMod.MOD_ID)
private static ReplayMod core;
private ReplayModReplay replayModule;
@Mod.Instance(ReplayModReplay.MOD_ID)
private static ReplayModReplay replayModule;
private Logger logger;
@@ -54,8 +53,6 @@ public class ReplayModOnline {
@Mod.EventHandler
public void preInit(FMLPreInitializationEvent event) {
logger = event.getModLog();
core = ReplayMod.instance;
replayModule = ReplayModReplay.instance;
core.getSettingsRegistry().register(Setting.class);
@@ -67,14 +64,12 @@ public class ReplayModOnline {
@Mod.EventHandler
public void init(FMLInitializationEvent event) {
if (!getDownloadsFolder().exists()){
if (!getDownloadsFolder().mkdirs()) {
logger.warn("Failed to create downloads folder: " + getDownloadsFolder());
}
if (!getDownloadsFolder().mkdirs()) {
logger.warn("Failed to create downloads folder: " + getDownloadsFolder());
}
new GuiHandler(this).register();
FMLCommonHandler.instance().bus().register(this);
MinecraftForge.EVENT_BUS.register(this);
}
@Mod.EventHandler

View File

@@ -2,7 +2,10 @@ 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"),
MC_1_11("Minecraft 1.11", "1.11");
private String niceName, apiName;

View File

@@ -14,11 +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.FMLCommonHandler;
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

@@ -11,7 +11,7 @@ import com.replaymod.replaystudio.pathing.path.Keyframe;
import com.replaymod.replaystudio.pathing.path.Path;
import com.replaymod.replaystudio.pathing.path.Timeline;
import net.minecraft.client.Minecraft;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import javax.annotation.Nullable;
@@ -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

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

@@ -4,7 +4,7 @@ import com.replaymod.core.ReplayMod;
import com.replaymod.core.utils.Restrictions;
import com.replaymod.recording.handler.ConnectionEventHandler;
import com.replaymod.recording.packet.PacketListener;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.common.MinecraftForge;
import net.minecraft.network.NetworkManager;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
@@ -14,17 +14,15 @@ import net.minecraftforge.fml.common.network.NetworkRegistry;
import org.apache.logging.log4j.Logger;
import org.lwjgl.input.Keyboard;
@Mod(modid = ReplayModRecording.MOD_ID,
version = "@MOD_VERSION@",
acceptedMinecraftVersions = "@MC_VERSION@",
useMetadata = true)
@Mod(modid = ReplayModRecording.MOD_ID, useMetadata = true)
public class ReplayModRecording {
public static final String MOD_ID = "replaymod-recording";
@Mod.Instance(MOD_ID)
public static ReplayModRecording instance;
private ReplayMod core;
@Mod.Instance(ReplayMod.MOD_ID)
private static ReplayMod core;
private Logger logger;
@@ -33,7 +31,6 @@ public class ReplayModRecording {
@Mod.EventHandler
public void preInit(FMLPreInitializationEvent event) {
logger = event.getModLog();
core = ReplayMod.instance;
core.getSettingsRegistry().register(Setting.class);
@@ -51,7 +48,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.newSimpleChannel(Restrictions.PLUGIN_CHANNEL);

View File

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

View File

@@ -1,25 +0,0 @@
package com.replaymod.recording.handler;
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
* not actually be handled.
* This handler is only present when connected to the integrated server as
* otherwise all packets must be handled.
*
* When in single player, the game state packets must never be handled
* otherwise wired bugs related to semi-singletons can occur.
* See https://bugs.replaymod.com/show_bug.cgi?id=44
*/
public class FMLHandshakeFilter extends SimpleChannelInboundHandler<FMLHandshakeMessage> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, FMLHandshakeMessage msg) throws Exception {
if (!(msg instanceof FMLHandshakeMessage.RegistryData)) {
// Pass on everything but RegistryData messages
ctx.fireChannelRead(msg);
}
}
}

View File

@@ -1,24 +1,22 @@
package com.replaymod.recording.handler;
import com.replaymod.recording.packet.PacketListener;
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.item.Item;
import net.minecraft.inventory.EntityEquipmentSlot;
import net.minecraft.item.ItemStack;
import net.minecraft.network.Packet;
import net.minecraft.network.PacketBuffer;
import net.minecraft.network.datasync.EntityDataManager;
import net.minecraft.network.play.server.*;
import net.minecraft.network.play.server.S14PacketEntity.S17PacketEntityLookMove;
import net.minecraft.server.integrated.IntegratedServer;
import net.minecraft.util.BlockPos;
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.FMLCommonHandler;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.PlayerEvent.ItemPickupEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
@@ -32,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) {
@@ -59,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();
}
@@ -67,39 +65,28 @@ 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) {
public void onClientSound(SoundEvent sound, SoundCategory category,
double x, double y, double z, float volume, float pitch) {
try {
S0CPacketSpawnPlayer packet = new S0CPacketSpawnPlayer();
ByteBuf bb = Unpooled.buffer();
PacketBuffer pb = new PacketBuffer(bb);
pb.writeVarIntToBuffer(player.getEntityId());
pb.writeUuid(EntityPlayer.getUUID(player.getGameProfile()));
pb.writeInt(MathHelper.floor_double(player.posX * 32.0D));
pb.writeInt(MathHelper.floor_double(player.posY * 32.0D));
pb.writeInt(MathHelper.floor_double(player.posZ * 32.0D));
pb.writeByte((byte) ((int) (player.rotationYaw * 256.0F / 360.0F)));
pb.writeByte((byte) ((int) (player.rotationPitch * 256.0F / 360.0F)));
ItemStack itemstack = player.inventory.getCurrentItem();
pb.writeShort(itemstack == null ? 0 : Item.getIdFromItem(itemstack.getItem()));
player.getDataWatcher().writeTo(pb);
packet.readPacketData(pb);
return packet;
// 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();
return null;
}
}
@@ -131,19 +118,14 @@ public class RecordingEventHandler {
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.posY * 32.0D);
int z = MathHelper.floor_double(e.player.posZ * 32.0D);
byte yaw = (byte) ((int) (e.player.rotationYaw * 256.0F / 360.0F));
byte pitch = (byte) ((int) (e.player.rotationPitch * 256.0F / 360.0F));
packet = new S18PacketEntityTeleport(e.player.getEntityId(), x, y, z, yaw, pitch, e.player.onGround);
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),
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);
}
@@ -153,37 +135,16 @@ public class RecordingEventHandler {
int rotationYawHead = ((int)(e.player.rotationYawHead * 256.0F / 360.0F));
if(!Objects.equals(rotationYawHead, rotationYawHeadBefore)) {
S19PacketEntityHeadLook head = new S19PacketEntityHeadLook();
ByteBuf bb1 = Unpooled.buffer();
PacketBuffer pb1 = new PacketBuffer(bb1);
pb1.writeVarIntToBuffer(e.player.getEntityId());
pb1.writeByte(rotationYawHead);
head.readPacketData(pb1);
packetListener.save(head);
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) {
S0BPacketAnimation pac = new S0BPacketAnimation();
ByteBuf bb = Unpooled.buffer();
PacketBuffer pb = new PacketBuffer(bb);
pb.writeVarIntToBuffer(e.player.getEntityId());
pb.writeByte(0);
pac.readPacketData(pb);
packetListener.save(pac);
if (e.player.isSwingInProgress && e.player.swingProgressInt == -1) {
packetListener.save(new SPacketAnimation(e.player, e.player.swingingHand == EnumHand.MAIN_HAND ? 0 : 3));
}
/*
@@ -207,77 +168,64 @@ 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.getItemStackFromSlot(EntityEquipmentSlot.FEET)) {
playerItems[2] = mc.thePlayer.getItemStackFromSlot(EntityEquipmentSlot.FEET);
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.getItemStackFromSlot(EntityEquipmentSlot.LEGS)) {
playerItems[3] = mc.thePlayer.getItemStackFromSlot(EntityEquipmentSlot.LEGS);
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.getItemStackFromSlot(EntityEquipmentSlot.CHEST)) {
playerItems[4] = mc.thePlayer.getItemStackFromSlot(EntityEquipmentSlot.CHEST);
packetListener.save(new SPacketEntityEquipment(e.player.getEntityId(), EntityEquipmentSlot.CHEST, playerItems[4]));
}
if (playerItems[5] != mc.thePlayer.getItemStackFromSlot(EntityEquipmentSlot.HEAD)) {
playerItems[5] = mc.thePlayer.getItemStackFromSlot(EntityEquipmentSlot.HEAD);
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) {
S0BPacketAnimation pac = new S0BPacketAnimation();
ByteBuf bb = Unpooled.buffer();
PacketBuffer pb = new PacketBuffer(bb);
pb.writeVarIntToBuffer(e.player.getEntityId());
pb.writeByte(2);
pac.readPacketData(pb);
packetListener.save(pac);
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();
}
@@ -286,27 +234,8 @@ public class RecordingEventHandler {
@SubscribeEvent
public void onPickupItem(ItemPickupEvent event) {
try {
packetListener.save(new S0DPacketCollectItem(event.pickedUp.getEntityId(), event.player.getEntityId()));
} catch(Exception e) {
e.printStackTrace();
}
}
@SubscribeEvent
public void onStartEating(PlayerUseItemEvent.Start event) {
try {
if(!event.entityPlayer.isEating()) return;
S0BPacketAnimation packet = new S0BPacketAnimation();
ByteBuf bb = Unpooled.buffer();
PacketBuffer pb = new PacketBuffer(bb);
pb.writeVarIntToBuffer(event.entityPlayer.getEntityId());
pb.writeByte(3);
packet.readPacketData(pb);
packetListener.save(packet);
packetListener.save(new SPacketCollectItem(event.pickedUp.getEntityId(), event.player.getEntityId(),
event.pickedUp.getEntityItem().getMaxStackSize()));
} catch(Exception e) {
e.printStackTrace();
}
@@ -315,21 +244,11 @@ public class RecordingEventHandler {
@SubscribeEvent
public void onSleep(PlayerSleepInBedEvent event) {
try {
if(event.entityPlayer != mc.thePlayer) {
if (event.getEntityPlayer() != mc.thePlayer) {
return;
}
S0APacketUseBed pub = new S0APacketUseBed();
ByteBuf buf = Unpooled.buffer();
PacketBuffer pbuf = new PacketBuffer(buf);
pbuf.writeVarIntToBuffer(event.entityPlayer.getEntityId());
pbuf.writeBlockPos(event.pos);
pub.readPacketData(pbuf);
packetListener.save(pub);
packetListener.save(new SPacketUseBed(event.getEntityPlayer(), event.getPos()));
wasSleeping = true;
@@ -341,24 +260,13 @@ public class RecordingEventHandler {
@SubscribeEvent
public void enterMinecart(MinecartInteractEvent event) {
try {
if(event.player != mc.thePlayer) {
if(event.getEntity() != mc.thePlayer) {
return;
}
S1BPacketEntityAttach pea = new S1BPacketEntityAttach();
packetListener.save(new SPacketEntityAttach(event.getPlayer(), event.getMinecart()));
ByteBuf buf = Unpooled.buffer();
PacketBuffer pbuf = new PacketBuffer(buf);
pbuf.writeInt(event.player.getEntityId());
pbuf.writeInt(event.minecart.getEntityId());
pbuf.writeBoolean(false);
pea.readPacketData(pbuf);
packetListener.save(pea);
lastRiding = event.minecart.getEntityId();
lastRiding = event.getMinecart().getEntityId();
} catch(Exception e) {
e.printStackTrace();
}
@@ -367,7 +275,7 @@ public class RecordingEventHandler {
public void onBlockBreakAnim(int breakerId, BlockPos pos, int progress) {
EntityPlayer thePlayer = mc.thePlayer;
if (thePlayer != null && breakerId == thePlayer.getEntityId()) {
packetListener.save(new S25PacketBlockBreakAnim(breakerId, pos, progress));
packetListener.save(new SPacketBlockBreakAnim(breakerId, pos, progress));
}
}

View File

@@ -4,15 +4,14 @@ import com.replaymod.recording.handler.RecordingEventHandler;
import net.minecraft.client.Minecraft;
import net.minecraft.client.network.NetHandlerPlayClient;
import net.minecraft.client.network.NetworkPlayerInfo;
import net.minecraft.network.play.server.S07PacketRespawn;
import net.minecraft.network.play.server.S38PacketPlayerListItem;
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.List;
import java.util.Map;
import java.util.UUID;
@@ -37,18 +36,16 @@ public abstract class MixinNetHandlerPlayClient {
* @param ci Callback info
*/
@Inject(method = "handlePlayerListItem", at=@At("HEAD"))
public void recordOwnJoin(S38PacketPlayerListItem packet, CallbackInfo ci) {
public void recordOwnJoin(SPacketPlayerListItem packet, CallbackInfo ci) {
if (gameController.thePlayer == null) return;
RecordingEventHandler handler = getRecordingEventHandler();
if (handler != null && packet.func_179768_b() == S38PacketPlayerListItem.Action.ADD_PLAYER) {
@SuppressWarnings("unchecked")
List<S38PacketPlayerListItem.AddPlayerData> dataList = packet.func_179767_a();
for (S38PacketPlayerListItem.AddPlayerData data : dataList) {
if (data.func_179962_a() == null || data.func_179962_a().getId() == null) continue;
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.func_179962_a().getId().equals(Minecraft.getMinecraft().thePlayer.getGameProfile().getId())
&& !playerInfoMap.containsKey(data.func_179962_a().getId())) {
if (data.getProfile().getId().equals(Minecraft.getMinecraft().thePlayer.getGameProfile().getId())
&& !playerInfoMap.containsKey(data.getProfile().getId())) {
handler.onPlayerJoin();
}
}
@@ -58,12 +55,12 @@ public abstract class MixinNetHandlerPlayClient {
/**
* 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(S38PacketPlayerListItem, 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,7 @@
package com.replaymod.recording.mixin;
import com.replaymod.recording.handler.FMLHandshakeFilter;
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;
@@ -15,32 +11,17 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(value = NetworkDispatcher.class, remap = false)
public abstract class MixinNetworkDispatcher {
@Shadow
private Side side;
@Shadow
private EmbeddedChannel handshakeChannel;
/**
* Always sets fml:isLocal to false on the server side.
* Always sets fml:isLocal to false.
* This effectively removes the difference in the FML handshake between SP and MP
* and forces the block/item ids, etc. to always be send.
* Injects a {@link FMLHandshakeFilter} on the client side to filter out
* those extra, unexpected packets.
* This might have undesired side effects but at least it works at all.
*/
@Inject(method = "insertIntoChannel", at=@At("HEAD"))
public void replayModRecording_setupForLocalRecording(CallbackInfo cb) {
// If we're in multiplayer, everything is fine as is
if (!handshakeChannel.attr(NetworkDispatcher.IS_LOCAL).get()) return;
if (side == Side.SERVER) {
// On the server side, force all packets to be sent
handshakeChannel.attr(NetworkDispatcher.IS_LOCAL).set(false);
} else {
// On the client side, discard additional packets
ChannelPipeline pipeline = handshakeChannel.pipeline();
pipeline.addAfter(pipeline.context(FMLHandshakeCodec.class).name(),
"replaymod_filter", new FMLHandshakeFilter());
}
public void replayModRecording_forceIsLocalToFalse(CallbackInfo cb) {
handshakeChannel.attr(NetworkDispatcher.IS_LOCAL).set(false);
}
}

View File

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

View File

@@ -2,7 +2,7 @@ package com.replaymod.recording.mixin;
import com.replaymod.recording.handler.RecordingEventHandler;
import net.minecraft.client.renderer.RenderGlobal;
import net.minecraft.util.BlockPos;
import net.minecraft.util.math.BlockPos;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;

View File

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

View File

@@ -9,21 +9,24 @@ import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.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.datasync.EntityDataManager;
import net.minecraft.network.play.server.*;
import net.minecraft.util.ChatComponentText;
import net.minecraft.util.text.TextComponentString;
import net.minecraftforge.fml.common.network.internal.FMLProxyPacket;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.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;
@@ -84,8 +87,8 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
public void save(Packet packet) {
try {
if(packet instanceof S0CPacketSpawnPlayer) {
UUID uuid = ((S0CPacketSpawnPlayer) packet).func_179819_c();
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()]));
@@ -151,16 +154,16 @@ 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 S48PacketResourcePackSend) {
save(resourcePackRecorder.handleResourcePack((S48PacketResourcePackSend) packet));
if (packet instanceof SPacketResourcePackSend) {
save(resourcePackRecorder.handleResourcePack((SPacketResourcePackSend) packet));
return;
}
@@ -173,10 +176,10 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
save(packet);
if (packet instanceof S3FPacketCustomPayload) {
S3FPacketCustomPayload p = (S3FPacketCustomPayload) packet;
if (packet instanceof SPacketCustomPayload) {
SPacketCustomPayload p = (SPacketCustomPayload) packet;
if (Restrictions.PLUGIN_CHANNEL.equals(p.getChannelName())) {
packet = new S40PacketDisconnect(new ChatComponentText("Please update to view this replay."));
packet = new SPacketDisconnect(new TextComponentString("Please update to view this replay."));
save(packet);
}
}
@@ -189,27 +192,31 @@ 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);
}
}
}

View File

@@ -15,8 +15,8 @@ import net.minecraft.client.network.NetHandlerPlayClient;
import net.minecraft.client.resources.I18n;
import net.minecraft.client.resources.ResourcePackRepository;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.play.client.C19PacketResourcePackStatus;
import net.minecraft.network.play.server.S48PacketResourcePackSend;
import net.minecraft.network.play.client.CPacketResourcePackStatus;
import net.minecraft.network.play.server.SPacketResourcePackSend;
import net.minecraft.util.HttpUtil;
import org.apache.commons.io.FileUtils;
import org.apache.logging.log4j.LogManager;
@@ -70,12 +70,12 @@ public class ResourcePackRecorder {
}
}
public synchronized S48PacketResourcePackSend handleResourcePack(S48PacketResourcePackSend packet) {
public synchronized SPacketResourcePackSend handleResourcePack(SPacketResourcePackSend packet) {
final int requestId = nextRequestId++;
final NetHandlerPlayClient netHandler = mc.getNetHandler();
final NetHandlerPlayClient netHandler = mc.getConnection();
final NetworkManager netManager = netHandler.getNetworkManager();
final String url = packet.func_179783_a();
final String hash = packet.func_179784_b();
final String url = packet.getURL();
final String hash = packet.getHash();
if (url.startsWith("level://")) {
String levelName = url.substring("level://".length());
@@ -83,60 +83,60 @@ public class ResourcePackRecorder {
final File levelDir = new File(savesDir, levelName);
if (levelDir.isFile()) {
netManager.sendPacket(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.ACCEPTED));
Futures.addCallback(mc.getResourcePackRepository().func_177319_a(levelDir), new FutureCallback() {
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 C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.SUCCESSFULLY_LOADED));
netManager.sendPacket(new CPacketResourcePackStatus(CPacketResourcePackStatus.Action.SUCCESSFULLY_LOADED));
}
@Override
public void onFailure(@Nonnull Throwable throwable) {
netManager.sendPacket(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.FAILED_DOWNLOAD));
netManager.sendPacket(new CPacketResourcePackStatus(CPacketResourcePackStatus.Action.FAILED_DOWNLOAD));
}
});
} else {
netManager.sendPacket(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.FAILED_DOWNLOAD));
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 C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.ACCEPTED));
netManager.sendPacket(new CPacketResourcePackStatus(CPacketResourcePackStatus.Action.ACCEPTED));
downloadResourcePackFuture(requestId, url, hash);
} else if (serverData != null && serverData.getResourceMode() != ServerData.ServerResourceMode.PROMPT) {
netManager.sendPacket(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.DECLINED));
netManager.sendPacket(new CPacketResourcePackStatus(CPacketResourcePackStatus.Action.DECLINED));
} else {
mc.addScheduledTask(() -> mc.displayGuiScreen(new GuiYesNo((result, id) -> {
if (serverData != null) {
serverData.setResourceMode(result ? ServerData.ServerResourceMode.ENABLED : ServerData.ServerResourceMode.DISABLED);
}
if (result) {
netManager.sendPacket(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.ACCEPTED));
netManager.sendPacket(new CPacketResourcePackStatus(CPacketResourcePackStatus.Action.ACCEPTED));
downloadResourcePackFuture(requestId, url, hash);
} else {
netManager.sendPacket(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.DECLINED));
netManager.sendPacket(new CPacketResourcePackStatus(CPacketResourcePackStatus.Action.DECLINED));
}
ServerList.func_147414_b(serverData);
ServerList.saveSingleServer(serverData);
mc.displayGuiScreen(null);
}, I18n.format("multiplayer.texturePrompt.line1"), I18n.format("multiplayer.texturePrompt.line2"), 0)));
}
}
return new S48PacketResourcePackSend("replay://" + requestId, "");
return new SPacketResourcePackSend("replay://" + requestId, "");
}
private void downloadResourcePackFuture(int requestId, String url, final String hash) {
Futures.addCallback(downloadResourcePack(requestId, url, hash), new FutureCallback() {
@Override
public void onSuccess(Object result) {
mc.getNetHandler().addToSendQueue(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.SUCCESSFULLY_LOADED));
mc.getConnection().sendPacket(new CPacketResourcePackStatus(CPacketResourcePackStatus.Action.SUCCESSFULLY_LOADED));
}
@Override
public void onFailure(@Nonnull Throwable throwable) {
mc.getNetHandler().addToSendQueue(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.FAILED_DOWNLOAD));
mc.getConnection().sendPacket(new CPacketResourcePackStatus(CPacketResourcePackStatus.Action.FAILED_DOWNLOAD));
}
});
}
@@ -161,16 +161,16 @@ public class ResourcePackRecorder {
}
final File file = new File(repo.dirServerResourcepacks, fileName);
repo.field_177321_h.lock();
repo.lock.lock();
try {
repo.func_148529_f();
repo.clearResourcePack();
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.func_177319_a(file);
return repo.setResourcePackInstance(file);
}
logger.warn("File " + file + " had wrong hash (expected " + hash + ", found " + fileHash + "). Deleting it.");
@@ -186,13 +186,13 @@ public class ResourcePackRecorder {
Futures.getUnchecked(mc.addScheduledTask(() -> mc.displayGuiScreen(guiScreen)));
Map sessionInfo = Minecraft.getSessionInfo();
repo.field_177322_i = HttpUtil.func_180192_a(file, url, sessionInfo, 50 * 1024 * 1024, guiScreen, mc.getProxy());
Futures.addCallback(repo.field_177322_i, new FutureCallback() {
Map<String, String> sessionInfo = ResourcePackRepository.getDownloadHeaders();
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.func_177319_a(file);
repo.setResourcePackInstance(file);
}
@Override
@@ -200,9 +200,9 @@ public class ResourcePackRecorder {
throwable.printStackTrace();
}
});
return repo.field_177322_i;
return repo.downloadingPacks;
} finally {
repo.field_177321_h.unlock();
repo.lock.unlock();
}
}

View File

@@ -1,8 +1,6 @@
package com.replaymod.render;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import net.minecraft.client.resources.I18n;
import org.lwjgl.util.ReadableColor;
@@ -47,7 +45,7 @@ public class RenderSettings {
}
public String getValue() {
return "-y -f rawvideo -pix_fmt rgb24 -s %WIDTH%x%HEIGHT% -r %FPS% -i - %FILTERS%" + preset;
return "-y -f rawvideo -pix_fmt rgb24 -s %WIDTH%x%HEIGHT% -r %FPS% -i - " + preset;
}
public String getFileExtension() {
@@ -66,19 +64,6 @@ public class RenderSettings {
}
}
@AllArgsConstructor
public enum AntiAliasing {
NONE(1), X2(2), X4(4), X8(8);
@Getter
private final int factor;
@Override
public String toString() {
return I18n.format("replaymod.gui.rendersettings.antialiasing." + name().toLowerCase());
}
}
private final RenderMethod renderMethod;
private final EncodingPreset encodingPreset;
private final int videoWidth;
@@ -93,48 +78,9 @@ public class RenderSettings {
private final boolean stabilizeRoll;
private final ReadableColor chromaKeyingColor;
private final boolean inject360Metadata;
private final AntiAliasing antiAliasing;
private final String exportCommand;
private final String exportArguments;
private final boolean highPerformance;
/**
* @return the width of the output video during rendering, including the upscale for Anti-Aliasing.
*/
public int getVideoWidth() {
return videoWidth * antiAliasing.getFactor();
}
/**
* @return the height of the output video during rendering, including the upscale for Anti-Aliasing.
*/
public int getVideoHeight() {
return videoHeight * antiAliasing.getFactor();
}
/**
* @return the actual width of the output video.
*/
public int getTargetVideoWidth() {
return videoWidth;
}
/**
* @return the actual height of the output video.
*/
public int getTargetVideoHeight() {
return videoHeight;
}
public String getVideoFilters() {
if (antiAliasing == AntiAliasing.NONE) {
return "";
} else {
double factor = 1.0 / antiAliasing.getFactor();
return String.format("-vf scale=iw*%1$s:ih*%1$s ", factor);
}
}
}

View File

@@ -1,39 +1,30 @@
package com.replaymod.render;
import com.replaymod.core.ReplayMod;
import com.replaymod.render.utils.RenderJob;
import com.replaymod.replay.events.ReplayCloseEvent;
import net.minecraft.crash.CrashReport;
import net.minecraft.util.ReportedException;
import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.fml.common.FMLCommonHandler;
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;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@Mod(modid = ReplayModRender.MOD_ID,
version = "@MOD_VERSION@",
acceptedMinecraftVersions = "@MC_VERSION@",
useMetadata = true)
@Mod(modid = ReplayModRender.MOD_ID, useMetadata = true)
public class ReplayModRender {
public static final String MOD_ID = "replaymod-render";
@Mod.Instance(MOD_ID)
public static ReplayModRender instance;
private ReplayMod core;
@Mod.Instance(ReplayMod.MOD_ID)
private static ReplayMod core;
public static Logger LOGGER;
private Logger logger;
private Configuration configuration;
private final List<RenderJob> renderQueue = new ArrayList<>();
public ReplayMod getCore() {
return core;
@@ -41,20 +32,12 @@ public class ReplayModRender {
@Mod.EventHandler
public void preInit(FMLPreInitializationEvent event) {
LOGGER = event.getModLog();
core = ReplayMod.instance;
logger = event.getModLog();
configuration = new Configuration(event.getSuggestedConfigurationFile());
FMLCommonHandler.instance().bus().register(this);
core.getSettingsRegistry().register(Setting.class);
}
@SubscribeEvent
public void onReplayClose(ReplayCloseEvent.Post event) {
renderQueue.clear();
}
public File getVideoFolder() {
String path = core.getSettingsRegistry().get(Setting.RENDER_PATH);
File folder = new File(path.startsWith("./") ? core.getMinecraft().mcDataDir : null, path);
@@ -69,8 +52,4 @@ public class ReplayModRender {
public Configuration getConfiguration() {
return configuration;
}
public List<RenderJob> getRenderQueue() {
return renderQueue;
}
}

View File

@@ -7,27 +7,17 @@ import com.replaymod.render.utils.StreamPipe;
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;
import org.apache.commons.io.output.TeeOutputStream;
import org.lwjgl.util.ReadableDimension;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.*;
import java.nio.channels.Channels;
import java.nio.channels.WritableByteChannel;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import static com.replaymod.render.ReplayModRender.LOGGER;
import static org.apache.commons.lang3.Validate.isTrue;
public class VideoWriter implements FrameConsumer<RGBFrame> {
@@ -53,11 +43,10 @@ public class VideoWriter implements FrameConsumer<RGBFrame> {
.replace("%HEIGHT%", String.valueOf(settings.getVideoHeight()))
.replace("%FPS%", String.valueOf(settings.getFramesPerSecond()))
.replace("%FILENAME%", fileName)
.replace("%BITRATE%", String.valueOf(settings.getBitRate()))
.replace("%FILTERS%", settings.getVideoFilters());
.replace("%BITRATE%", String.valueOf(settings.getBitRate()));
String executable = settings.getExportCommand().isEmpty() ? findFFmpeg() : settings.getExportCommand();
System.out.println("Starting " + executable + " with args: " + commandArgs);
String executable = settings.getExportCommand().isEmpty() ? "ffmpeg" : settings.getExportCommand();
System.out.println("Starting " + settings.getExportCommand() + " with args: " + commandArgs);
String[] cmdline = new CommandLine(executable).addArguments(commandArgs).toStrings();
process = new ProcessBuilder(cmdline).directory(outputFolder).start();
File exportLogFile = new File(Minecraft.getMinecraft().mcDataDir, "export.log");
@@ -68,54 +57,6 @@ public class VideoWriter implements FrameConsumer<RGBFrame> {
channel = Channels.newChannel(outputStream);
}
private String findFFmpeg() {
switch (Util.getOSType()) {
case WINDOWS:
// Allow windows users to unpack the ffmpeg archive into a sub-folder of their .minecraft folder
File inDotMinecraft = new File(Minecraft.getMinecraft().mcDataDir, "ffmpeg/bin/ffmpeg.exe");
if (inDotMinecraft.exists()) {
LOGGER.debug("FFmpeg found in .minecraft/ffmpeg");
return inDotMinecraft.getAbsolutePath();
}
break;
case OSX:
// The PATH doesn't seem to be set as expected on OSX, therefore we check some common locations ourselves
for (String path : new String[]{"/usr/local/bin/ffmpeg", "/usr/bin/ffmpeg"}) {
File file = new File(path);
if (file.exists()) {
LOGGER.debug("Found FFmpeg at {}", path);
return path;
} else {
LOGGER.debug("FFmpeg not located at {}", path);
}
}
// Homebrew doesn't seem to reliably symlink its installed binaries either
File homebrewFolder = new File("/usr/local/Cellar/ffmpeg");
String[] homebrewVersions = homebrewFolder.list();
if (homebrewVersions != null) {
Optional<File> latestOpt = Arrays.stream(homebrewVersions)
.map(ComparableVersion::new) // Convert file name to comparable version
.sorted(Comparator.reverseOrder()) // Sort for latest version
.map(ComparableVersion::toString) // Convert back to file name
.map(v -> new File(new File(homebrewFolder, v), "bin/ffmpeg")) // Convert to binary files
.filter(File::exists) // Filter invalid installations (missing executable)
.findFirst(); // Take first one
if (latestOpt.isPresent()) {
File latest = latestOpt.get();
LOGGER.debug("Found {} versions of FFmpeg installed with homebrew, chose {}",
homebrewVersions.length, latest);
return latest.getAbsolutePath();
}
}
break;
case LINUX: // Linux users are entrusted to have their PATH configured correctly (most package manager do this)
case SOLARIS: // Never heard of anyone running this mod on Solaris having any problems
case UNKNOWN: // Unknown OS, just try to use "ffmpeg"
}
LOGGER.debug("Using default FFmpeg executable");
return "ffmpeg";
}
@Override
public void close() throws IOException {
IOUtils.closeQuietly(outputStream);

View File

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

View File

@@ -1,277 +0,0 @@
package com.replaymod.render.gui;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.replaymod.render.ReplayModRender;
import com.replaymod.render.rendering.VideoRenderer;
import com.replaymod.render.utils.RenderJob;
import com.replaymod.replay.ReplayHandler;
import com.replaymod.replaystudio.pathing.path.Timeline;
import com.replaymod.replaystudio.util.I18n;
import de.johni0702.minecraft.gui.GuiRenderer;
import de.johni0702.minecraft.gui.RenderInfo;
import de.johni0702.minecraft.gui.container.AbstractGuiClickableContainer;
import de.johni0702.minecraft.gui.container.GuiContainer;
import de.johni0702.minecraft.gui.container.GuiPanel;
import de.johni0702.minecraft.gui.container.GuiVerticalList;
import de.johni0702.minecraft.gui.element.GuiButton;
import de.johni0702.minecraft.gui.element.GuiLabel;
import de.johni0702.minecraft.gui.element.GuiTextField;
import de.johni0702.minecraft.gui.layout.CustomLayout;
import de.johni0702.minecraft.gui.layout.GridLayout;
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
import de.johni0702.minecraft.gui.popup.AbstractGuiPopup;
import de.johni0702.minecraft.gui.popup.GuiYesNoPopup;
import de.johni0702.minecraft.gui.utils.Colors;
import net.minecraft.client.gui.GuiErrorScreen;
import net.minecraft.crash.CrashReport;
import net.minecraft.util.ReportedException;
import org.lwjgl.util.Dimension;
import org.lwjgl.util.ReadableDimension;
import javax.annotation.Nullable;
import java.io.IOException;
import java.util.List;
import static com.replaymod.render.ReplayModRender.LOGGER;
public class GuiRenderQueue extends AbstractGuiPopup<GuiRenderQueue> {
private final GuiLabel title = new GuiLabel().setI18nText("replaymod.gui.renderqueue.title").setColor(Colors.BLACK);
private final GuiVerticalList list = new GuiVerticalList().setDrawShadow(true).setDrawSlider(true);
private final GuiButton addButton = new GuiButton().setI18nLabel("replaymod.gui.renderqueue.add").setSize(150, 20);
private final GuiButton renameButton = new GuiButton().setI18nLabel("replaymod.gui.rename").setSize(73, 20);
private final GuiButton removeButton = new GuiButton().setI18nLabel("replaymod.gui.remove").setSize(73, 20);
private final GuiButton renderButton = new GuiButton().setI18nLabel("replaymod.gui.render").setSize(150, 20);
private final GuiButton closeButton = new GuiButton().setI18nLabel("replaymod.gui.close").setSize(150, 20).onClick(this::close);
/*
|---------------------------------|
| Add | Render |
|---------------------------------|
| Rename | Remove | Close |
|---------------------------------|
*/
private final GuiPanel buttonPanel = new GuiPanel()
.setLayout(new GridLayout().setSpacingX(5).setSpacingY(5).setColumns(2))
.addElements(null,
addButton,
renderButton,
new GuiPanel().setLayout(new HorizontalLayout().setSpacing(4)).addElements(null,
renameButton, removeButton),
closeButton);
private final GuiContainer container;
private Entry selectedEntry;
{
popup.setLayout(new CustomLayout<GuiPanel>() {
@Override
protected void layout(GuiPanel container, int width, int height) {
pos(title, width / 2 - width(title) / 2, 0);
pos(list, 0, y(title) + height(title) + 5);
pos(buttonPanel, width / 2 - width(buttonPanel) / 2, height - height(buttonPanel));
size(list, width, y(buttonPanel) - y(list) - 10);
}
@Override
public ReadableDimension calcMinSize(GuiContainer<?> container) {
ReadableDimension screenSize = GuiRenderQueue.this.container.getMinSize();
return new Dimension(screenSize.getWidth() - 40,
screenSize.getHeight() - 20 - buttonPanel.getMinSize().getHeight() - title.getMinSize().getHeight());
}
}).addElements(null, title, list, buttonPanel);
}
public GuiRenderQueue(GuiContainer container, GuiRenderSettings guiRenderSettings, ReplayHandler replayHandler, Timeline timeline) {
super(container);
this.container = container;
LOGGER.trace("Opening render queue popup");
setBackgroundColor(Colors.DARK_TRANSPARENT);
List<RenderJob> queue = ReplayModRender.instance.getRenderQueue();
for (RenderJob renderJob : queue) {
LOGGER.trace("Adding {} to job queue list", renderJob);
list.getListPanel().addElements(null, new Entry(renderJob));
}
addButton.onClick(() -> {
LOGGER.trace("Add button clicked");
// Open popup
GuiYesNoPopup popup = GuiYesNoPopup.open(container)
.setYesI18nLabel("replaymod.gui.add").setNoI18nLabel("replaymod.gui.cancel");
popup.getInfo().setLayout(new HorizontalLayout(HorizontalLayout.Alignment.CENTER).setSpacing(5));
// Add content
GuiLabel label = new GuiLabel().setI18nText("replaymod.gui.renderqueue.jobname").setColor(Colors.BLACK);
GuiTextField nameField = new GuiTextField().setSize(150, 20).setFocused(true);
popup.getInfo().addElements(new HorizontalLayout.Data(0.5), label, nameField);
// Disable "Yes" button while name is empty
nameField.onTextChanged(old -> popup.getYesButton().setEnabled(!nameField.getText().isEmpty())).onEnter(() -> {
if (popup.getYesButton().isEnabled()) {
popup.getYesButton().onClick();
}
});
popup.getYesButton().setDisabled();
// Register callback
Futures.addCallback(popup.getFuture(), new FutureCallback<Boolean>() {
@Override
public void onSuccess(@Nullable Boolean result) {
if (result == Boolean.TRUE) {
RenderJob newJob = new RenderJob();
newJob.setName(nameField.getText());
newJob.setSettings(guiRenderSettings.save(false));
newJob.setTimeline(timeline);
LOGGER.trace("Adding new job: {}", newJob);
queue.add(newJob);
list.getListPanel().addElements(null, new Entry(newJob));
} else {
LOGGER.trace("Adding cancelled");
}
}
@Override
public void onFailure(Throwable t) {
LOGGER.error("Add Job popup:", t);
}
});
});
if (guiRenderSettings != null) {
addButton.setEnabled(guiRenderSettings.renderButton.isEnabled());
}
renameButton.onClick(() -> {
LOGGER.trace("Rename button clicked for {}", selectedEntry.job);
// Open popup
GuiYesNoPopup popup = GuiYesNoPopup.open(container)
.setYesI18nLabel("replaymod.gui.rename").setNoI18nLabel("replaymod.gui.cancel");
popup.getInfo().setLayout(new HorizontalLayout(HorizontalLayout.Alignment.CENTER).setSpacing(5));
// Add content
GuiLabel label = new GuiLabel().setI18nText("replaymod.gui.renderqueue.jobname").setColor(Colors.BLACK);
GuiTextField nameField = new GuiTextField().setSize(150, 20).setFocused(true)
.setText(selectedEntry.job.getName());
popup.getInfo().addElements(new HorizontalLayout.Data(0.5), label, nameField);
// Disable "Yes" button while name is empty
nameField.onTextChanged(old -> popup.getYesButton().setEnabled(!nameField.getText().isEmpty())).onEnter(() -> {
if (popup.getYesButton().isEnabled()) {
popup.getYesButton().onClick();
}
});
// Register callback
Futures.addCallback(popup.getFuture(), new FutureCallback<Boolean>() {
@Override
public void onSuccess(@Nullable Boolean result) {
if (result == Boolean.TRUE) {
LOGGER.trace("Renaming {} to \"{}\"", selectedEntry.job, nameField.getText());
selectedEntry.setName(nameField.getText());
} else {
LOGGER.trace("Renaming cancelled");
}
}
@Override
public void onFailure(Throwable t) {
LOGGER.error("Rename Job popup:", t);
}
});
});
removeButton.onClick(() -> {
LOGGER.trace("Remove button clicked for {}", selectedEntry.job);
list.getListPanel().removeElement(selectedEntry);
queue.remove(selectedEntry.job);
selectedEntry = null;
updateButtons();
});
renderButton.onClick(() -> {
LOGGER.trace("Render button clicked");
// Close all GUIs (so settings in GuiRenderSettings are saved)
getMinecraft().displayGuiScreen(null);
// Start rendering
for (RenderJob renderJob : queue) {
LOGGER.info("Starting render job {}", renderJob);
try {
VideoRenderer videoRenderer = new VideoRenderer(renderJob.getSettings(), replayHandler, renderJob.getTimeline());
videoRenderer.renderVideo();
} catch (IOException e) {
LOGGER.error("Rendering video:", e);
GuiErrorScreen errorScreen = new GuiErrorScreen(I18n.format("replaymod.gui.rendering.error.title"),
I18n.format("replaymod.gui.rendering.error.message"));
getMinecraft().displayGuiScreen(errorScreen);
return;
} catch (Throwable t) {
CrashReport crashReport = CrashReport.makeCrashReport(t, "Rendering video");
throw new ReportedException(crashReport);
}
}
});
updateButtons();
}
@Override
public void open() {
super.open();
}
@Override
protected GuiRenderQueue getThis() {
return this;
}
public void updateButtons() {
renameButton.setEnabled(selectedEntry != null);
removeButton.setEnabled(selectedEntry != null);
renderButton.setEnabled(!list.getListPanel().getChildren().isEmpty());
}
public class Entry extends AbstractGuiClickableContainer<Entry> {
public final GuiLabel label = new GuiLabel(this);
public final RenderJob job;
public Entry(RenderJob job) {
this.job = job;
setLayout(new CustomLayout<Entry>() {
@Override
protected void layout(Entry container, int width, int height) {
pos(label, 5, height / 2 - height(label) / 2);
}
@Override
public ReadableDimension calcMinSize(GuiContainer<?> container) {
return new Dimension(buttonPanel.calcMinSize().getWidth(), 16);
}
});
label.setText(job.getName());
}
@Override
protected void onClick() {
selectedEntry = this;
updateButtons();
}
@Override
public void draw(GuiRenderer renderer, ReadableDimension size, RenderInfo renderInfo) {
if (selectedEntry == this) {
renderer.drawRect(0, 0, size.getWidth(), size.getHeight(), Colors.BLACK);
renderer.drawRect(0, 0, 2, size.getHeight(), Colors.WHITE);
}
super.draw(renderer, size, renderInfo);
}
public void setName(String name) {
job.setName(name);
label.setText(name);
}
@Override
protected Entry getThis() {
return this;
}
}
}

View File

@@ -32,14 +32,13 @@ import net.minecraft.crash.CrashReport;
import net.minecraft.util.ReportedException;
import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.common.config.Property;
import org.lwjgl.util.Color;
import org.lwjgl.util.Dimension;
import org.lwjgl.util.ReadableColor;
import org.lwjgl.util.ReadableDimension;
import javax.annotation.Nullable;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
@@ -150,18 +149,13 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
public final GuiCheckbox inject360Metadata = new GuiCheckbox()
.setI18nLabel("replaymod.gui.rendersettings.360metadata");
public final GuiDropdownMenu<RenderSettings.AntiAliasing> antiAliasingDropdown = new GuiDropdownMenu<RenderSettings.AntiAliasing>()
.setSize(200, 20).setValues(RenderSettings.AntiAliasing.values()).setSelected(RenderSettings.AntiAliasing.NONE);
public final GuiPanel advancedPanel = new GuiPanel().setLayout(new VerticalLayout().setSpacing(15))
.addElements(null, nametagCheckbox, new GuiPanel().setLayout(
new GridLayout().setCellsEqualSize(false).setColumns(2).setSpacingX(5).setSpacingY(15))
.addElements(new GridLayout.Data(0, 0.5),
new GuiLabel().setI18nText("replaymod.gui.rendersettings.stabilizecamera"), stabilizePanel,
chromaKeyingCheckbox, chromaKeyingColor,
inject360Metadata,
new GuiLabel(), // to show the anti-aliasing options in a new line
new GuiLabel().setI18nText("replaymod.gui.rendersettings.antialiasing"), antiAliasingDropdown));
inject360Metadata));
public final GuiTextField exportCommand = new GuiTextField().setI18nHint("replaymod.gui.rendersettings.command")
.setSize(55, 20).setMaxLength(100);
@@ -185,12 +179,6 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
public final GuiPanel buttonPanel = new GuiPanel(contentPanel).setLayout(new HorizontalLayout().setSpacing(4));
public final GuiButton queueButton = new GuiButton(buttonPanel).onClick(new Runnable() {
@Override
public void run() {
new GuiRenderQueue(GuiRenderSettings.this, GuiRenderSettings.this, replayHandler, timeline).open();
}
}).setSize(100, 20).setI18nLabel("replaymod.gui.renderqueue.open");
public final GuiButton renderButton = new GuiButton(buttonPanel).onClick(new Runnable() {
@Override
public void run() {
@@ -210,13 +198,13 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
throw new ReportedException(crashReport);
}
}
}).setSize(100, 20).setI18nLabel("replaymod.gui.render");
}).setSize(148, 20).setI18nLabel("replaymod.gui.render");
public final GuiButton cancelButton = new GuiButton(buttonPanel).onClick(new Runnable() {
@Override
public void run() {
getMinecraft().displayGuiScreen(null);
}
}).setSize(100, 20).setI18nLabel("replaymod.gui.cancel");
}).setSize(148, 20).setI18nLabel("replaymod.gui.cancel");
{
setBackground(Background.NONE);
@@ -273,10 +261,12 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
this.timeline = timeline;
String json = getConfigProperty(ReplayModRender.instance.getConfiguration()).getString();
RenderSettings settings = new GsonBuilder()
.registerTypeAdapter(RenderSettings.class, (InstanceCreator<RenderSettings>) type -> getDefaultRenderSettings())
.registerTypeAdapter(ReadableColor.class, new Gson().getAdapter(Color.class))
.create().fromJson(json, RenderSettings.class);
RenderSettings settings = new GsonBuilder().registerTypeAdapter(RenderSettings.class, new InstanceCreator<RenderSettings>() {
@Override
public RenderSettings createInstance(Type type) {
return getDefaultRenderSettings();
}
}).create().fromJson(json, RenderSettings.class);
load(settings);
}
@@ -357,8 +347,8 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
public void load(RenderSettings settings) {
renderMethodDropdown.setSelected(settings.getRenderMethod());
encodingPresetDropdown.setSelected(settings.getEncodingPreset());
videoWidth.setValue(settings.getTargetVideoWidth());
videoHeight.setValue(settings.getTargetVideoHeight());
videoWidth.setValue(settings.getVideoWidth());
videoHeight.setValue(settings.getVideoHeight());
frameRateSlider.setValue(settings.getFramesPerSecond() - 10);
if (settings.getBitRate() % (1 << 20) == 0) {
bitRateField.setValue(settings.getBitRate() >> 20);
@@ -390,7 +380,6 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
chromaKeyingColor.setColor(settings.getChromaKeyingColor());
}
inject360Metadata.setChecked(settings.isInject360Metadata());
antiAliasingDropdown.setSelected(settings.getAntiAliasing());
exportCommand.setText(settings.getExportCommand());
exportArguments.setText(settings.getExportArguments());
@@ -412,7 +401,6 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
stabilizeRoll.isChecked() && (serialize || stabilizeRoll.isEnabled()),
chromaKeyingCheckbox.isChecked() ? chromaKeyingColor.getColor() : null,
inject360Metadata.isChecked() && (serialize || inject360Metadata.isEnabled()),
antiAliasingDropdown.getSelectedValue(),
exportCommand.getText(),
exportArguments.getText(),
net.minecraft.client.gui.GuiScreen.isCtrlKeyDown()
@@ -427,7 +415,7 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
private RenderSettings getDefaultRenderSettings() {
return new RenderSettings(RenderSettings.RenderMethod.DEFAULT, RenderSettings.EncodingPreset.MP4_DEFAULT, 1920, 1080, 60, 10 << 20, null,
true, false, false, false, null, false, RenderSettings.AntiAliasing.NONE, "", RenderSettings.EncodingPreset.MP4_DEFAULT.getValue(), false);
true, false, false, false, null, false, "", RenderSettings.EncodingPreset.MP4_DEFAULT.getValue(), false);
}
@Override
@@ -443,8 +431,4 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
return configuration.get("rendersettings", "settings", "{}",
"Last state of the render settings GUI. Internal use only.");
}
public ReplayHandler getReplayHandler() {
return replayHandler;
}
}

View File

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

View File

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

View File

@@ -12,7 +12,7 @@ import net.minecraft.client.renderer.RenderGlobal;
import net.minecraft.client.settings.GameSettings;
import net.minecraft.entity.Entity;
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;
@@ -118,10 +118,10 @@ public abstract class MixinEntityRenderer implements EntityRendererHandler.IEnti
@Shadow
public abstract void renderHand(float partialTicks, int renderPass);
@Redirect(method = "renderWorldPass", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderGlobal;drawSelectionBox(Lnet/minecraft/entity/player/EntityPlayer;Lnet/minecraft/util/MovingObjectPosition;IF)V"))
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);
}
}

View File

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

View File

@@ -7,15 +7,14 @@ import net.minecraft.entity.Entity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.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(Render.class)
public abstract class MixinRender {
@Inject(method = "canRenderName", at = @At("HEAD"), cancellable = true)
private void replayModRender_areAllNamesHidden(Entity 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

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

View File

@@ -8,7 +8,7 @@ import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.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

@@ -1,8 +1,6 @@
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;
import com.replaymod.render.RenderSettings;
import com.replaymod.render.ReplayModRender;
@@ -14,17 +12,18 @@ 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 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.fml.common.FMLCommonHandler;
import net.minecraftforge.common.MinecraftForge;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.Display;
import org.lwjgl.util.Dimension;
@@ -54,9 +53,8 @@ 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;
@@ -90,7 +88,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() {
FMLCommonHandler.instance().bus().post(new ReplayRenderEvent.Pre(this));
MinecraftForge.EVENT_BUS.post(new ReplayRenderEvent.Pre(this));
setup();
@@ -136,7 +134,7 @@ public class VideoRenderer implements RenderInfo {
finish();
FMLCommonHandler.instance().bus().post(new ReplayRenderEvent.Post(this));
MinecraftForge.EVENT_BUS.post(new ReplayRenderEvent.Post(this));
return !cancelled;
}
@@ -176,8 +174,7 @@ public class VideoRenderer implements RenderInfo {
}
private void setup() {
timelinePlayer = new TimelinePlayer(replayHandler);
timelinePlayerFuture = timelinePlayer.start(timeline);
timelinePlayerFuture = new TimelinePlayer(replayHandler).start(timeline);
if (!OpenGlHelper.isFramebufferEnabled()) {
Display.setResizable(false);
@@ -196,9 +193,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();
@@ -219,9 +216,6 @@ 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
@@ -232,8 +226,6 @@ public class VideoRenderer implements RenderInfo {
if (!timelinePlayerFuture.isDone()) {
timelinePlayerFuture.cancel(false);
}
// Tear down of the timeline player might only happen the next tick after it was cancelled
timelinePlayer.onTick(new ReplayTimer.UpdatedEvent());
if (!OpenGlHelper.isFramebufferEnabled()) {
Display.setResizable(true);
@@ -242,13 +234,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();
new GuiRenderingDone(ReplayModRender.instance, videoWriter.getVideoFile(), totalFrames, settings).display();
@@ -286,7 +278,7 @@ public class VideoRenderer implements RenderInfo {
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());
try {

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

@@ -1,12 +0,0 @@
package com.replaymod.render.utils;
import com.replaymod.render.RenderSettings;
import com.replaymod.replaystudio.pathing.path.Timeline;
import lombok.Data;
@Data
public class RenderJob {
private String name;
private Timeline timeline;
private RenderSettings settings;
}

View File

@@ -143,10 +143,11 @@ 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) {
mc.ingameGUI.getChatGUI().clearChatMessages();
mc.ingameGUI.getChatGUI().clearChatMessages(false);
}
if (key == 31 && Keyboard.isKeyDown(61)) {

View File

@@ -16,8 +16,6 @@ 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
@@ -45,7 +43,7 @@ public class NoGuiScreenshot {
mc.getFramebuffer().bindFramebuffer(true);
GlStateManager.enableTexture2D();
mc.entityRenderer.updateCameraAndRender(mc.timer.renderPartialTicks);
mc.entityRenderer.updateCameraAndRender(mc.timer.renderPartialTicks, System.nanoTime());
mc.getFramebuffer().unbindFramebuffer();
GlStateManager.popMatrix();
@@ -101,9 +99,7 @@ public class NoGuiScreenshot {
// of the game loop. We cannot use the addScheduledTask method as it'll run the task if called
// 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

@@ -23,8 +23,8 @@ import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
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.FMLCommonHandler;
import net.minecraftforge.fml.common.network.handshake.NetworkDispatcher;
import org.lwjgl.opengl.Display;
@@ -76,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()));
@@ -87,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() {
@@ -101,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();
@@ -124,11 +124,11 @@ public class ReplayHandler {
ReplayModReplay.instance.replayHandler = null;
FMLCommonHandler.instance().bus().post(new ReplayCloseEvent.Post(this));
MinecraftForge.EVENT_BUS.post(new ReplayCloseEvent.Post(this));
}
private void setup() {
mc.ingameGUI.getChatGUI().clearChatMessages();
mc.ingameGUI.getChatGUI().clearChatMessages(true);
NetworkManager networkManager = new NetworkManager(EnumPacketDirection.CLIENTBOUND) {
@Override
@@ -311,7 +311,7 @@ public class ReplayHandler {
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);
@@ -328,10 +328,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);

View File

@@ -28,17 +28,15 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Optional;
@Mod(modid = ReplayModReplay.MOD_ID,
version = "@MOD_VERSION@",
acceptedMinecraftVersions = "@MC_VERSION@",
useMetadata = true)
@Mod(modid = ReplayModReplay.MOD_ID, useMetadata = true)
public class ReplayModReplay {
public static final String MOD_ID = "replaymod-replay";
@Mod.Instance(MOD_ID)
public static ReplayModReplay instance;
private ReplayMod core;
@Mod.Instance(ReplayMod.MOD_ID)
private static ReplayMod core;
private final CameraControllerRegistry cameraControllerRegistry = new CameraControllerRegistry();
@@ -53,7 +51,6 @@ public class ReplayModReplay {
@Mod.EventHandler
public void preInit(FMLPreInitializationEvent event) {
logger = event.getModLog();
core = ReplayMod.instance;
core.getSettingsRegistry().register(Setting.class);

View File

@@ -12,17 +12,22 @@ import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
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.I18n;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.network.*;
import net.minecraft.network.play.server.*;
import net.minecraft.util.IChatComponent;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.world.EnumDifficulty;
import net.minecraft.world.GameType;
import net.minecraft.world.World;
import net.minecraft.world.WorldSettings.GameType;
import net.minecraft.world.WorldType;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
@@ -39,30 +44,22 @@ import java.util.concurrent.Callable;
*/
@Sharable
public class ReplaySender extends ChannelInboundHandlerAdapter {
/**
* Previously packets for the client player were inserted using one fixed entity id (this one).
* This is no longer the case however to provide backwards compatibility, we have to convert
* these old packets to use the normal entity id.
* Need to punch someone? -> CrushedPixel
*/
public static final int LEGACY_ENTITY_ID = Integer.MIN_VALUE + 9001;
/**
* These packets are ignored completely during replay.
*/
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,
S43PacketCamera.class,
S39PacketPlayerAbilities.class,
S45PacketTitle.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;
@@ -171,6 +168,8 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
this.asyncMode = asyncMode;
this.replayLength = file.getMetaData().getDuration();
MinecraftForge.EVENT_BUS.register(this);
if (asyncMode) {
new Thread(asyncSender, "replaymod-async-sender").start();
}
@@ -239,6 +238,7 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
*/
public void terminateReplay() {
terminate = true;
MinecraftForge.EVENT_BUS.unregister(this);
try {
channelInactive(ctx);
ctx.channel().pipeline().close();
@@ -247,6 +247,25 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
}
}
@SubscribeEvent
public void onWorldTick(TickEvent.ClientTickEvent event) {
// Unfortunately the WorldTickEvent doesn't seem to be emitted on the CLIENT side
if (event.phase != TickEvent.Phase.START) return;
// Spawning a player into an empty chunk (which we might do with the recording player)
// prevents it from being moved by teleport packets (it essentially gets stuck) because
// Entity#addedToChunk is not set and it is therefore not updated every tick.
// To counteract this, we need to manually update it's position if it hasn't been added
// to any chunk yet.
if (mc.theWorld != null) {
for (EntityPlayer playerEntity : mc.theWorld.playerEntities) {
if (!playerEntity.addedToChunk && playerEntity instanceof EntityOtherPlayerMP) {
playerEntity.onLivingUpdate();
}
}
}
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
@@ -274,21 +293,21 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
// Therefore we have to remove all loaded, dead entities manually if we are in sync mode.
// 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);
}
@@ -326,8 +345,8 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
* @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 (p instanceof SPacketCustomPayload) {
SPacketCustomPayload packet = (SPacketCustomPayload) p;
if (Restrictions.PLUGIN_CHANNEL.equals(packet.getChannelName())) {
final String unknown = replayHandler.getRestrictions().handle(packet);
if (unknown == null) {
@@ -353,8 +372,8 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
}
}
}
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.
@@ -364,11 +383,9 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
if(BAD_PACKETS.contains(p.getClass())) return null;
convertLegacyEntityIds(p);
if(p instanceof S48PacketResourcePackSend) {
S48PacketResourcePackSend packet = (S48PacketResourcePackSend) p;
String url = packet.func_179783_a();
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();
@@ -379,17 +396,17 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
if (!file.exists()) {
IOUtils.copy(replayFile.getResourcePack(hash).get(), new FileOutputStream(file));
}
mc.getResourcePackRepository().func_177319_a(file);
mc.getResourcePackRepository().setResourcePackInstance(file);
}
}
return null;
}
}
if(p instanceof S01PacketJoinGame) {
S01PacketJoinGame packet = (S01PacketJoinGame) p;
if(p instanceof SPacketJoinGame) {
SPacketJoinGame packet = (SPacketJoinGame) p;
allowMovement = true;
int entId = packet.getEntityId();
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.getDimension();
@@ -397,21 +414,21 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
int maxPlayers = packet.getMaxPlayers();
WorldType worldType = packet.getWorldType();
p = new S01PacketJoinGame(entId, GameType.SPECTATOR, false, dimension,
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.SPECTATOR);
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
@@ -422,17 +439,17 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
CameraEntity cent = replayHandler.getCameraEntity();
for (Object relative : ppl.func_179834_f()) {
if (relative == S08PacketPlayerPosLook.EnumFlags.X
|| relative == S08PacketPlayerPosLook.EnumFlags.Y
|| relative == S08PacketPlayerPosLook.EnumFlags.Z) {
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;
@@ -451,15 +468,15 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
}
CameraEntity cent = replayHandler.getCameraEntity();
cent.setCameraPosition(ppl.func_148932_c(), ppl.func_148928_d(), ppl.func_148933_e());
cent.setCameraPosition(ppl.getX(), ppl.getY(), ppl.getZ());
return null;
}
}.call();
}
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
@@ -473,7 +490,7 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
}
}
if (p instanceof S02PacketChat) {
if (p instanceof SPacketChat) {
if (!ReplayModReplay.instance.getCore().getSettingsRegistry().get(Setting.SHOW_CHAT)) {
return null;
}
@@ -486,7 +503,7 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
@SuppressWarnings("unchecked")
public void channelActive(ChannelHandlerContext ctx) throws Exception {
this.ctx = ctx;
ctx.attr(NetworkManager.attrKeyConnectionState).set(EnumConnectionState.PLAY);
ctx.attr(NetworkManager.PROTOCOL_ATTRIBUTE_KEY).set(EnumConnectionState.PLAY);
super.channelActive(ctx);
}
@@ -691,11 +708,11 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
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;
}
@@ -781,68 +798,6 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
return p; // During synchronous playback everything is sent normally
}
/**
* This is necessary to convert packets from old replays to new replays.
* @param packet The packet to be transformed.
* @see #LEGACY_ENTITY_ID
*/
private void convertLegacyEntityIds(Packet packet) {
if (packet instanceof S0CPacketSpawnPlayer) {
S0CPacketSpawnPlayer p = (S0CPacketSpawnPlayer) packet;
if (p.field_148957_a == LEGACY_ENTITY_ID) {
p.field_148957_a = actualID;
}
} else if (packet instanceof S18PacketEntityTeleport) {
S18PacketEntityTeleport p = (S18PacketEntityTeleport) packet;
if (p.field_149458_a == LEGACY_ENTITY_ID) {
p.field_149458_a = actualID;
}
} else if (packet instanceof S14PacketEntity.S17PacketEntityLookMove) {
S14PacketEntity.S17PacketEntityLookMove p = (S14PacketEntity.S17PacketEntityLookMove) packet;
if (p.field_149074_a == LEGACY_ENTITY_ID) {
p.field_149074_a = actualID;
}
} else if (packet instanceof S19PacketEntityHeadLook) {
S19PacketEntityHeadLook p = (S19PacketEntityHeadLook) packet;
if (p.field_149384_a == LEGACY_ENTITY_ID) {
p.field_149384_a = actualID;
}
} else if (packet instanceof S12PacketEntityVelocity) {
S12PacketEntityVelocity p = (S12PacketEntityVelocity) packet;
if (p.field_149417_a == LEGACY_ENTITY_ID) {
p.field_149417_a = actualID;
}
} else if (packet instanceof S0BPacketAnimation) {
S0BPacketAnimation p = (S0BPacketAnimation) packet;
if (p.entityId == LEGACY_ENTITY_ID) {
p.entityId = actualID;
}
} else if (packet instanceof S04PacketEntityEquipment) {
S04PacketEntityEquipment p = (S04PacketEntityEquipment) packet;
if (p.field_149394_a == LEGACY_ENTITY_ID) {
p.field_149394_a = actualID;
}
} else if (packet instanceof S1BPacketEntityAttach) {
S1BPacketEntityAttach p = (S1BPacketEntityAttach) packet;
if (p.field_149408_a == LEGACY_ENTITY_ID) {
p.field_149408_a = actualID;
}
if (p.field_149406_b == LEGACY_ENTITY_ID) {
p.field_149406_b = actualID;
}
} else if (packet instanceof S0DPacketCollectItem) {
S0DPacketCollectItem p = (S0DPacketCollectItem) packet;
if (p.field_149356_b == LEGACY_ENTITY_ID) {
p.field_149356_b = actualID;
}
} else if (packet instanceof S13PacketDestroyEntities) {
S13PacketDestroyEntities p = (S13PacketDestroyEntities) packet;
if (p.field_149100_a.length == 1 && p.field_149100_a[0] == LEGACY_ENTITY_ID) {
p.field_149100_a[0] = actualID;
}
}
}
private static final class PacketData {
private final int timestamp;
private final byte[] bytes;

View File

@@ -18,16 +18,17 @@ import net.minecraft.entity.Entity;
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.AxisAlignedBB;
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.math.AxisAlignedBB;
import net.minecraft.util.math.RayTraceResult;
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.FMLCommonHandler;
import net.minecraftforge.fml.common.eventhandler.EventPriority;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
@@ -65,9 +66,8 @@ public class CameraEntity extends EntityPlayerSP {
*/
private final EventHandler eventHandler = new EventHandler();
public CameraEntity(Minecraft mcIn, World worldIn, NetHandlerPlayClient netHandlerPlayClient, StatFileWriter statFileWriter) {
super(mcIn, worldIn, netHandlerPlayClient, statFileWriter);
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);
@@ -280,12 +280,49 @@ public class CameraEntity extends EntityPlayerSP {
}
@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;
@@ -300,7 +337,6 @@ public class CameraEntity extends EntityPlayerSP {
@Override
public void setDead() {
super.setDead();
FMLCommonHandler.instance().bus().unregister(eventHandler);
MinecraftForge.EVENT_BUS.unregister(eventHandler);
}
@@ -358,9 +394,13 @@ public class CameraEntity extends EntityPlayerSP {
@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
@@ -391,10 +431,13 @@ public class CameraEntity extends EntityPlayerSP {
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;
@@ -405,7 +448,7 @@ public class CameraEntity extends EntityPlayerSP {
@SubscribeEvent
public void onEntityViewRenderEvent(EntityViewRenderEvent.CameraSetup event) {
if (mc.getRenderViewEntity() == CameraEntity.this) {
event.roll = roll;
event.setRoll(roll);
}
}
}

View File

@@ -2,8 +2,8 @@ package com.replaymod.replay.camera;
import net.minecraft.client.Minecraft;
import net.minecraft.client.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();
@@ -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,7 +158,7 @@ public class ClassicCameraController implements CameraController {
break;
}
Vec3 dbf = direction;
Vec3d dbf = direction;
if(dirBefore != null) {
direction = dirBefore.normalize().add(direction);
@@ -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 new Vec3((double)(f3 * f4), (double)f5, (double)(f2 * f4));
return new Vec3d((double)(f3 * f4), (double)f5, (double)(f2 * f4));
}
public enum MoveDirection {

View File

@@ -41,8 +41,9 @@ public class SpectatorCameraController implements CameraController {
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

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

View File

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

View File

@@ -9,12 +9,10 @@ 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.FMLCommonHandler;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class GuiHandler {
private static final int BUTTON_EXIT_SERVER = 1;
@@ -35,13 +33,12 @@ public class GuiHandler {
}
public void register() {
FMLCommonHandler.instance().bus().register(this);
MinecraftForge.EVENT_BUS.register(this);
}
@SubscribeEvent
public void injectIntoIngameMenu(GuiScreenEvent.InitGuiEvent.Post event) {
if (!(event.gui instanceof GuiIngameMenu)) {
if (!(event.getGui() instanceof GuiIngameMenu)) {
return;
}
@@ -49,9 +46,7 @@ public class GuiHandler {
// Pause replay when menu is opened
mod.getReplayHandler().getReplaySender().setReplaySpeed(0);
@SuppressWarnings("unchecked")
List<GuiButton> buttonList = event.buttonList;
for(GuiButton b : new ArrayList<>(buttonList)) {
for(GuiButton b : new ArrayList<>(event.getButtonList())) {
switch (b.id) {
// Replace "Exit Server" button with "Exit Replay" button
case BUTTON_EXIT_SERVER:
@@ -62,7 +57,7 @@ public class GuiHandler {
case BUTTON_ACHIEVEMENTS:
case BUTTON_STATS:
case BUTTON_OPEN_TO_LAN:
buttonList.remove(b);
event.getButtonList().remove(b);
}
// Move all buttons except the "Return to game" button upwards
if (b.id != BUTTON_RETURN_TO_GAME) {
@@ -74,31 +69,29 @@ public class GuiHandler {
@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_VIEWER, event.gui.width / 2 - 100,
event.gui.height / 4 + 10 + 3 * 24, I18n.format("replaymod.gui.replayviewer"));
GuiButton button = new GuiButton(BUTTON_REPLAY_VIEWER, event.getGui().width / 2 - 100,
event.getGui().height / 4 + 10 + 3 * 24, I18n.format("replaymod.gui.replayviewer"));
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_VIEWER) {
if (event.getGui() instanceof GuiMainMenu) {
if (event.getButton().id == BUTTON_REPLAY_VIEWER) {
new GuiReplayViewer(mod).display();
}
}
if (event.gui instanceof GuiIngameMenu && mod.getReplayHandler() != null) {
if (event.button.id == BUTTON_EXIT_REPLAY) {
event.button.enabled = false;
if (event.getGui() instanceof GuiIngameMenu && mod.getReplayHandler() != null) {
if (event.getButton().id == BUTTON_EXIT_REPLAY) {
event.getButton().enabled = false;
mc.displayGuiScreen(new GuiMainMenu());
try {
mod.getReplayHandler().endReplay();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,7 +3,7 @@ package com.replaymod.replay.mixin;
import com.replaymod.replay.ReplayHandler;
import com.replaymod.replay.ReplayModReplay;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.entity.RenderItem;
import net.minecraft.client.renderer.RenderItem;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;

View File

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

View File

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

View File

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

View File

@@ -18,7 +18,7 @@ import com.replaymod.replaystudio.pathing.path.Keyframe;
import com.replaymod.replaystudio.pathing.path.Timeline;
import com.replaymod.simplepathing.gui.GuiPathing;
import com.replaymod.simplepathing.preview.PathPreview;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
@@ -26,27 +26,24 @@ import org.apache.logging.log4j.Logger;
import java.io.IOException;
@Mod(modid = ReplayModSimplePathing.MOD_ID,
version = "@MOD_VERSION@",
acceptedMinecraftVersions = "@MC_VERSION@",
useMetadata = true)
@Mod(modid = ReplayModSimplePathing.MOD_ID, useMetadata = true)
public class ReplayModSimplePathing implements PathingRegistry {
public static final String MOD_ID = "replaymod-simplepathing";
private ReplayMod core;
@Mod.Instance(ReplayMod.MOD_ID)
private static ReplayMod core;
public static Logger LOGGER;
private Logger logger;
private GuiPathing guiPathing;
@Mod.EventHandler
public void preInit(FMLPreInitializationEvent event) {
LOGGER = event.getModLog();
core = ReplayMod.instance;
logger = event.getModLog();
core.getSettingsRegistry().register(Setting.class);
FMLCommonHandler.instance().bus().register(this);
MinecraftForge.EVENT_BUS.register(this);
PathPreview pathPreview = new PathPreview(this);
pathPreview.register();

View File

@@ -63,7 +63,7 @@ public abstract class GuiEditKeyframe<T extends GuiEditKeyframe<T>> extends Abst
setBackgroundColor(Colors.DARK_TRANSPARENT);
popup.setLayout(new VerticalLayout().setSpacing(10))
.addElements(new VerticalLayout.Data(0.5, false), title, inputs, timePanel, buttons);
.addElements(new VerticalLayout.Data(0.5), title, inputs, timePanel, buttons);
}
public GuiEditKeyframe(GuiPathing gui, Path path, Keyframe keyframe, String type) {
@@ -179,13 +179,13 @@ public abstract class GuiEditKeyframe<T extends GuiEditKeyframe<T>> extends Abst
}
public static class Position extends GuiEditKeyframe<Position> {
public final GuiNumberField xField = newGuiNumberField().setSize(60, 20).setPrecision(5);
public final GuiNumberField yField = newGuiNumberField().setSize(60, 20).setPrecision(5);
public final GuiNumberField zField = newGuiNumberField().setSize(60, 20).setPrecision(5);
public final GuiNumberField xField = newGuiNumberField().setSize(150, 20).setPrecision(10);
public final GuiNumberField yField = newGuiNumberField().setSize(150, 20).setPrecision(10);
public final GuiNumberField zField = newGuiNumberField().setSize(150, 20).setPrecision(10);
public final GuiNumberField yawField = newGuiNumberField().setSize(60, 20).setPrecision(5);
public final GuiNumberField pitchField = newGuiNumberField().setSize(60, 20).setPrecision(5);
public final GuiNumberField rollField = newGuiNumberField().setSize(60, 20).setPrecision(5);
public final GuiNumberField yawField = newGuiNumberField().setSize(150, 20).setPrecision(5);
public final GuiNumberField pitchField = newGuiNumberField().setSize(150, 20).setPrecision(5);
public final GuiNumberField rollField = newGuiNumberField().setSize(150, 20).setPrecision(5);
{
inputs.setLayout(new GridLayout().setCellsEqualSize(false).setColumns(4).setSpacingX(3).setSpacingY(5))

View File

@@ -23,7 +23,6 @@ import com.replaymod.replaystudio.pathing.path.Path;
import com.replaymod.replaystudio.pathing.path.PathSegment;
import com.replaymod.replaystudio.pathing.path.Timeline;
import com.replaymod.replaystudio.pathing.property.Property;
import com.replaymod.replaystudio.pathing.serialize.TimelineSerialization;
import com.replaymod.replaystudio.util.EntityPositionTracker;
import com.replaymod.replaystudio.util.Location;
import com.replaymod.simplepathing.ReplayModSimplePathing;
@@ -42,7 +41,6 @@ import de.johni0702.minecraft.gui.popup.AbstractGuiPopup;
import de.johni0702.minecraft.gui.popup.GuiInfoPopup;
import de.johni0702.minecraft.gui.popup.GuiYesNoPopup;
import de.johni0702.minecraft.gui.utils.Colors;
import net.minecraft.crash.CrashReport;
import net.minecraft.entity.Entity;
import net.minecraftforge.fml.common.Loader;
import org.apache.commons.lang3.tuple.Triple;
@@ -58,14 +56,10 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import static com.replaymod.core.utils.Utils.error;
import static com.replaymod.simplepathing.ReplayModSimplePathing.LOGGER;
/**
* Gui plug-in to the GuiReplayOverlay for simple pathing.
@@ -97,19 +91,7 @@ public class GuiPathing {
@Override
public void run() {
if (!preparePathsForPlayback()) return;
// Clone the timeline passed to the settings gui as it may be stored for later rendering in a queue
Timeline timeline = mod.getCurrentTimeline();
try {
TimelineSerialization serialization = new TimelineSerialization(mod, null);
String serialized = serialization.serialize(Collections.singletonMap("", timeline));
timeline = serialization.deserialize(serialized).get("");
} catch (Throwable t) {
error(LOGGER, replayHandler.getOverlay(), CrashReport.makeCrashReport(t, "Cloning timeline"), () -> {});
return;
}
new GuiRenderSettings(replayHandler, timeline).display();
new GuiRenderSettings(replayHandler, mod.getCurrentTimeline()).display();
}
}).setSize(20, 20).setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE).setTexturePosH(40, 0)
.setTooltip(new GuiTooltip().setI18nText("replaymod.gui.ingame.menu.renderpath"));
@@ -530,13 +512,6 @@ public class GuiPathing {
lastTime = time;
}
// Make sure there are at least two position- and two time-keyframes
if (timeline.getPaths().get(POSITION_PATH).getSegments().isEmpty()
|| timeline.getPaths().get(TIME_PATH).getSegments().isEmpty()) {
GuiInfoPopup.open(replayHandler.getOverlay(), "replaymod.chat.morekeyframes");
return false;
}
return true;
}

View File

@@ -8,7 +8,7 @@ import com.replaymod.replay.events.ReplayCloseEvent;
import com.replaymod.replay.events.ReplayOpenEvent;
import com.replaymod.simplepathing.ReplayModSimplePathing;
import com.replaymod.simplepathing.Setting;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import org.lwjgl.input.Keyboard;
@@ -23,7 +23,7 @@ public class PathPreview {
}
public void register() {
FMLCommonHandler.instance().bus().register(this);
MinecraftForge.EVENT_BUS.register(this);
ReplayMod core = mod.getCore();
mod.getCore().getKeyBindingRegistry().registerKeyBinding("replaymod.input.pathpreview", Keyboard.KEY_H, () -> {

View File

@@ -17,7 +17,8 @@ import com.replaymod.simplepathing.gui.GuiPathing;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.client.renderer.VertexBuffer;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.entity.Entity;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.event.RenderWorldLastEvent;
@@ -203,26 +204,35 @@ public class PathPreviewRenderer {
if (distanceSquared(view, pos2) > renderDistanceSquared) return;
Tessellator tessellator = Tessellator.getInstance();
WorldRenderer renderer = tessellator.getWorldRenderer();
renderer.setTranslation(-view.getLeft(), -view.getMiddle(), -view.getRight());
VertexBuffer vertexBuffer = tessellator.getBuffer();
vertexBuffer.setTranslation(-view.getLeft(), -view.getMiddle(), -view.getRight());
renderer.startDrawing(GL11.GL_LINES);
renderer.setColorRGBA_I(color, 255);
vertexBuffer.begin(GL11.GL_LINES, DefaultVertexFormats.POSITION_COLOR);
renderer.addVertex(pos1.getLeft(), pos1.getMiddle(), pos1.getRight());
renderer.addVertex(pos2.getLeft(), pos2.getMiddle(), pos2.getRight());
vertexBuffer.pos(pos1.getLeft(), pos1.getMiddle(), pos1.getRight()).color(
color >> 16 & 0xff,
color >> 8 & 0xff,
color & 0xff,
255
).endVertex();
vertexBuffer.pos(pos2.getLeft(), pos2.getMiddle(), pos2.getRight()).color(
color >> 16 & 0xff,
color >> 8 & 0xff,
color & 0xff,
255
).endVertex();
GL11.glLineWidth(3);
tessellator.draw();
renderer.setTranslation(0, 0, 0);
vertexBuffer.setTranslation(0, 0, 0);
}
private void drawPoint(Triple<Double, Double, Double> view,
Triple<Double, Double, Double> pos,
Keyframe keyframe) {
Tessellator tessellator = Tessellator.getInstance();
WorldRenderer renderer = tessellator.getWorldRenderer();
renderer.setTranslation(0, 0, 0);
VertexBuffer vertexBuffer = tessellator.getBuffer();
vertexBuffer.setTranslation(0, 0, 0);
mc.renderEngine.bindTexture(TEXTURE);
@@ -243,12 +253,12 @@ public class PathPreviewRenderer {
float maxX = 0.5f;
float maxY = 0.5f;
renderer.startDrawingQuads();
vertexBuffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
renderer.addVertexWithUV(minX, minY, 0, posX + size, posY + size);
renderer.addVertexWithUV(minX, maxY, 0, posX + size, posY);
renderer.addVertexWithUV(maxX, maxY, 0, posX, posY);
renderer.addVertexWithUV(maxX, minY, 0, posX, posY + size);
vertexBuffer.pos(minX, minY, 0).tex(posX + size, posY + size).endVertex();
vertexBuffer.pos(minX, maxY, 0).tex(posX + size, posY).endVertex();
vertexBuffer.pos(maxX, maxY, 0).tex(posX, posY).endVertex();
vertexBuffer.pos(maxX, minY, 0).tex(posX, posY + size).endVertex();
GL11.glPushMatrix();
@@ -270,8 +280,8 @@ public class PathPreviewRenderer {
Triple<Double, Double, Double> pos,
Triple<Float, Float, Float> rot) {
Tessellator tessellator = Tessellator.getInstance();
WorldRenderer renderer = tessellator.getWorldRenderer();
renderer.setTranslation(0, 0, 0);
VertexBuffer vertexBuffer = tessellator.getBuffer();
vertexBuffer.setTranslation(0, 0, 0);
mc.renderEngine.bindTexture(CAMERA_HEAD);
@@ -289,12 +299,10 @@ public class PathPreviewRenderer {
//draw the position line
GL11.glDisable(GL11.GL_TEXTURE_2D);
renderer.startDrawing(GL11.GL_LINES);
vertexBuffer.begin(GL11.GL_LINES, DefaultVertexFormats.POSITION_COLOR);
renderer.setColorRGBA_I(0x00ff00, 170);
renderer.addVertex(0, 0, 0);
renderer.addVertex(0, 0, 2);
vertexBuffer.pos(0, 0, 0).color(0, 255, 0, 170).endVertex();
vertexBuffer.pos(0, 0, 2).color(0, 255, 0, 170).endVertex();
tessellator.draw();
@@ -305,45 +313,43 @@ public class PathPreviewRenderer {
double r = -cubeSize/2;
renderer.startDrawingQuads();
renderer.setColorRGBA_I(0xffffff, 200);
vertexBuffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX_COLOR);
//back
renderer.addVertexWithUV(r, r + cubeSize, r, 3 * 8 / 64f, 8 / 64f);
renderer.addVertexWithUV(r + cubeSize, r + cubeSize, r, 4*8/64f, 8/64f);
renderer.addVertexWithUV(r + cubeSize, r, r, 4*8/64f, 2*8/64f);
renderer.addVertexWithUV(r, r, r, 3*8/64f, 2*8/64f);
vertexBuffer.pos(r, r + cubeSize, r).tex(3 * 8 / 64f, 8 / 64f).color(255, 255, 255, 200).endVertex();
vertexBuffer.pos(r + cubeSize, r + cubeSize, r).tex(4*8/64f, 8/64f).color(255, 255, 255, 200).endVertex();
vertexBuffer.pos(r + cubeSize, r, r).tex(4*8/64f, 2*8/64f).color(255, 255, 255, 200).endVertex();
vertexBuffer.pos(r, r, r).tex(3*8/64f, 2*8/64f).color(255, 255, 255, 200).endVertex();
//front
renderer.addVertexWithUV(r + cubeSize, r, r + cubeSize, 2 * 8 / 64f, 2*8/64f);
renderer.addVertexWithUV(r + cubeSize, r + cubeSize, r + cubeSize, 2 * 8 / 64f, 8/64f);
renderer.addVertexWithUV(r, r + cubeSize, r + cubeSize, 8 / 64f, 8 / 64f);
renderer.addVertexWithUV(r, r, r + cubeSize, 8 / 64f, 2*8/64f);
vertexBuffer.pos(r + cubeSize, r, r + cubeSize).tex(2 * 8 / 64f, 2*8/64f).color(255, 255, 255, 200).endVertex();
vertexBuffer.pos(r + cubeSize, r + cubeSize, r + cubeSize).tex(2 * 8 / 64f, 8/64f).color(255, 255, 255, 200).endVertex();
vertexBuffer.pos(r, r + cubeSize, r + cubeSize).tex(8 / 64f, 8 / 64f).color(255, 255, 255, 200).endVertex();
vertexBuffer.pos(r, r, r + cubeSize).tex(8 / 64f, 2*8/64f).color(255, 255, 255, 200).endVertex();
//left
renderer.addVertexWithUV(r + cubeSize, r + cubeSize, r, 0, 8/64f);
renderer.addVertexWithUV(r + cubeSize, r + cubeSize, r + cubeSize, 8/64f, 8/64f);
renderer.addVertexWithUV(r + cubeSize, r, r + cubeSize, 8/64f, 2*8/64f);
renderer.addVertexWithUV(r+cubeSize, r, r, 0, 2*8/64f);
vertexBuffer.pos(r + cubeSize, r + cubeSize, r).tex(0, 8/64f).color(255, 255, 255, 200).endVertex();
vertexBuffer.pos(r + cubeSize, r + cubeSize, r + cubeSize).tex(8/64f, 8/64f).color(255, 255, 255, 200).endVertex();
vertexBuffer.pos(r + cubeSize, r, r + cubeSize).tex(8/64f, 2*8/64f).color(255, 255, 255, 200).endVertex();
vertexBuffer.pos(r+cubeSize, r, r).tex(0, 2*8/64f).color(255, 255, 255, 200).endVertex();
//right
renderer.addVertexWithUV(r, r + cubeSize, r + cubeSize, 2*8/64f, 8/64f);
renderer.addVertexWithUV(r, r + cubeSize, r, 3*8/64f, 8/64f);
renderer.addVertexWithUV(r, r, r, 3*8/64f, 2*8/64f);
renderer.addVertexWithUV(r, r, r + cubeSize, 2 * 8 / 64f, 2 * 8 / 64f);
vertexBuffer.pos(r, r + cubeSize, r + cubeSize).tex(2*8/64f, 8/64f).color(255, 255, 255, 200).endVertex();
vertexBuffer.pos(r, r + cubeSize, r).tex(3*8/64f, 8/64f).color(255, 255, 255, 200).endVertex();
vertexBuffer.pos(r, r, r).tex(3*8/64f, 2*8/64f).color(255, 255, 255, 200).endVertex();
vertexBuffer.pos(r, r, r + cubeSize).tex(2 * 8 / 64f, 2 * 8 / 64f).color(255, 255, 255, 200).endVertex();
//bottom
renderer.addVertexWithUV(r + cubeSize, r, r, 3*8/64f, 0);
renderer.addVertexWithUV(r + cubeSize, r, r + cubeSize, 3*8/64f, 8/64f);
renderer.addVertexWithUV(r, r, r + cubeSize, 2*8/64f, 8/64f);
renderer.addVertexWithUV(r, r, r, 2 * 8 / 64f, 0);
vertexBuffer.pos(r + cubeSize, r, r).tex(3*8/64f, 0).color(255, 255, 255, 200).endVertex();
vertexBuffer.pos(r + cubeSize, r, r + cubeSize).tex(3*8/64f, 8/64f).color(255, 255, 255, 200).endVertex();
vertexBuffer.pos(r, r, r + cubeSize).tex(2*8/64f, 8/64f).color(255, 255, 255, 200).endVertex();
vertexBuffer.pos(r, r, r).tex(2 * 8 / 64f, 0).color(255, 255, 255, 200).endVertex();
//top
renderer.addVertexWithUV(r, r + cubeSize, r, 8/64f, 0);
renderer.addVertexWithUV(r, r + cubeSize, r + cubeSize, 8/64f, 8/64f);
renderer.addVertexWithUV(r + cubeSize, r + cubeSize, r + cubeSize, 2*8/64f, 8/64f);
renderer.addVertexWithUV(r + cubeSize, r + cubeSize, r, 2 * 8 / 64f, 0);
vertexBuffer.pos(r, r + cubeSize, r).tex(8/64f, 0).color(255, 255, 255, 200).endVertex();
vertexBuffer.pos(r, r + cubeSize, r + cubeSize).tex(8/64f, 8/64f).color(255, 255, 255, 200).endVertex();
vertexBuffer.pos(r + cubeSize, r + cubeSize, r + cubeSize).tex(2*8/64f, 8/64f).color(255, 255, 255, 200).endVertex();
vertexBuffer.pos(r + cubeSize, r + cubeSize, r).tex(2 * 8 / 64f, 0).color(255, 255, 255, 200).endVertex();
tessellator.draw();

View File

@@ -1,7 +1,7 @@
# To apply changes in this file run: ./gradlew clean setupDecompWorkspace idea
# Minecraft
public net.minecraft.client.Minecraft field_71428_T # timer
public-f net.minecraft.client.Minecraft field_71428_T # timer
public net.minecraft.client.Minecraft field_83002_am # debugCrashKeyPressTime
public net.minecraft.client.Minecraft field_71467_ac # rightClickDelayTimer
public net.minecraft.client.Minecraft field_71423_H # systemTime
@@ -21,23 +21,23 @@ public net.minecraft.client.Minecraft func_147112_ai()V # middleClickMouse
public net.minecraft.client.Minecraft func_147115_a(Z)V # sendClickBlockToController
# Packets
public net.minecraft.network.play.server.S0FPacketSpawnMob field_149043_l # dataWatcher
public net.minecraft.network.play.server.SPacketSpawnMob field_149043_l # dataManager
public net.minecraft.network.play.server.SPacketSpawnPlayer field_148960_i # watcher
#public net.minecraft.network.play.server.S38PacketPlayerListItem field_179964_d # profile
public net.minecraft.network.play.server.S1DPacketEntityEffect field_149434_a # entityId
public net.minecraft.network.play.server.S1CPacketEntityMetadata field_149379_a # entityId
public net.minecraft.network.play.server.S1CPacketEntityMetadata field_149378_b # list
public net.minecraft.network.play.server.S0BPacketAnimation field_148981_a # entityId
public net.minecraft.network.play.server.S02PacketChat field_179842_b # position
public net.minecraft.network.play.server.S19PacketEntityStatus field_149164_a # entityId
public net.minecraft.network.play.server.S0CPacketSpawnPlayer *
public net.minecraft.network.play.server.S18PacketEntityTeleport *
public net.minecraft.network.play.server.S14PacketEntity *
public net.minecraft.network.play.server.S19PacketEntityHeadLook *
public net.minecraft.network.play.server.S12PacketEntityVelocity *
public net.minecraft.network.play.server.S04PacketEntityEquipment *
public net.minecraft.network.play.server.S1BPacketEntityAttach *
public net.minecraft.network.play.server.S0DPacketCollectItem *
public net.minecraft.network.play.server.S13PacketDestroyEntities *
# public net.minecraft.network.play.server.S1DPacketEntityEffect field_149434_a # entityId
# public net.minecraft.network.play.server.S1CPacketEntityMetadata field_149379_a # entityId
# public net.minecraft.network.play.server.S1CPacketEntityMetadata field_149378_b # list
# public net.minecraft.network.play.server.S0BPacketAnimation field_148981_a # entityId
# public net.minecraft.network.play.server.S02PacketChat field_179842_b # position
# public net.minecraft.network.play.server.S19PacketEntityStatus field_149164_a # entityId
# public net.minecraft.network.play.server.S18PacketEntityTeleport *
# public net.minecraft.network.play.server.S14PacketEntity *
# public net.minecraft.network.play.server.S19PacketEntityHeadLook *
# public net.minecraft.network.play.server.S12PacketEntityVelocity *
# public net.minecraft.network.play.server.S04PacketEntityEquipment *
# public net.minecraft.network.play.server.S1BPacketEntityAttach *
# public net.minecraft.network.play.server.S0DPacketCollectItem *
# public net.minecraft.network.play.server.S13PacketDestroyEntities *
# GuiTextField
public net.minecraft.client.gui.GuiTextField field_146216_j # text
@@ -61,7 +61,7 @@ public net.minecraft.client.renderer.RenderGlobal field_175009_l # chunksToUpdat
public net.minecraft.client.renderer.RenderGlobal field_72755_R # renderInfos
public net.minecraft.client.renderer.RenderGlobal field_175009_l # chunksToUpdate
public net.minecraft.client.renderer.RenderGlobal field_72740_G # renderEntitiesStartupCounter
public net.minecraft.client.renderer.RenderGlobal func_174983_a(Lnet.minecraft.util.BlockPos;Lnet.minecraft.client.renderer.chunk.RenderChunk;)Z # isPositionInRenderChunk
# public net.minecraft.client.renderer.RenderGlobal func_174983_a(Lnet.minecraft.util.BlockPos;Lnet.minecraft.client.renderer.chunk.RenderChunk;)Z # isPositionInRenderChunk
public net.minecraft.client.renderer.RenderGlobal$ContainerLocalRenderInformation
#public net.minecraft.client.renderer.RenderGlobal$ContainerLocalRenderInformation field_178553_a # renderChunk
@@ -88,17 +88,17 @@ public net.minecraft.entity.player.EntityPlayer *
public net.minecraft.client.entity.EntityOtherPlayerMP *
# EffectRenderer
public net.minecraft.client.particle.EffectRenderer field_110737_b # particleTextures
public net.minecraft.client.particle.EffectRenderer field_78876_b # fxLayers
# public net.minecraft.client.particle.EffectRenderer field_110737_b # particleTextures
# public net.minecraft.client.particle.EffectRenderer field_78876_b # fxLayers
# IntegratedServer
public net.minecraft.server.integrated.IntegratedServer field_71348_o # isGamePaused
# GameSettings
public net.minecraft.client.settings.GameSettings field_151446_aD # mapSoundLevels
public-f net.minecraft.client.settings.GameSettings field_186714_aM # soundLevels
# Frustum
public net.minecraft.client.renderer.culling.Frustum field_78552_a # clippingHelper
public-f net.minecraft.client.renderer.culling.Frustum field_78552_a # clippingHelper
# CrashReportCategory
public net.minecraft.crash.CrashReportCategory field_85077_c # children
@@ -117,5 +117,12 @@ public net.minecraft.client.renderer.GlStateManager$FogState
public net.minecraft.client.renderer.GlStateManager$BooleanState
public net.minecraft.client.renderer.GlStateManager$BooleanState *
# EntityLivingBase
public net.minecraft.entity.EntityLivingBase field_184621_as # HAND_STATES
public net.minecraft.entity.EntityLivingBase field_184628_bn # activeItemStackUseCount
# EntityLivingBase
public net.minecraft.client.particle.Particle *
# Example
# public net.minecraft.package.ClassName func_some_id(Ljava/lang/Class;IZS)V # methodName

View File

@@ -255,8 +255,6 @@ replaymod.gui.editor.trim.description=Removes the beginning and end of a Replay
replaymod.gui.editor.connect.description=Connects multiple Replays in the specified order
replaymod.gui.editor.modify.description=Provides several filters to modify Replay Files
replaymod.gui.editor.trim.marker=Select Event Marker
replaymod.gui.editor.progress.title=Editing Replay File...
replaymod.gui.editor.progress.pleasewait=Please wait while the Replay is being edited.
@@ -426,18 +424,6 @@ replaymod.gui.rendersettings.presets.webm.custom=WEBM - Custom Bitrate
replaymod.gui.rendersettings.presets.mkv.lossless=MKV - Lossless
replaymod.gui.rendersettings.presets.png=PNG Sequence
replaymod.gui.rendersettings.antialiasing=Anti-Aliasing
replaymod.gui.rendersettings.antialiasing.none=None
replaymod.gui.rendersettings.antialiasing.x2=2x
replaymod.gui.rendersettings.antialiasing.x4=4x
replaymod.gui.rendersettings.antialiasing.x8=8x
#Render Queue GUI
replaymod.gui.renderqueue.title=Render Queue
replaymod.gui.renderqueue.open=Open Queue
replaymod.gui.renderqueue.jobname=Job Name:
replaymod.gui.renderqueue.add=Add current configuration
#Rendering GUI
replaymod.gui.rendering.title=Rendering Video
replaymod.gui.rendering.pause=Pause Rendering

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