Merge branch '1.9.4' into 1.10.2
ca2bbcaMerge branch '1.8' into 1.9.45b3284fUpdate jGui and ReplayStudio (fixes #33)61d6fd4Close replay file zip after recording (fixes #32)00bcc9eFix paths of files that are supposed to be in the mc data dir (fixes #40)e6a789dFix camera rotation when jumping in time (fixes #39)50b53fcAdd a way to register key bindings triggered every render tick (fixes #31)a817c73Prevent GUIs of other mods from opening during replayd64ef8bHide NPCs in the Player Overview GUI (fixes #29)1f2c05eFix spectating player entities past death and respawn (fixes #36)ad2893bFix vanilla bug caused by unremovable entities in the entityList of ClientWorld (fixes #29)75c0e7bFix particles being visible after world change when jumping in time (fixes #35)c934cb9Fix spawn player packet being sent twice6efbf91Handle F1 properly while mouse is visible in the replay overlay (fixes #30)9add2afDeselect keyframe when pressing "V" aka. sync timelines (fixes #27)
This commit is contained in:
Submodule ReplayStudio updated: 66ab00102f...be55f610ac
2
jGui
2
jGui
Submodule jGui updated: 7849fb3bb7...5c0ca7d7d8
@@ -9,6 +9,7 @@ import net.minecraft.util.ReportedException;
|
|||||||
import net.minecraftforge.fml.client.registry.ClientRegistry;
|
import net.minecraftforge.fml.client.registry.ClientRegistry;
|
||||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||||
import net.minecraftforge.fml.common.gameevent.InputEvent;
|
import net.minecraftforge.fml.common.gameevent.InputEvent;
|
||||||
|
import net.minecraftforge.fml.common.gameevent.TickEvent;
|
||||||
import org.lwjgl.input.Keyboard;
|
import org.lwjgl.input.Keyboard;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -19,16 +20,25 @@ import java.util.Map;
|
|||||||
public class KeyBindingRegistry {
|
public class KeyBindingRegistry {
|
||||||
private Map<String, KeyBinding> keyBindings = new HashMap<String, KeyBinding>();
|
private Map<String, KeyBinding> keyBindings = new HashMap<String, KeyBinding>();
|
||||||
private Multimap<KeyBinding, Runnable> keyBindingHandlers = ArrayListMultimap.create();
|
private Multimap<KeyBinding, Runnable> keyBindingHandlers = ArrayListMultimap.create();
|
||||||
|
private Multimap<KeyBinding, Runnable> repeatedKeyBindingHandlers = ArrayListMultimap.create();
|
||||||
private Multimap<Integer, Runnable> rawHandlers = ArrayListMultimap.create();
|
private Multimap<Integer, Runnable> rawHandlers = ArrayListMultimap.create();
|
||||||
|
|
||||||
public void registerKeyBinding(String name, int keyCode, Runnable whenPressed) {
|
public void registerKeyBinding(String name, int keyCode, Runnable whenPressed) {
|
||||||
|
keyBindingHandlers.put(registerKeyBinding(name, keyCode), whenPressed);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerRepeatedKeyBinding(String name, int keyCode, Runnable whenPressed) {
|
||||||
|
repeatedKeyBindingHandlers.put(registerKeyBinding(name, keyCode), whenPressed);
|
||||||
|
}
|
||||||
|
|
||||||
|
private KeyBinding registerKeyBinding(String name, int keyCode) {
|
||||||
KeyBinding keyBinding = keyBindings.get(name);
|
KeyBinding keyBinding = keyBindings.get(name);
|
||||||
if (keyBinding == null) {
|
if (keyBinding == null) {
|
||||||
keyBinding = new KeyBinding(name, keyCode, "replaymod.title");
|
keyBinding = new KeyBinding(name, keyCode, "replaymod.title");
|
||||||
keyBindings.put(name, keyBinding);
|
keyBindings.put(name, keyBinding);
|
||||||
ClientRegistry.registerKeyBinding(keyBinding);
|
ClientRegistry.registerKeyBinding(keyBinding);
|
||||||
}
|
}
|
||||||
keyBindingHandlers.put(keyBinding, whenPressed);
|
return keyBinding;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerRaw(int keyCode, Runnable whenPressed) {
|
public void registerRaw(int keyCode, Runnable whenPressed) {
|
||||||
@@ -45,20 +55,38 @@ public class KeyBindingRegistry {
|
|||||||
handleRaw();
|
handleRaw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void onTick(TickEvent.RenderTickEvent event) {
|
||||||
|
if (event.phase != TickEvent.Phase.START) return;
|
||||||
|
handleRepeatedKeyBindings();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleRepeatedKeyBindings() {
|
||||||
|
for (Map.Entry<KeyBinding, Collection<Runnable>> entry : repeatedKeyBindingHandlers.asMap().entrySet()) {
|
||||||
|
if (entry.getKey().isKeyDown()) {
|
||||||
|
invokeKeyBindingHandlers(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void handleKeyBindings() {
|
public void handleKeyBindings() {
|
||||||
for (Map.Entry<KeyBinding, Collection<Runnable>> entry : keyBindingHandlers.asMap().entrySet()) {
|
for (Map.Entry<KeyBinding, Collection<Runnable>> entry : keyBindingHandlers.asMap().entrySet()) {
|
||||||
while (entry.getKey().isPressed()) {
|
while (entry.getKey().isPressed()) {
|
||||||
for (final Runnable runnable : entry.getValue()) {
|
invokeKeyBindingHandlers(entry.getKey(), entry.getValue());
|
||||||
try {
|
}
|
||||||
runnable.run();
|
}
|
||||||
} catch (Throwable cause) {
|
}
|
||||||
CrashReport crashReport = CrashReport.makeCrashReport(cause, "Handling Key Binding");
|
|
||||||
CrashReportCategory category = crashReport.makeCategory("Key Binding");
|
private void invokeKeyBindingHandlers(KeyBinding keyBinding, Collection<Runnable> handlers) {
|
||||||
category.addCrashSection("Key Binding", entry.getKey());
|
for (final Runnable runnable : handlers) {
|
||||||
category.setDetail("Handler", runnable::toString);
|
try {
|
||||||
throw new ReportedException(crashReport);
|
runnable.run();
|
||||||
}
|
} catch (Throwable cause) {
|
||||||
}
|
CrashReport crashReport = CrashReport.makeCrashReport(cause, "Handling Key Binding");
|
||||||
|
CrashReportCategory category = crashReport.makeCategory("Key Binding");
|
||||||
|
category.addCrashSection("Key Binding", keyBinding);
|
||||||
|
category.setDetail("Handler", runnable::toString);
|
||||||
|
throw new ReportedException(crashReport);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,7 +82,8 @@ public class ReplayMod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public File getReplayFolder() throws IOException {
|
public File getReplayFolder() throws IOException {
|
||||||
File folder = new File(getSettingsRegistry().get(Setting.RECORDING_PATH));
|
String path = getSettingsRegistry().get(Setting.RECORDING_PATH);
|
||||||
|
File folder = new File(path.startsWith("./") ? getMinecraft().mcDataDir : null, path);
|
||||||
FileUtils.forceMkdir(folder);
|
FileUtils.forceMkdir(folder);
|
||||||
return folder;
|
return folder;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.replaymod.extras.playeroverview;
|
|||||||
import com.google.common.base.Optional;
|
import com.google.common.base.Optional;
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.replaymod.core.ReplayMod;
|
import com.replaymod.core.ReplayMod;
|
||||||
|
import com.replaymod.core.utils.Utils;
|
||||||
import com.replaymod.extras.Extra;
|
import com.replaymod.extras.Extra;
|
||||||
import com.replaymod.replay.ReplayModReplay;
|
import com.replaymod.replay.ReplayModReplay;
|
||||||
import com.replaymod.replay.camera.CameraEntity;
|
import com.replaymod.replay.camera.CameraEntity;
|
||||||
@@ -39,6 +40,16 @@ public class PlayerOverview implements Extra {
|
|||||||
return !(input instanceof CameraEntity); // Exclude the camera entity
|
return !(input instanceof CameraEntity); // Exclude the camera entity
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if (!Utils.isCtrlDown()) {
|
||||||
|
// Hide all players that have an UUID v2 (commonly used for NPCs)
|
||||||
|
Iterator<EntityPlayer> iter = players.iterator();
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
UUID uuid = iter.next().getGameProfile().getId();
|
||||||
|
if (uuid != null && uuid.version() == 2) {
|
||||||
|
iter.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
new PlayerOverviewGui(PlayerOverview.this, players).display();
|
new PlayerOverviewGui(PlayerOverview.this, players).display();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ import org.apache.logging.log4j.Logger;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static net.minecraft.client.Minecraft.getMinecraft;
|
||||||
|
|
||||||
@Mod(modid = ReplayModOnline.MOD_ID, useMetadata = true)
|
@Mod(modid = ReplayModOnline.MOD_ID, useMetadata = true)
|
||||||
public class ReplayModOnline {
|
public class ReplayModOnline {
|
||||||
public static final String MOD_ID = "replaymod-online";
|
public static final String MOD_ID = "replaymod-online";
|
||||||
@@ -101,7 +103,8 @@ public class ReplayModOnline {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public File getDownloadsFolder() {
|
public File getDownloadsFolder() {
|
||||||
return new File(core.getSettingsRegistry().get(Setting.DOWNLOAD_PATH));
|
String path = core.getSettingsRegistry().get(Setting.DOWNLOAD_PATH);
|
||||||
|
return new File(path.startsWith("./") ? getMinecraft().mcDataDir : null, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
public File getDownloadedFile(int id) {
|
public File getDownloadedFile(int id) {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.replaymod.recording.mixin;
|
|||||||
import com.replaymod.recording.handler.RecordingEventHandler;
|
import com.replaymod.recording.handler.RecordingEventHandler;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.network.NetHandlerPlayClient;
|
import net.minecraft.client.network.NetHandlerPlayClient;
|
||||||
|
import net.minecraft.client.network.NetworkPlayerInfo;
|
||||||
import net.minecraft.network.play.server.SPacketPlayerListItem;
|
import net.minecraft.network.play.server.SPacketPlayerListItem;
|
||||||
import net.minecraft.network.play.server.SPacketRespawn;
|
import net.minecraft.network.play.server.SPacketRespawn;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
@@ -11,12 +12,18 @@ import org.spongepowered.asm.mixin.injection.At;
|
|||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
@Mixin(NetHandlerPlayClient.class)
|
@Mixin(NetHandlerPlayClient.class)
|
||||||
public abstract class MixinNetHandlerPlayClient {
|
public abstract class MixinNetHandlerPlayClient {
|
||||||
|
|
||||||
@Shadow
|
@Shadow
|
||||||
private Minecraft gameController;
|
private Minecraft gameController;
|
||||||
|
|
||||||
|
@Shadow
|
||||||
|
private Map<UUID, NetworkPlayerInfo> playerInfoMap;
|
||||||
|
|
||||||
public RecordingEventHandler getRecordingEventHandler() {
|
public RecordingEventHandler getRecordingEventHandler() {
|
||||||
return ((RecordingEventHandler.RecordingEventSender) gameController.renderGlobal).getRecordingEventHandler();
|
return ((RecordingEventHandler.RecordingEventSender) gameController.renderGlobal).getRecordingEventHandler();
|
||||||
}
|
}
|
||||||
@@ -28,12 +35,17 @@ public abstract class MixinNetHandlerPlayClient {
|
|||||||
* @param packet The packet
|
* @param packet The packet
|
||||||
* @param ci Callback info
|
* @param ci Callback info
|
||||||
*/
|
*/
|
||||||
@Inject(method = "handlePlayerListItem", at=@At("RETURN"))
|
@Inject(method = "handlePlayerListItem", at=@At("HEAD"))
|
||||||
public void recordOwnJoin(SPacketPlayerListItem packet, CallbackInfo ci) {
|
public void recordOwnJoin(SPacketPlayerListItem packet, CallbackInfo ci) {
|
||||||
|
if (gameController.thePlayer == null) return;
|
||||||
|
|
||||||
RecordingEventHandler handler = getRecordingEventHandler();
|
RecordingEventHandler handler = getRecordingEventHandler();
|
||||||
if (handler != null && packet.getAction() == SPacketPlayerListItem.Action.ADD_PLAYER) {
|
if (handler != null && packet.getAction() == SPacketPlayerListItem.Action.ADD_PLAYER) {
|
||||||
for (SPacketPlayerListItem.AddPlayerData data : packet.getEntries()) {
|
for (SPacketPlayerListItem.AddPlayerData data : packet.getEntries()) {
|
||||||
if (data.getProfile().getId().equals(gameController.thePlayer.getGameProfile().getId())) {
|
if (data.getProfile() == null || data.getProfile().getId() == null) continue;
|
||||||
|
// Only add spawn packet for our own player and only if he isn't known yet
|
||||||
|
if (data.getProfile().getId().equals(Minecraft.getMinecraft().thePlayer.getGameProfile().getId())
|
||||||
|
&& !playerInfoMap.containsKey(data.getProfile().getId())) {
|
||||||
handler.onPlayerJoin();
|
handler.onPlayerJoin();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,6 +132,7 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
|
|||||||
synchronized (replayFile) {
|
synchronized (replayFile) {
|
||||||
try {
|
try {
|
||||||
replayFile.save();
|
replayFile.save();
|
||||||
|
replayFile.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error("Saving replay file:", e);
|
logger.error("Saving replay file:", e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,17 @@
|
|||||||
package com.replaymod.render;
|
package com.replaymod.render;
|
||||||
|
|
||||||
import com.replaymod.core.ReplayMod;
|
import com.replaymod.core.ReplayMod;
|
||||||
|
import net.minecraft.crash.CrashReport;
|
||||||
|
import net.minecraft.util.ReportedException;
|
||||||
import net.minecraftforge.common.config.Configuration;
|
import net.minecraftforge.common.config.Configuration;
|
||||||
import net.minecraftforge.fml.common.Mod;
|
import net.minecraftforge.fml.common.Mod;
|
||||||
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
|
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
|
||||||
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
@Mod(modid = ReplayModRender.MOD_ID, useMetadata = true)
|
@Mod(modid = ReplayModRender.MOD_ID, useMetadata = true)
|
||||||
public class ReplayModRender {
|
public class ReplayModRender {
|
||||||
public static final String MOD_ID = "replaymod-render";
|
public static final String MOD_ID = "replaymod-render";
|
||||||
@@ -32,6 +38,17 @@ public class ReplayModRender {
|
|||||||
core.getSettingsRegistry().register(Setting.class);
|
core.getSettingsRegistry().register(Setting.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public File getVideoFolder() {
|
||||||
|
String path = core.getSettingsRegistry().get(Setting.RENDER_PATH);
|
||||||
|
File folder = new File(path.startsWith("./") ? core.getMinecraft().mcDataDir : null, path);
|
||||||
|
try {
|
||||||
|
FileUtils.forceMkdir(folder);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ReportedException(CrashReport.makeCrashReport(e, "Cannot create video folder."));
|
||||||
|
}
|
||||||
|
return folder;
|
||||||
|
}
|
||||||
|
|
||||||
public Configuration getConfiguration() {
|
public Configuration getConfiguration() {
|
||||||
return configuration;
|
return configuration;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,8 @@ public class VideoWriter implements FrameConsumer<RGBFrame> {
|
|||||||
System.out.println("Starting " + settings.getExportCommand() + " with args: " + commandArgs);
|
System.out.println("Starting " + settings.getExportCommand() + " with args: " + commandArgs);
|
||||||
String[] cmdline = new CommandLine(executable).addArguments(commandArgs).toStrings();
|
String[] cmdline = new CommandLine(executable).addArguments(commandArgs).toStrings();
|
||||||
process = new ProcessBuilder(cmdline).directory(outputFolder).start();
|
process = new ProcessBuilder(cmdline).directory(outputFolder).start();
|
||||||
OutputStream exportLogOut = new TeeOutputStream(new FileOutputStream("export.log"), ffmpegLog);
|
File exportLogFile = new File(Minecraft.getMinecraft().mcDataDir, "export.log");
|
||||||
|
OutputStream exportLogOut = new TeeOutputStream(new FileOutputStream(exportLogFile), ffmpegLog);
|
||||||
new StreamPipe(process.getInputStream(), exportLogOut).start();
|
new StreamPipe(process.getInputStream(), exportLogOut).start();
|
||||||
new StreamPipe(process.getErrorStream(), exportLogOut).start();
|
new StreamPipe(process.getErrorStream(), exportLogOut).start();
|
||||||
outputStream = process.getOutputStream();
|
outputStream = process.getOutputStream();
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import com.google.gson.GsonBuilder;
|
|||||||
import com.google.gson.InstanceCreator;
|
import com.google.gson.InstanceCreator;
|
||||||
import com.replaymod.render.RenderSettings;
|
import com.replaymod.render.RenderSettings;
|
||||||
import com.replaymod.render.ReplayModRender;
|
import com.replaymod.render.ReplayModRender;
|
||||||
import com.replaymod.render.Setting;
|
|
||||||
import com.replaymod.render.rendering.VideoRenderer;
|
import com.replaymod.render.rendering.VideoRenderer;
|
||||||
import com.replaymod.replay.ReplayHandler;
|
import com.replaymod.replay.ReplayHandler;
|
||||||
import com.replaymod.replaystudio.pathing.path.Timeline;
|
import com.replaymod.replaystudio.pathing.path.Timeline;
|
||||||
@@ -410,7 +409,7 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
|
|||||||
|
|
||||||
private File generateOutputFile(RenderSettings.EncodingPreset encodingPreset) {
|
private File generateOutputFile(RenderSettings.EncodingPreset encodingPreset) {
|
||||||
String fileName = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss").format(new Date());
|
String fileName = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss").format(new Date());
|
||||||
File folder = new File(ReplayModRender.instance.getCore().getSettingsRegistry().get(Setting.RENDER_PATH));
|
File folder = ReplayModRender.instance.getVideoFolder();
|
||||||
return new File(folder, fileName + "." + encodingPreset.getFileExtension());
|
return new File(folder, fileName + "." + encodingPreset.getFileExtension());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -280,7 +280,7 @@ public class ReplayHandler {
|
|||||||
CameraEntity cam = getCameraEntity();
|
CameraEntity cam = getCameraEntity();
|
||||||
if (cam != null) {
|
if (cam != null) {
|
||||||
targetCameraPosition = new Location(cam.posX, cam.posY, cam.posZ,
|
targetCameraPosition = new Location(cam.posX, cam.posY, cam.posZ,
|
||||||
cam.rotationPitch, cam.rotationYaw);
|
cam.rotationYaw, cam.rotationPitch);
|
||||||
} else {
|
} else {
|
||||||
targetCameraPosition = null;
|
targetCameraPosition = null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,7 +69,11 @@ public class CameraEntity extends EntityPlayerSP {
|
|||||||
public CameraEntity(Minecraft mcIn, World worldIn, NetHandlerPlayClient netHandlerPlayClient, StatisticsManager statisticsManager) {
|
public CameraEntity(Minecraft mcIn, World worldIn, NetHandlerPlayClient netHandlerPlayClient, StatisticsManager statisticsManager) {
|
||||||
super(mcIn, worldIn, netHandlerPlayClient, statisticsManager);
|
super(mcIn, worldIn, netHandlerPlayClient, statisticsManager);
|
||||||
MinecraftForge.EVENT_BUS.register(eventHandler);
|
MinecraftForge.EVENT_BUS.register(eventHandler);
|
||||||
cameraController = ReplayModReplay.instance.createCameraController(this);
|
if (ReplayModReplay.instance.getReplayHandler().getSpectatedUUID() == null) {
|
||||||
|
cameraController = ReplayModReplay.instance.createCameraController(this);
|
||||||
|
} else {
|
||||||
|
cameraController = new SpectatorCameraController(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -151,7 +155,9 @@ public class CameraEntity extends EntityPlayerSP {
|
|||||||
// This is important if the spectated player respawns as their
|
// This is important if the spectated player respawns as their
|
||||||
// entity is recreated and we have to spectate a new entity
|
// entity is recreated and we have to spectate a new entity
|
||||||
UUID spectating = ReplayModReplay.instance.getReplayHandler().getSpectatedUUID();
|
UUID spectating = ReplayModReplay.instance.getReplayHandler().getSpectatedUUID();
|
||||||
if (spectating != null && (view.getUniqueID() != spectating || view.worldObj != worldObj)) {
|
if (spectating != null && (view.getUniqueID() != spectating
|
||||||
|
|| view.worldObj != worldObj)
|
||||||
|
|| worldObj.getEntityByID(view.getEntityId()) != view) {
|
||||||
view = worldObj.getPlayerEntityByUUID(spectating);
|
view = worldObj.getPlayerEntityByUUID(spectating);
|
||||||
if (view != null) {
|
if (view != null) {
|
||||||
mc.setRenderViewEntity(view);
|
mc.setRenderViewEntity(view);
|
||||||
@@ -322,6 +328,12 @@ public class CameraEntity extends EntityPlayerSP {
|
|||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void openGui(Object mod, int modGuiId, World world, int x, int y, int z) {
|
||||||
|
// Do not open any block GUIs for the camera entities
|
||||||
|
// Note: Vanilla GUIs are filtered out on a packet level, this only applies to mod GUIs
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setDead() {
|
public void setDead() {
|
||||||
super.setDead();
|
super.setDead();
|
||||||
@@ -394,7 +406,11 @@ public class CameraEntity extends EntityPlayerSP {
|
|||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
public void onSettingsChanged(SettingsChangedEvent event) {
|
public void onSettingsChanged(SettingsChangedEvent event) {
|
||||||
if (event.getKey() == Setting.CAMERA) {
|
if (event.getKey() == Setting.CAMERA) {
|
||||||
cameraController = ReplayModReplay.instance.createCameraController(CameraEntity.this);
|
if (ReplayModReplay.instance.getReplayHandler().getSpectatedUUID() == null) {
|
||||||
|
cameraController = ReplayModReplay.instance.createCameraController(CameraEntity.this);
|
||||||
|
} else {
|
||||||
|
cameraController = new SpectatorCameraController(CameraEntity.this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import net.minecraft.client.settings.GameSettings;
|
|||||||
import net.minecraftforge.common.MinecraftForge;
|
import net.minecraftforge.common.MinecraftForge;
|
||||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||||
import net.minecraftforge.fml.common.gameevent.InputEvent;
|
import net.minecraftforge.fml.common.gameevent.InputEvent;
|
||||||
|
import org.lwjgl.input.Keyboard;
|
||||||
import org.lwjgl.util.ReadableDimension;
|
import org.lwjgl.util.ReadableDimension;
|
||||||
import org.lwjgl.util.ReadablePoint;
|
import org.lwjgl.util.ReadablePoint;
|
||||||
import org.lwjgl.util.WritablePoint;
|
import org.lwjgl.util.WritablePoint;
|
||||||
@@ -153,6 +154,23 @@ public class GuiReplayOverlay extends AbstractGuiOverlay<GuiReplayOverlay> {
|
|||||||
setMouseVisible(true);
|
setMouseVisible(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (Keyboard.getEventKeyState()) {
|
||||||
|
// Handle the F1 key binding while the overlay is opened as a gui screen
|
||||||
|
if (isMouseVisible() && Keyboard.getEventKey() == Keyboard.KEY_F1) {
|
||||||
|
gameSettings.hideGUI = !gameSettings.hideGUI;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw(GuiRenderer renderer, ReadableDimension size, RenderInfo renderInfo) {
|
||||||
|
// Do not render overlay when user pressed F1 and we are not currently in some popup
|
||||||
|
if (getMinecraft().gameSettings.hideGUI && isAllowUserInput()) {
|
||||||
|
// Note that this only applies to when the mouse is visible, otherwise
|
||||||
|
// the draw method isn't called in the first place
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
super.draw(renderer, size, renderInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package com.replaymod.replay.mixin;
|
||||||
|
|
||||||
|
import net.minecraft.client.multiplayer.WorldClient;
|
||||||
|
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.CallbackInfo;
|
||||||
|
|
||||||
|
@Mixin(WorldClient.class)
|
||||||
|
public abstract class MixinWorldClient {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fixes a bug in vanilla Minecraft that leaves entities remaining in the entityList even after respawn.
|
||||||
|
* The entityList in WorldClient is a Set that assumes that the entity ID of its entities does not change.
|
||||||
|
* However in {@link WorldClient#addEntityToWorld(int, Entity)}, the entity passed in is first added to the
|
||||||
|
* set and subsequently its id is changed, leaving it stuck in he Set. That is the buggy method.
|
||||||
|
* This wouldn't be too much of a problem if the entity had the correct id to begin with, however the handler for
|
||||||
|
* the spawn player packet creates an EntityOtherPlayerMP which takes its id from a counter and then passes it to
|
||||||
|
* this method with the wrong id.
|
||||||
|
* This mixin fixes the id of the entity before it is added to the set instead of right after.
|
||||||
|
* The original id change right after is not changed, however it should not have any effect.
|
||||||
|
* @param entityId The id to be set for the entity
|
||||||
|
* @param entity The entity to be added
|
||||||
|
* @param ci Callback info
|
||||||
|
*/
|
||||||
|
@Inject(method = "addEntityToWorld", at=@At("HEAD"))
|
||||||
|
public void replayModReplay_fix_addEntityToWorld(int entityId, Entity entity, CallbackInfo ci) {
|
||||||
|
entity.setEntityId(entityId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -431,7 +431,7 @@ public class GuiPathing {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
core.getKeyBindingRegistry().registerKeyBinding("replaymod.input.synctimeline", Keyboard.KEY_V, () -> {
|
core.getKeyBindingRegistry().registerRepeatedKeyBinding("replaymod.input.synctimeline", Keyboard.KEY_V, () -> {
|
||||||
// Current replay time
|
// Current replay time
|
||||||
int time = replayHandler.getReplaySender().currentTimeStamp();
|
int time = replayHandler.getReplaySender().currentTimeStamp();
|
||||||
// Position of the cursor
|
// Position of the cursor
|
||||||
@@ -452,6 +452,8 @@ public class GuiPathing {
|
|||||||
int cursorPassed = (int) (timePassed / speed);
|
int cursorPassed = (int) (timePassed / speed);
|
||||||
// Move cursor to new position
|
// Move cursor to new position
|
||||||
timeline.setCursorPosition(keyframeCursor + cursorPassed);
|
timeline.setCursorPosition(keyframeCursor + cursorPassed);
|
||||||
|
// Deselect keyframe to allow the user to add a new one right away
|
||||||
|
mod.setSelectedKeyframe(null);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
"package": "com.replaymod.replay.mixin",
|
"package": "com.replaymod.replay.mixin",
|
||||||
"mixins": [
|
"mixins": [
|
||||||
"MixinGuiSpectator",
|
"MixinGuiSpectator",
|
||||||
|
"MixinParticleManager",
|
||||||
"MixinPlayerControllerMP",
|
"MixinPlayerControllerMP",
|
||||||
"MixinRenderArmorStand",
|
"MixinRenderArmorStand",
|
||||||
"MixinRenderArrow",
|
"MixinRenderArrow",
|
||||||
@@ -10,7 +11,8 @@
|
|||||||
"MixinRenderLivingBase",
|
"MixinRenderLivingBase",
|
||||||
"MixinRenderManager",
|
"MixinRenderManager",
|
||||||
"MixinTileEntityEndPortalRenderer",
|
"MixinTileEntityEndPortalRenderer",
|
||||||
"MixinViewFrustum"
|
"MixinViewFrustum",
|
||||||
|
"MixinWorldClient"
|
||||||
],
|
],
|
||||||
"server": [],
|
"server": [],
|
||||||
"client": [],
|
"client": [],
|
||||||
|
|||||||
Reference in New Issue
Block a user