First Commit
This commit is contained in:
76
src/main/java/eu/crushedpixel/replaymod/ReplayMod.java
Normal file
76
src/main/java/eu/crushedpixel/replaymod/ReplayMod.java
Normal file
@@ -0,0 +1,76 @@
|
||||
package eu.crushedpixel.replaymod;
|
||||
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.common.config.Configuration;
|
||||
import net.minecraftforge.common.config.Property;
|
||||
import net.minecraftforge.fml.common.FMLCommonHandler;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.Mod.EventHandler;
|
||||
import net.minecraftforge.fml.common.Mod.Instance;
|
||||
import net.minecraftforge.fml.common.SidedProxy;
|
||||
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
|
||||
import net.minecraftforge.fml.common.event.FMLPostInitializationEvent;
|
||||
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
|
||||
import eu.crushedpixel.replaymod.events.GuiEventHandler;
|
||||
import eu.crushedpixel.replaymod.events.GuiReplayOverlay;
|
||||
import eu.crushedpixel.replaymod.events.RecordingHandler;
|
||||
import eu.crushedpixel.replaymod.events.ReplayTickHandler;
|
||||
import eu.crushedpixel.replaymod.recording.ConnectionEventHandler;
|
||||
import eu.crushedpixel.replaymod.settings.ReplaySettings;
|
||||
|
||||
@Mod(modid = ReplayMod.MODID, version = ReplayMod.VERSION)
|
||||
public class ReplayMod
|
||||
{
|
||||
|
||||
//TODO: Set ReplayHandler replaying to false when replay is exited
|
||||
|
||||
public static final String MODID = "replaymod";
|
||||
public static final String VERSION = "0.0.1";
|
||||
|
||||
public static GuiReplayOverlay overlay = new GuiReplayOverlay();
|
||||
|
||||
public static ReplaySettings replaySettings = new ReplaySettings(0, true, true, true, false);
|
||||
public static Configuration config;
|
||||
|
||||
public static int PLAYER_ID = -1;
|
||||
|
||||
// The instance of your mod that Forge uses.
|
||||
@Instance(value = "ReplayModID")
|
||||
public static ReplayMod instance;
|
||||
|
||||
@EventHandler
|
||||
public void preInit(FMLPreInitializationEvent event) {
|
||||
config = new Configuration(event.getSuggestedConfigurationFile());
|
||||
config.load();
|
||||
|
||||
Property recServer = config.get("settings", "enableRecordingServer", true, "Defines whether a recording should be started upon joining a server.");
|
||||
Property recSP = config.get("settings", "enableRecordingSingleplayer", true, "Defines whether a recording should be started upon joining a singleplayer world.");
|
||||
Property maxFileSize = config.get("settings", "maximumFileSize", 0, "The maximum File size (in MB) of a recording. 0 means unlimited.");
|
||||
Property showNot = ReplayMod.instance.config.get("settings", "showNotifications", true, "Defines whether notifications should be sent to the player.");
|
||||
Property linear = ReplayMod.instance.config.get("settings", "forceLinearPath", false, "Defines whether travelling paths should be linear instead of interpolated.");
|
||||
|
||||
replaySettings = new ReplaySettings(maxFileSize.getInt(0), recServer.getBoolean(true), recSP.getBoolean(true), showNot.getBoolean(true), linear.getBoolean(false));
|
||||
|
||||
config.save();
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void init(FMLInitializationEvent event) {
|
||||
FMLCommonHandler.instance().bus().register(new ConnectionEventHandler());
|
||||
MinecraftForge.EVENT_BUS.register(new GuiEventHandler());
|
||||
ReplayTickHandler tickHandler = new ReplayTickHandler();
|
||||
FMLCommonHandler.instance().bus().register(tickHandler);
|
||||
MinecraftForge.EVENT_BUS.register(tickHandler);
|
||||
|
||||
RecordingHandler rh = new RecordingHandler();
|
||||
FMLCommonHandler.instance().bus().register(rh);
|
||||
MinecraftForge.EVENT_BUS.register(rh);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void postInit(FMLPostInitializationEvent event) {
|
||||
overlay = new GuiReplayOverlay();
|
||||
FMLCommonHandler.instance().bus().register(overlay);
|
||||
MinecraftForge.EVENT_BUS.register(overlay);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package eu.crushedpixel.replaymod.chat;
|
||||
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.entity.EntityPlayerSP;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.util.ChatComponentText;
|
||||
import net.minecraft.util.IChatComponent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
import eu.crushedpixel.replaymod.ReplayMod;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class ChatMessageRequests {
|
||||
|
||||
public enum ChatMessageType {
|
||||
INFORMATION, WARNING;
|
||||
}
|
||||
|
||||
private static boolean active = true;
|
||||
private static boolean alive = true;
|
||||
|
||||
private static Queue<IChatComponent> requests = new ConcurrentLinkedQueue<IChatComponent>();
|
||||
private static String prefix = "§8[§6Replay Mod§8]§r ";
|
||||
|
||||
private static EntityPlayerSP player = null;
|
||||
|
||||
public static Thread t = new Thread(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while(alive) {
|
||||
while(active) {
|
||||
try {
|
||||
while(player == null) {
|
||||
if(!alive) {
|
||||
break;
|
||||
}
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
player = Minecraft.getMinecraft().thePlayer;
|
||||
} catch(Exception e) {}
|
||||
}
|
||||
|
||||
player.addChatComponentMessage(requests.poll());
|
||||
Thread.sleep(100);
|
||||
} catch(Exception e) {}
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
static {
|
||||
t.start();
|
||||
}
|
||||
|
||||
public static void addChatMessage(String message, ChatMessageType type) {
|
||||
if(ReplayMod.replaySettings.isShowNotifications()) {
|
||||
message = prefix+toColor(message, type);
|
||||
ChatComponentText cct = new ChatComponentText(message);
|
||||
requests.add(cct);
|
||||
}
|
||||
}
|
||||
|
||||
private static String toColor(String message, ChatMessageType type) {
|
||||
if(type == ChatMessageType.INFORMATION) {
|
||||
return "§2"+message;
|
||||
} else if(type == ChatMessageType.WARNING) {
|
||||
return "§c"+message;
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
public static void stop() {
|
||||
active = false;
|
||||
}
|
||||
|
||||
public static void initialize() {
|
||||
active = true;
|
||||
requests.clear();
|
||||
if(ReplayMod.replaySettings.isShowNotifications()) {
|
||||
} else {
|
||||
System.out.println("Chat messages are disabled");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
package eu.crushedpixel.replaymod.entities;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.MathHelper;
|
||||
import net.minecraft.util.MovingObjectPosition;
|
||||
import net.minecraft.util.Vec3;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.WorldSettings.GameType;
|
||||
|
||||
import org.lwjgl.Sys;
|
||||
|
||||
import eu.crushedpixel.replaymod.holders.Position;
|
||||
import eu.crushedpixel.replaymod.replay.LesserDataWatcher;
|
||||
import eu.crushedpixel.replaymod.replay.ReplayHandler;
|
||||
|
||||
public class CameraEntity extends Entity {
|
||||
|
||||
private Vec3 direction;
|
||||
private double motion;
|
||||
|
||||
private Field drawBlockOutline;
|
||||
|
||||
private static final double SPEED = 10; //2 blocks per second
|
||||
|
||||
private double decay = 4; //decays by 75% per second;
|
||||
|
||||
private long lastCall = 0;
|
||||
|
||||
//frac = time since last tick
|
||||
public void updateMovement() {
|
||||
Minecraft mc = Minecraft.getMinecraft();
|
||||
if(ReplayHandler.getCameraEntity() != null && mc.thePlayer != null) {
|
||||
//Aligns the particle rotation
|
||||
mc.thePlayer.rotationPitch = ReplayHandler.getCameraEntity().rotationPitch;
|
||||
mc.thePlayer.rotationYaw = ReplayHandler.getCameraEntity().rotationYaw;
|
||||
|
||||
//removes water/suffocation/shadow overlays in screen
|
||||
mc.thePlayer.posX = 0;
|
||||
mc.thePlayer.posY = 500;
|
||||
mc.thePlayer.posZ = 0;
|
||||
}
|
||||
|
||||
if(direction == null || motion < 0.1) {
|
||||
lastCall = Sys.getTime();
|
||||
return;
|
||||
}
|
||||
|
||||
long frac = Sys.getTime() - lastCall;
|
||||
|
||||
if(frac == 0) return;
|
||||
|
||||
Vec3 movement = direction.normalize();
|
||||
double factor = motion*(frac/1000D);
|
||||
|
||||
moveRelative(movement.xCoord*factor, movement.yCoord*factor, movement.zCoord*factor);
|
||||
|
||||
double decFac = Math.max(0, 1-(decay*(frac/1000D)));
|
||||
motion *= decFac;
|
||||
|
||||
lastCall = Sys.getTime();
|
||||
}
|
||||
|
||||
public void setDirection(float pitch, float yaw) {
|
||||
this.setRotation(yaw, pitch);
|
||||
}
|
||||
|
||||
public void setMovement(MoveDirection dir) {
|
||||
Vec3 oldDir = direction;
|
||||
|
||||
switch(dir) {
|
||||
case BACKWARD:
|
||||
direction = this.getVectorForRotation(-rotationPitch, rotationYaw-180);
|
||||
break;
|
||||
case DOWN:
|
||||
direction = this.getVectorForRotation(90, 0);
|
||||
break;
|
||||
case FORWARD:
|
||||
direction = this.getVectorForRotation(rotationPitch, rotationYaw);
|
||||
break;
|
||||
case LEFT:
|
||||
direction = this.getVectorForRotation(0, rotationYaw-90);
|
||||
break;
|
||||
case RIGHT:
|
||||
direction = this.getVectorForRotation(0, rotationYaw+90);
|
||||
break;
|
||||
case UP:
|
||||
direction = this.getVectorForRotation(-90, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
if(oldDir != null) direction = direction.normalize().add(new Vec3(oldDir.xCoord*(motion/4f), oldDir.yCoord*(motion/4f), oldDir.zCoord*(motion/4f)).normalize());
|
||||
|
||||
this.motion = SPEED;
|
||||
}
|
||||
|
||||
public void moveAbsolute(double x, double y, double z) {
|
||||
if(ReplayHandler.isReplaying()) return;
|
||||
this.posX = x;
|
||||
this.posY = y;
|
||||
this.posZ = z;
|
||||
this.prevPosX = this.posX;
|
||||
this.prevPosY = this.posY;
|
||||
this.prevPosZ = this.posZ;
|
||||
this.lastTickPosX = this.posX;
|
||||
this.lastTickPosY = this.posY;
|
||||
this.lastTickPosZ = this.posZ;
|
||||
}
|
||||
|
||||
public void moveRelative(double x, double y, double z) {
|
||||
if(ReplayHandler.isReplaying()) return;
|
||||
this.posX += x;
|
||||
this.posY += y;
|
||||
this.posZ += z;
|
||||
this.prevPosX = this.posX;
|
||||
this.prevPosY = this.posY;
|
||||
this.prevPosZ = this.posZ;
|
||||
this.lastTickPosX = this.posX;
|
||||
this.lastTickPosY = this.posY;
|
||||
this.lastTickPosZ = this.posZ;
|
||||
}
|
||||
|
||||
public void movePath(Position pos) {
|
||||
this.posX = pos.getX();
|
||||
this.posY = pos.getY();
|
||||
this.posZ = pos.getZ();
|
||||
this.rotationPitch = pos.getPitch();
|
||||
this.rotationYaw = pos.getYaw();
|
||||
this.prevPosX = this.posX;
|
||||
this.prevPosY = this.posY;
|
||||
this.prevPosZ = this.posZ;
|
||||
this.prevRotationPitch = this.rotationPitch;
|
||||
this.prevRotationYaw = this.rotationYaw;
|
||||
this.lastTickPosX = this.posX;
|
||||
this.lastTickPosY = this.posY;
|
||||
this.lastTickPosZ = this.posZ;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void entityInit() {
|
||||
this.dataWatcher = new LesserDataWatcher(this);
|
||||
}
|
||||
|
||||
|
||||
public CameraEntity(World worldIn) {
|
||||
super(worldIn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAngles(float yaw, float pitch)
|
||||
{
|
||||
this.rotationYaw = (float)((double)this.rotationYaw + (double)yaw * 0.15D);
|
||||
this.rotationPitch = (float)((double)this.rotationPitch - (double)pitch * 0.15D);
|
||||
this.rotationPitch = MathHelper.clamp_float(this.rotationPitch, -90.0F, 90.0F);
|
||||
this.prevRotationPitch = this.rotationPitch;
|
||||
this.prevRotationYaw = this.rotationYaw;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public MovingObjectPosition rayTrace(double p_174822_1_, float p_174822_3_) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBePushed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createRunningParticles() {}
|
||||
|
||||
@Override
|
||||
public boolean canBeCollidedWith() {
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public boolean canRenderOnFire() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
@Override
|
||||
public boolean isSpectator() {
|
||||
return true;
|
||||
}
|
||||
*/
|
||||
|
||||
@Override
|
||||
protected void readEntityFromNBT(NBTTagCompound tagCompund) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeEntityToNBT(NBTTagCompound tagCompound) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
public enum MoveDirection {
|
||||
UP, DOWN, LEFT, RIGHT, FORWARD, BACKWARD;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package eu.crushedpixel.replaymod.events;
|
||||
|
||||
import eu.crushedpixel.replaymod.gui.GuiCustomMainMenu;
|
||||
import eu.crushedpixel.replaymod.gui.GuiCustomOptions;
|
||||
import eu.crushedpixel.replaymod.gui.GuiExitReplay;
|
||||
import eu.crushedpixel.replaymod.replay.ReplayHandler;
|
||||
import net.minecraft.client.gui.GuiChat;
|
||||
import net.minecraft.client.gui.GuiDisconnected;
|
||||
import net.minecraft.client.gui.GuiIngameMenu;
|
||||
import net.minecraft.client.gui.GuiMainMenu;
|
||||
import net.minecraft.client.gui.GuiOptions;
|
||||
import net.minecraft.client.gui.inventory.GuiInventory;
|
||||
import net.minecraftforge.client.event.GuiOpenEvent;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
public class GuiEventHandler {
|
||||
|
||||
@SubscribeEvent
|
||||
public void onGui(GuiOpenEvent event) {
|
||||
if(event.gui instanceof GuiMainMenu) {
|
||||
event.gui = new GuiCustomMainMenu();
|
||||
} else if(event.gui instanceof GuiOptions) {
|
||||
GuiOptions go = (GuiOptions)event.gui;
|
||||
GuiCustomOptions gco = new GuiCustomOptions(GuiCustomOptions.getGuiScreen(go), GuiCustomOptions.getGameSettings(go));
|
||||
event.gui = gco;
|
||||
}
|
||||
|
||||
else if(event.gui instanceof GuiChat || event.gui instanceof GuiInventory) {
|
||||
if(ReplayHandler.replayActive()) {
|
||||
event.setCanceled(true);
|
||||
}
|
||||
}
|
||||
|
||||
else if(event.gui instanceof GuiIngameMenu) {
|
||||
if(ReplayHandler.replayActive()) {
|
||||
event.gui = new GuiExitReplay();
|
||||
}
|
||||
}
|
||||
|
||||
else if(event.gui instanceof GuiDisconnected) {
|
||||
if(!ReplayHandler.replayActive() && System.currentTimeMillis() - ReplayHandler.lastExit < 5000) {
|
||||
event.setCanceled(true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,810 @@
|
||||
package eu.crushedpixel.replaymod.events;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.Gui;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.gui.ScaledResolution;
|
||||
import net.minecraft.client.renderer.EntityRenderer;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.settings.KeyBinding;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.client.event.MouseEvent;
|
||||
import net.minecraftforge.client.event.RenderGameOverlayEvent;
|
||||
import net.minecraftforge.client.event.RenderWorldLastEvent;
|
||||
import net.minecraftforge.fml.client.FMLClientHandler;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.gameevent.InputEvent.KeyInputEvent;
|
||||
import net.minecraftforge.fml.common.gameevent.TickEvent;
|
||||
|
||||
import org.lwjgl.input.Mouse;
|
||||
import org.lwjgl.opengl.Display;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import eu.crushedpixel.replaymod.entities.CameraEntity;
|
||||
import eu.crushedpixel.replaymod.entities.CameraEntity.MoveDirection;
|
||||
import eu.crushedpixel.replaymod.gui.GuiMouseInput;
|
||||
import eu.crushedpixel.replaymod.gui.GuiReplaySpeedSlider;
|
||||
import eu.crushedpixel.replaymod.holders.Keyframe;
|
||||
import eu.crushedpixel.replaymod.holders.Position;
|
||||
import eu.crushedpixel.replaymod.holders.PositionKeyframe;
|
||||
import eu.crushedpixel.replaymod.holders.TimeKeyframe;
|
||||
import eu.crushedpixel.replaymod.reflection.MCPNames;
|
||||
import eu.crushedpixel.replaymod.replay.ReplayHandler;
|
||||
import eu.crushedpixel.replaymod.replay.ReplayProcess;
|
||||
|
||||
public class GuiReplayOverlay extends Gui {
|
||||
|
||||
private Minecraft mc = Minecraft.getMinecraft();
|
||||
|
||||
private int sliderX = 35;
|
||||
private int sliderY = 10;
|
||||
|
||||
private int timelineX = sliderX+100+5;
|
||||
private int realTimelineX = 10 + 3*25;
|
||||
private int realTimelineY = 33+10;
|
||||
|
||||
private int realTimePosition = 0;
|
||||
|
||||
private int ppButtonX = 10;
|
||||
private int ppButtonY = 10;
|
||||
|
||||
private int r_ppButtonX = 10;
|
||||
private int r_ppButtonY = realTimelineY+1;
|
||||
|
||||
private int place_ButtonX = 35;
|
||||
private int place_ButtonY = realTimelineY+1;
|
||||
|
||||
private int time_ButtonX = 60;
|
||||
private int time_ButtonY = realTimelineY+1;
|
||||
|
||||
private long lastSystemTime = System.currentTimeMillis();
|
||||
|
||||
private ResourceLocation guiLocation = new ResourceLocation("replaymod", "replay_gui.png");
|
||||
private ResourceLocation keyframeLocation = new ResourceLocation("replaymod", "extended_gui.png");
|
||||
private ResourceLocation timelineLocation = new ResourceLocation("replaymod", "timeline_icons.png");
|
||||
|
||||
private GuiReplaySpeedSlider speedSlider;
|
||||
|
||||
private boolean mouseDown = false;
|
||||
|
||||
private Field drawBlockOutline;
|
||||
|
||||
public GuiReplayOverlay() {
|
||||
try {
|
||||
drawBlockOutline = EntityRenderer.class.getDeclaredField(MCPNames.field("field_175073_D"));
|
||||
drawBlockOutline.setAccessible(true);
|
||||
drawBlockOutline.set(Minecraft.getMinecraft().entityRenderer, false);
|
||||
} catch(Exception e) {}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void tick(TickEvent event) {
|
||||
if(!ReplayHandler.replayActive()) return;
|
||||
if(ReplayHandler.getCameraEntity() != null)
|
||||
ReplayHandler.getCameraEntity().updateMovement();
|
||||
onMouseMove(new MouseEvent());
|
||||
onKeyInput(new KeyInputEvent());
|
||||
if(ReplayHandler.isReplaying()) ReplayProcess.tickReplay();
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onRenderWorld(RenderWorldLastEvent event)
|
||||
throws IllegalAccessException, IllegalArgumentException,
|
||||
InvocationTargetException, IOException {
|
||||
if(!ReplayHandler.replayActive()) return;
|
||||
ReplayHandler.setCameraEntity(ReplayHandler.getCameraEntity());
|
||||
if(ReplayHandler.replayActive() && ReplayHandler.isPaused()) {
|
||||
if(mc != null && mc.thePlayer != null)
|
||||
MinecraftTicker.runMouseKeyboardTick(mc);
|
||||
}
|
||||
}
|
||||
|
||||
public void resetUI() throws Exception {
|
||||
if(FMLClientHandler.instance().isGUIOpen(GuiMouseInput.class)) {
|
||||
mc.displayGuiScreen((GuiScreen)null);
|
||||
}
|
||||
realTimePosition = 0;
|
||||
speedSlider = new GuiReplaySpeedSlider(1, sliderX, sliderY, "Speed");
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onRenderGui(RenderGameOverlayEvent.Post event) throws IllegalArgumentException, IllegalAccessException {
|
||||
if(!ReplayHandler.replayActive()) return;
|
||||
GL11.glEnable(GL11.GL_BLEND);
|
||||
drawBlockOutline.set(Minecraft.getMinecraft().entityRenderer, false);
|
||||
|
||||
if(!ReplayHandler.replayActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ScaledResolution sr = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight);
|
||||
final int width = sr.getScaledWidth();
|
||||
final int height = sr.getScaledHeight();
|
||||
|
||||
final int mouseX = (Mouse.getX() * width / mc.displayWidth);
|
||||
final int mouseY = (height - Mouse.getY() * height / mc.displayHeight);
|
||||
|
||||
//Draw Timeline
|
||||
drawTimeline(timelineX, width - 14, 9);
|
||||
drawRealTimeline(realTimelineX, width - 14 - 11, realTimelineY, mouseX, mouseY);
|
||||
|
||||
//Play/Pause button
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
|
||||
boolean play = !ReplayHandler.isPaused();
|
||||
boolean hover = false;
|
||||
|
||||
if(FMLClientHandler.instance().isGUIOpen(GuiMouseInput.class)) {
|
||||
if(mouseX >= ppButtonX && mouseX <= ppButtonX+20
|
||||
&& mouseY >= ppButtonY && mouseY <= ppButtonY+20) {
|
||||
hover = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(play) {
|
||||
y = 20;
|
||||
}
|
||||
if(hover) {
|
||||
x = 20;
|
||||
}
|
||||
|
||||
mc.renderEngine.bindTexture(guiLocation);
|
||||
|
||||
GlStateManager.resetColor();
|
||||
this.drawModalRectWithCustomSizedTexture(ppButtonX, ppButtonY, x, y, 20, 20, 64, 64);
|
||||
|
||||
//GlStateManager.resetColor();
|
||||
|
||||
if(Mouse.isButtonDown(0) && FMLClientHandler.instance().isGUIOpen(GuiMouseInput.class)) { //clicking the Button
|
||||
speedSlider.mousePressed(mc, mouseX, mouseY);
|
||||
if(!mouseDown) {
|
||||
mouseDown = true;
|
||||
if(hover) {
|
||||
boolean paused = !ReplayHandler.isPaused();
|
||||
if(paused) {
|
||||
ReplayHandler.setSpeed(0);
|
||||
} else {
|
||||
ReplayHandler.setSpeed(speedSlider.getSliderValue());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(mouseX >= timelineX+4 && mouseX <= width - 18 && mouseY >= 11 && mouseY <= 29) {
|
||||
double tot = (width - 18)-(timelineX+4);
|
||||
double perc = (mouseX-(timelineX+4))/tot;
|
||||
double time = perc*(double)ReplayHandler.getReplayLength();
|
||||
|
||||
if(time < ReplayHandler.getReplayTime()) {
|
||||
mc.displayGuiScreen((GuiScreen)null);
|
||||
}
|
||||
|
||||
ReplayHandler.setReplayPos((int)time, false);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
speedSlider.mouseReleased(mouseX, mouseY);
|
||||
mouseDown = false;
|
||||
}
|
||||
|
||||
|
||||
//Place Keyframe Button
|
||||
hover = false;
|
||||
x = 0;
|
||||
y = 0;
|
||||
|
||||
if(FMLClientHandler.instance().isGUIOpen(GuiMouseInput.class)) {
|
||||
if(mouseX >= place_ButtonX && mouseX <= place_ButtonX+20
|
||||
&& mouseY >= place_ButtonY && mouseY <= place_ButtonY+20) {
|
||||
hover = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(hover) {
|
||||
x = 20;
|
||||
}
|
||||
|
||||
if(ReplayHandler.getSelected() != null && ReplayHandler.getSelected() instanceof PositionKeyframe) {
|
||||
y += 20;
|
||||
}
|
||||
|
||||
mc.renderEngine.bindTexture(keyframeLocation);
|
||||
|
||||
if(hover && Mouse.isButtonDown(0) && isClick()) {
|
||||
if(ReplayHandler.getSelected() == null || !(ReplayHandler.getSelected() instanceof PositionKeyframe)) {
|
||||
addPlaceKeyframe();
|
||||
} else {
|
||||
ReplayHandler.removeKeyframe(ReplayHandler.getSelected());
|
||||
}
|
||||
}
|
||||
|
||||
GlStateManager.resetColor();
|
||||
this.drawModalRectWithCustomSizedTexture(place_ButtonX, place_ButtonY, x, y, 20, 20, 64, 64);
|
||||
|
||||
|
||||
//Time Keyframe Button
|
||||
hover = false;
|
||||
x = 0;
|
||||
y = 40;
|
||||
|
||||
boolean timeSelected = false;
|
||||
if(ReplayHandler.getSelected() != null && ReplayHandler.getSelected() instanceof TimeKeyframe) {
|
||||
timeSelected = true;
|
||||
x = 40;
|
||||
y = 0;
|
||||
}
|
||||
|
||||
if(FMLClientHandler.instance().isGUIOpen(GuiMouseInput.class)) {
|
||||
if(mouseX >= time_ButtonX && mouseX <= time_ButtonX+20
|
||||
&& mouseY >= time_ButtonY && mouseY <= time_ButtonY+20) {
|
||||
hover = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(hover) {
|
||||
if(timeSelected) {
|
||||
y = 20;
|
||||
} else {
|
||||
x = 20;
|
||||
}
|
||||
}
|
||||
|
||||
mc.renderEngine.bindTexture(keyframeLocation);
|
||||
|
||||
if(hover && Mouse.isButtonDown(0) && isClick()) {
|
||||
if(ReplayHandler.getSelected() == null || !(ReplayHandler.getSelected() instanceof TimeKeyframe)) {
|
||||
addTimeKeyframe();
|
||||
} else {
|
||||
ReplayHandler.removeKeyframe(ReplayHandler.getSelected());
|
||||
}
|
||||
}
|
||||
|
||||
GlStateManager.resetColor();
|
||||
this.drawModalRectWithCustomSizedTexture(time_ButtonX, time_ButtonY, x, y, 20, 20, 64, 64);
|
||||
|
||||
if(mouseX >= (timelineX+4) && mouseX <= width - 18 && mouseY >= 11 && mouseY <= 29) {
|
||||
double tot = (width - 18)-(timelineX+4);
|
||||
double perc = (mouseX-(timelineX+4))/tot;
|
||||
long time = Math.round(perc*(double)ReplayHandler.getReplayLength());
|
||||
|
||||
String timestamp = (String.format("%02d:%02ds",
|
||||
TimeUnit.MILLISECONDS.toMinutes(time),
|
||||
TimeUnit.MILLISECONDS.toSeconds(time) -
|
||||
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(time))
|
||||
));
|
||||
|
||||
this.drawCenteredString(mc.fontRendererObj, timestamp, mouseX, mouseY+5, Color.WHITE.getRGB());
|
||||
}
|
||||
|
||||
if(mc.inGameHasFocus) {
|
||||
Mouse.setCursorPosition(width/2, height/2);
|
||||
}
|
||||
|
||||
speedSlider.drawButton(mc, mouseX, mouseY);
|
||||
GlStateManager.resetColor();
|
||||
|
||||
Entity player = ReplayHandler.getCameraEntity();
|
||||
if(player != null) {
|
||||
player.setVelocity(0, 0, 0);
|
||||
}
|
||||
|
||||
if(!Mouse.isButtonDown(0)) isClick();
|
||||
}
|
||||
|
||||
private int tl_begin_x=0;
|
||||
private int tl_begin_width = 4;
|
||||
|
||||
private int tl_end_x=60;
|
||||
private int tl_end_width = 4;
|
||||
|
||||
private int tl_middle_x=4;
|
||||
|
||||
private int tl_y=40;
|
||||
|
||||
private void drawTimeline(int minX, int maxX, int y) {
|
||||
int zero = minX+tl_begin_width;
|
||||
int full = maxX-tl_end_width;
|
||||
|
||||
GlStateManager.resetColor();
|
||||
mc.renderEngine.bindTexture(guiLocation);
|
||||
this.drawModalRectWithCustomSizedTexture(minX, y, tl_begin_x, tl_y, tl_begin_width, 22, 64, 64);
|
||||
|
||||
for(int i=minX+tl_begin_width; i<maxX-tl_end_width; i += tl_end_x-tl_begin_width) {
|
||||
this.drawModalRectWithCustomSizedTexture(i, y, tl_begin_x+tl_begin_width
|
||||
, tl_y, Math.min(tl_end_x-tl_begin_width, maxX-tl_end_width-i)
|
||||
, 22, 64, 64);
|
||||
}
|
||||
|
||||
this.drawModalRectWithCustomSizedTexture(maxX-tl_end_width, y, tl_end_x, tl_y, tl_end_width, 22, 64, 64);
|
||||
|
||||
//Cursor
|
||||
double width = full-zero;
|
||||
double perc = (double)ReplayHandler.getReplayTime()/(double)ReplayHandler.getReplayLength();
|
||||
|
||||
int cursorX = (int)Math.round(zero+(perc*width));
|
||||
this.drawModalRectWithCustomSizedTexture(cursorX-3, y+3, 44, 0, 8, 16, 64, 64);
|
||||
}
|
||||
|
||||
int sl_begin_x = 0;
|
||||
int sl_end_x = 63;
|
||||
int sl_y = 40;
|
||||
|
||||
int plus_x = 0;
|
||||
int plus_y = 0;
|
||||
|
||||
int minus_x = 0;
|
||||
int minus_y = 9;
|
||||
|
||||
int slider_begin_x = 1;
|
||||
int slider_begin_width = 1;
|
||||
int slider_end_x = 62;
|
||||
int slider_end_width = 1;
|
||||
int slider_y = 50;
|
||||
int slider_height = 7;
|
||||
|
||||
private float zoom_scale = 0.1f; //can see 1/10th of the timeline
|
||||
private float pos_left = 0f; //left border of timeline is at 0%
|
||||
private float cursor_pos = 0f; //cursor is at 0%
|
||||
private long timelineLength = 10*60*1000; //10 min of timeline
|
||||
|
||||
private float zoom_steps = 0.05f;
|
||||
|
||||
private boolean wasSliding = false;
|
||||
|
||||
private void drawRealTimeline(int minX, int maxX, int y, int mouseX, int mouseY) {
|
||||
int zero = minX+tl_begin_width;
|
||||
int full = maxX-tl_end_width;
|
||||
|
||||
//the real timeline
|
||||
GlStateManager.resetColor();
|
||||
mc.renderEngine.bindTexture(guiLocation);
|
||||
this.drawModalRectWithCustomSizedTexture(minX, y, tl_begin_x, tl_y, tl_begin_width, 22, 64, 64);
|
||||
|
||||
for(int i=minX+tl_begin_width; i<maxX-tl_end_width; i += tl_end_x-tl_begin_width) {
|
||||
this.drawModalRectWithCustomSizedTexture(i, y, tl_begin_x+tl_begin_width
|
||||
, tl_y, Math.min(tl_end_x-tl_begin_width, maxX-tl_end_width-i)
|
||||
, 22, 64, 64);
|
||||
}
|
||||
|
||||
this.drawModalRectWithCustomSizedTexture(maxX-tl_end_width, y, tl_end_x, tl_y, tl_end_width, 22, 64, 64);
|
||||
|
||||
//Time Slider
|
||||
int yo = y+22+1;
|
||||
GlStateManager.resetColor();
|
||||
mc.renderEngine.bindTexture(timelineLocation);
|
||||
this.drawModalRectWithCustomSizedTexture(minX, yo, sl_begin_x, sl_y, 2, 9, 64, 64);
|
||||
|
||||
for(int i=minX+2; i<maxX-1; i+= sl_end_x-2) {
|
||||
this.drawModalRectWithCustomSizedTexture(i, yo, 2, sl_y,
|
||||
Math.min(sl_end_x-2, maxX-1-i), 9, 64, 64);
|
||||
}
|
||||
|
||||
this.drawModalRectWithCustomSizedTexture(maxX-1, yo, sl_end_x, sl_y, 1, 9, 64, 64);
|
||||
|
||||
//Timeline Pos Slider
|
||||
int sl_y = yo+1;
|
||||
int minPos = minX+1;
|
||||
int maxPos = maxX-2;
|
||||
int tlWidth = maxPos - minPos;
|
||||
|
||||
int slider_min = minPos+Math.round(pos_left*tlWidth);
|
||||
int slider_width = Math.round(zoom_scale*tlWidth);
|
||||
|
||||
int sl_max = slider_min+slider_width;
|
||||
|
||||
this.drawModalRectWithCustomSizedTexture(slider_min, sl_y, slider_begin_x, slider_y, slider_begin_width, slider_height, 64, 64);
|
||||
|
||||
for(int i=slider_min+slider_begin_width; i<sl_max-slider_end_width; i+=slider_end_x-slider_begin_width-slider_begin_x) {
|
||||
this.drawModalRectWithCustomSizedTexture(i, sl_y, slider_begin_x+slider_begin_width, slider_y,
|
||||
Math.min(slider_end_x-slider_end_width-slider_begin_x, sl_max-slider_end_width-i), slider_height, 64, 64);
|
||||
}
|
||||
|
||||
this.drawModalRectWithCustomSizedTexture(sl_max-slider_end_width, sl_y, slider_end_x, slider_y,
|
||||
slider_end_width, slider_height, 64, 64);
|
||||
|
||||
//Slider dragging
|
||||
if(Mouse.isButtonDown(0) && (mouseX >= slider_min && mouseX <= sl_max && mouseY >= sl_y && mouseY <= sl_y+slider_height || wasSliding)) {
|
||||
wasSliding = true;
|
||||
float dx = ((float)Mouse.getDX() * (float)new ScaledResolution(mc, mc.displayWidth, mc.displayHeight).getScaledWidth() / mc.displayWidth);
|
||||
this.pos_left = Math.min(1f-this.zoom_scale, Math.max(0f, this.pos_left+(dx/(float)tlWidth)));
|
||||
}
|
||||
|
||||
if(!Mouse.isButtonDown(0)) {
|
||||
wasSliding = false;
|
||||
}
|
||||
|
||||
//Timeline Buttons
|
||||
//+- Buttons
|
||||
boolean hover = false;
|
||||
int px = plus_x;
|
||||
if(FMLClientHandler.instance().isGUIOpen(GuiMouseInput.class)) {
|
||||
if(mouseX >= maxX+2 && mouseX <= maxX+2+9
|
||||
&& mouseY >= y+1 && mouseY <= y+1+9) {
|
||||
hover = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(hover) {
|
||||
px+=9;
|
||||
}
|
||||
|
||||
this.drawModalRectWithCustomSizedTexture(maxX+2, y+1, px, plus_y, 9, 9, 64, 64);
|
||||
|
||||
if(hover && Mouse.isButtonDown(0)) {
|
||||
zoomIn();
|
||||
}
|
||||
|
||||
hover = false;
|
||||
int mx = minus_x;
|
||||
|
||||
if(FMLClientHandler.instance().isGUIOpen(GuiMouseInput.class)) {
|
||||
if(mouseX >= maxX+2 && mouseX <= maxX+2+9
|
||||
&& mouseY >= y+9+3 && mouseY <= y+9+3+9) {
|
||||
hover = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(hover) {
|
||||
mx+=9;
|
||||
}
|
||||
|
||||
this.drawModalRectWithCustomSizedTexture(maxX+2, y+9+3, mx, minus_y, 9, 9, 64, 64);
|
||||
|
||||
if(hover && Mouse.isButtonDown(0)) {
|
||||
zoomOut();
|
||||
}
|
||||
|
||||
//show Time String
|
||||
if(mouseX >= zero && mouseX <= full && mouseY >= y && mouseY <= y+22) {
|
||||
long tot = Math.round((double)timelineLength*zoom_scale);
|
||||
double perc = (mouseX-(realTimelineX+4))/(double)(full-zero);
|
||||
|
||||
long time = Math.round(this.pos_left*(double)timelineLength)+Math.round(perc*(double)tot);
|
||||
|
||||
String timestamp = (String.format("%02d:%02ds",
|
||||
TimeUnit.MILLISECONDS.toMinutes(time),
|
||||
TimeUnit.MILLISECONDS.toSeconds(time) -
|
||||
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(time))
|
||||
));
|
||||
this.drawCenteredString(mc.fontRendererObj, timestamp, mouseX, mouseY+5, Color.WHITE.getRGB());
|
||||
}
|
||||
|
||||
//draw Markers on timeline
|
||||
MarkerType mt = MarkerType.getMarkerType(zoom_scale, timelineLength);
|
||||
|
||||
//every x seconds, draw small marker
|
||||
long left_real = Math.round(pos_left*(double)timelineLength);
|
||||
long right_real = left_real+(Math.round(zoom_scale*timelineLength));
|
||||
long tot = Math.round((double)timelineLength*zoom_scale);
|
||||
|
||||
for(int s=0; s<=timelineLength; s+= mt.getSmallDistance()) {
|
||||
if(s > right_real) break;
|
||||
if(s >= left_real) {
|
||||
//calculate absolute position on screen
|
||||
long relative = (s) - (left_real);
|
||||
double perc = ((double)relative/(double)tot);
|
||||
|
||||
long real_width = full-zero;
|
||||
long rel_x = Math.round(perc*(double)real_width);
|
||||
|
||||
long real_x = zero+rel_x;
|
||||
|
||||
this.drawVerticalLine((int)real_x, y+19-3, y+19, Color.WHITE.getRGB());
|
||||
}
|
||||
}
|
||||
|
||||
//every x seconds, draw big marker
|
||||
for(int s=0; s<=timelineLength; s+= mt.getDistance()) {
|
||||
if(s > right_real) break;
|
||||
if(s >= left_real) {
|
||||
//calculate absolute position on screen
|
||||
long relative = s - (left_real);
|
||||
double perc = ((double)relative/(double)tot);
|
||||
|
||||
long real_width = full-zero;
|
||||
long rel_x = Math.round(perc*(double)real_width);
|
||||
|
||||
long real_x = zero+rel_x;
|
||||
|
||||
this.drawVerticalLine((int)real_x, y+19-7, y+19, Color.LIGHT_GRAY.getRGB());
|
||||
|
||||
//write text
|
||||
int time = s;
|
||||
String timestamp = (String.format("%02d:%02d",
|
||||
TimeUnit.MILLISECONDS.toMinutes(time),
|
||||
TimeUnit.MILLISECONDS.toSeconds(time) -
|
||||
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(time))
|
||||
));
|
||||
|
||||
this.drawCenteredString(mc.fontRendererObj, timestamp, (int)real_x, y-8, Color.WHITE.getRGB());
|
||||
}
|
||||
}
|
||||
|
||||
//handle Mouse clicks on realTimeLine
|
||||
if(Mouse.isButtonDown(0) && !wasSliding && mouseX >= minX+tl_begin_width && mouseX <= maxX-tl_end_width &&
|
||||
mouseY >= y && mouseY <= y+22) {
|
||||
|
||||
//calculate real time and set cursor accordingly
|
||||
int width = (maxX-tl_end_width) - (minX+tl_begin_width);
|
||||
int rel_x = mouseX-(minX+tl_begin_width);
|
||||
|
||||
float rel_pos = (float)rel_x/(float)width;
|
||||
|
||||
float abs_width = (zoom_scale*(float)timelineLength);
|
||||
int real_pos = Math.round(left_real+((rel_pos)*abs_width));
|
||||
|
||||
realTimePosition = real_pos;
|
||||
|
||||
//Keyframe click handling here
|
||||
if(isClick()) {
|
||||
//tolerance is 2 pixels multiplied with the timespan of one pixel
|
||||
int tolerance = 2*Math.round(abs_width/(float)width);
|
||||
|
||||
if(mouseY >= y+9) {
|
||||
TimeKeyframe close = ReplayHandler.getClosestTimeKeyframeForRealTime(realTimePosition, tolerance);
|
||||
ReplayHandler.selectKeyframe(close); //can be null, deselects keyframe
|
||||
} else {
|
||||
PositionKeyframe close = ReplayHandler.getClosestPlaceKeyframeForRealTime(realTimePosition, tolerance);
|
||||
ReplayHandler.selectKeyframe(close); //can be null, deselects keyframe
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Draw Realtime Cursor
|
||||
if(realTimePosition >= left_real && realTimePosition <= right_real) {
|
||||
long rel_pos = realTimePosition-left_real;
|
||||
long rel_width = right_real-left_real;
|
||||
double perc = (double)rel_pos/(double)rel_width;
|
||||
|
||||
int real_width = (maxX-tl_end_width) - (minX+tl_begin_width);
|
||||
double rel_x = (float)real_width*perc;
|
||||
|
||||
int real_x = (int)Math.round((minX+tl_begin_width)+rel_x);
|
||||
mc.renderEngine.bindTexture(this.guiLocation);
|
||||
|
||||
GL11.glEnable(GL11.GL_BLEND);
|
||||
this.drawModalRectWithCustomSizedTexture(real_x-3, y+3, 44, 0, 8, 16, 64, 64);
|
||||
//this.drawModalRectWithCustomSizedTexture(real_x, sl_y, u, v, width, height, textureWidth, textureHeight)
|
||||
}
|
||||
|
||||
|
||||
//Draw Keyframe logos
|
||||
mc.renderEngine.bindTexture(timelineLocation);
|
||||
for(Keyframe kf : ReplayHandler.getKeyframes()) {
|
||||
if(kf.getRealTimestamp() > right_real) break;
|
||||
if(kf.getRealTimestamp() >= left_real) {
|
||||
int dx = 18;
|
||||
int dy = 0;
|
||||
|
||||
int ry = y+3;
|
||||
|
||||
if(kf instanceof TimeKeyframe) {
|
||||
dy = 5;
|
||||
ry += 5;
|
||||
}
|
||||
if(ReplayHandler.isSelected(kf)) {
|
||||
dx += 5;
|
||||
}
|
||||
|
||||
long relative = kf.getRealTimestamp() - (left_real);
|
||||
double perc = ((double)relative/(double)tot);
|
||||
|
||||
long real_width = full-zero;
|
||||
long rel_x = Math.round(perc*(double)real_width);
|
||||
|
||||
long real_x = zero+rel_x - 2;
|
||||
|
||||
this.drawModalRectWithCustomSizedTexture((int)real_x, ry, dx, dy, 5, 5, 64, 64);
|
||||
}
|
||||
}
|
||||
|
||||
//Draw Play/Pause Button
|
||||
//Play/Pause button
|
||||
|
||||
int dx = 0;
|
||||
int dy = 0;
|
||||
|
||||
boolean play = ReplayHandler.isReplaying();
|
||||
hover = false;
|
||||
|
||||
if(FMLClientHandler.instance().isGUIOpen(GuiMouseInput.class)) {
|
||||
if(mouseX >= r_ppButtonX && mouseX <= r_ppButtonX+20
|
||||
&& mouseY >= r_ppButtonY && mouseY <= r_ppButtonY+20) {
|
||||
hover = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(play) {
|
||||
dy = 20;
|
||||
}
|
||||
if(hover) {
|
||||
dx = 20;
|
||||
}
|
||||
|
||||
mc.renderEngine.bindTexture(guiLocation);
|
||||
|
||||
GlStateManager.resetColor();
|
||||
this.drawModalRectWithCustomSizedTexture(r_ppButtonX, r_ppButtonY, dx, dy, 20, 20, 64, 64);
|
||||
|
||||
//Handling the click on the Replay starter
|
||||
if(hover && Mouse.isButtonDown(0) && isClick()) {
|
||||
if(ReplayHandler.isReplaying()) {
|
||||
ReplayHandler.interruptReplay();
|
||||
} else {
|
||||
ReplayHandler.startPath();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void addPlaceKeyframe() {
|
||||
CameraEntity cam = ReplayHandler.getCameraEntity();
|
||||
if(cam == null) return;
|
||||
ReplayHandler.addKeyframe(new PositionKeyframe(realTimePosition, new Position(cam.posX, cam.posY, cam.posZ, cam.rotationPitch, cam.rotationYaw)));
|
||||
}
|
||||
|
||||
private void addTimeKeyframe() {
|
||||
ReplayHandler.addKeyframe(new TimeKeyframe(realTimePosition, ReplayHandler.getReplayTime()));
|
||||
}
|
||||
|
||||
private enum MarkerType {
|
||||
|
||||
ONE_S(1*1000, 100),
|
||||
FIVE_S(5*1000, 1*1000),
|
||||
QUARTER_M(15*1000, 3*1000),
|
||||
HALF_M(30*1000, 5*1000),
|
||||
ONE_M(60*1000, 10*1000),
|
||||
FIVE_M(5*60*1000, 50*1000);
|
||||
|
||||
int minimum;
|
||||
int small_min;
|
||||
int maximum = 10;
|
||||
|
||||
int getDistance() {
|
||||
return minimum;
|
||||
}
|
||||
|
||||
int getSmallDistance() {
|
||||
return small_min;
|
||||
}
|
||||
|
||||
MarkerType(int minimum, int small_min) {
|
||||
this.minimum = minimum;
|
||||
this.small_min = small_min;
|
||||
}
|
||||
|
||||
public static MarkerType getMarkerType(float scale, long totalLength) {
|
||||
long visible = Math.round((double)totalLength*scale);
|
||||
long seconds = visible;
|
||||
|
||||
for(MarkerType mt : values()) {
|
||||
if(seconds/mt.getDistance() <= 10) {
|
||||
return mt;
|
||||
}
|
||||
}
|
||||
|
||||
return FIVE_M;
|
||||
}
|
||||
}
|
||||
|
||||
private void zoomIn() {
|
||||
if(!isClick()) return;
|
||||
this.zoom_scale = Math.max(0.025f, zoom_scale-zoom_steps);
|
||||
}
|
||||
|
||||
private void zoomOut() {
|
||||
if(!isClick()) return;
|
||||
this.zoom_scale = Math.min(1f, zoom_scale+zoom_steps);
|
||||
this.pos_left = Math.min(pos_left, 1f-zoom_scale);
|
||||
}
|
||||
|
||||
private boolean mouseDwn = false;
|
||||
|
||||
private boolean isClick() {
|
||||
if(Mouse.isButtonDown(0)) {
|
||||
boolean bef = new Boolean(mouseDwn);
|
||||
mouseDwn = true;
|
||||
return !bef;
|
||||
} else {
|
||||
mouseDwn = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onMouseMove(MouseEvent event) {
|
||||
if(!ReplayHandler.replayActive()) return;
|
||||
boolean flag = Display.isActive();
|
||||
flag = true;
|
||||
|
||||
mc.mcProfiler.startSection("mouse");
|
||||
|
||||
if (flag && Minecraft.isRunningOnMac && mc.inGameHasFocus && !Mouse.isInsideWindow())
|
||||
{
|
||||
Mouse.setGrabbed(false);
|
||||
Mouse.setCursorPosition(Display.getWidth() / 2, Display.getHeight() / 2);
|
||||
Mouse.setGrabbed(true);
|
||||
}
|
||||
|
||||
if (mc.inGameHasFocus && flag)
|
||||
{
|
||||
mc.mouseHelper.mouseXYChange();
|
||||
float f1 = mc.gameSettings.mouseSensitivity * 0.6F + 0.2F;
|
||||
float f2 = f1 * f1 * f1 * 8.0F;
|
||||
float f3 = (float)mc.mouseHelper.deltaX * f2;
|
||||
float f4 = (float)mc.mouseHelper.deltaY * f2;
|
||||
byte b0 = 1;
|
||||
|
||||
if (mc.gameSettings.invertMouse)
|
||||
{
|
||||
b0 = -1;
|
||||
}
|
||||
|
||||
ReplayHandler.getCameraEntity().setAngles(f3, f4 * (float)b0);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onKeyInput(KeyInputEvent event) {
|
||||
if(!ReplayHandler.replayActive()) return;
|
||||
if(FMLClientHandler.instance().isGUIOpen(GuiMouseInput.class)) {
|
||||
return;
|
||||
}
|
||||
|
||||
KeyBinding[] keyBindings = Minecraft.getMinecraft().gameSettings.keyBindings;
|
||||
for(KeyBinding kb : keyBindings) {
|
||||
if(!kb.isKeyDown()) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
|
||||
if(kb.getKeyDescription().equals("key.forward")) {
|
||||
ReplayHandler.getCameraEntity().setMovement(MoveDirection.FORWARD);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(kb.getKeyDescription().equals("key.back")) {
|
||||
ReplayHandler.getCameraEntity().setMovement(MoveDirection.BACKWARD);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(kb.getKeyDescription().equals("key.jump")) {
|
||||
ReplayHandler.getCameraEntity().setMovement(MoveDirection.UP);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(kb.getKeyDescription().equals("key.sneak")) {
|
||||
ReplayHandler.getCameraEntity().setMovement(MoveDirection.DOWN);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(kb.getKeyDescription().equals("key.left")) {
|
||||
ReplayHandler.getCameraEntity().setMovement(MoveDirection.LEFT);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(kb.getKeyDescription().equals("key.right")) {
|
||||
ReplayHandler.getCameraEntity().setMovement(MoveDirection.RIGHT);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(kb.getKeyDescription().equals("key.chat")) {
|
||||
mc.displayGuiScreen(new GuiMouseInput());
|
||||
continue;
|
||||
}
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,442 @@
|
||||
package eu.crushedpixel.replaymod.events;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiChat;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.gui.inventory.GuiInventory;
|
||||
import net.minecraft.client.settings.GameSettings;
|
||||
import net.minecraft.client.settings.KeyBinding;
|
||||
import net.minecraft.crash.CrashReport;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.network.play.client.C16PacketClientStatus;
|
||||
import net.minecraft.util.MathHelper;
|
||||
import net.minecraft.util.ReportedException;
|
||||
|
||||
import org.lwjgl.input.Keyboard;
|
||||
import org.lwjgl.input.Mouse;
|
||||
import org.lwjgl.opengl.Display;
|
||||
|
||||
import eu.crushedpixel.replaymod.entities.CameraEntity;
|
||||
import eu.crushedpixel.replaymod.reflection.MCPNames;
|
||||
import eu.crushedpixel.replaymod.replay.ReplayHandler;
|
||||
|
||||
public class MinecraftTicker {
|
||||
|
||||
private static Field debugCrashKeyPressTime, rightClickDelayTimer, systemTime, leftClickCounter;
|
||||
private static Method getSystemTime, updateDebugProfilerName,
|
||||
clickMouse, middleClickMouse, rightClickMouse, sendClickBlockToController;
|
||||
|
||||
private static Minecraft mc = Minecraft.getMinecraft();
|
||||
|
||||
private static float camPitch, camYaw, smoothCamPartialTicks,
|
||||
smoothCamFilterX, smoothCamFilterY;
|
||||
|
||||
static {
|
||||
try {
|
||||
debugCrashKeyPressTime = Minecraft.class.getDeclaredField(MCPNames.field("field_83002_am"));
|
||||
debugCrashKeyPressTime.setAccessible(true);
|
||||
|
||||
rightClickDelayTimer = Minecraft.class.getDeclaredField(MCPNames.field("field_71467_ac"));
|
||||
rightClickDelayTimer.setAccessible(true);
|
||||
|
||||
systemTime = Minecraft.class.getDeclaredField(MCPNames.field("field_71423_H"));
|
||||
systemTime.setAccessible(true);
|
||||
|
||||
leftClickCounter = Minecraft.class.getDeclaredField(MCPNames.field("field_71429_W"));
|
||||
leftClickCounter.setAccessible(true);
|
||||
|
||||
getSystemTime = Minecraft.class.getDeclaredMethod(MCPNames.method("func_71386_F"));
|
||||
getSystemTime.setAccessible(true);
|
||||
|
||||
updateDebugProfilerName = Minecraft.class.getDeclaredMethod(MCPNames.method("func_71383_b"), int.class);
|
||||
updateDebugProfilerName.setAccessible(true);
|
||||
|
||||
clickMouse = Minecraft.class.getDeclaredMethod(MCPNames.method("func_147116_af"));
|
||||
clickMouse.setAccessible(true);
|
||||
|
||||
rightClickMouse = Minecraft.class.getDeclaredMethod(MCPNames.method("func_147121_ag"));
|
||||
rightClickMouse.setAccessible(true);
|
||||
|
||||
middleClickMouse = Minecraft.class.getDeclaredMethod(MCPNames.method("func_147112_ai"));
|
||||
middleClickMouse.setAccessible(true);
|
||||
|
||||
sendClickBlockToController = Minecraft.class.getDeclaredMethod(MCPNames.method("func_147115_a"), boolean.class);
|
||||
sendClickBlockToController.setAccessible(true);
|
||||
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void runMouseKeyboardTick(Minecraft mc) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, IOException {
|
||||
|
||||
try {
|
||||
mc.mcProfiler.endStartSection("mouse");
|
||||
int i;
|
||||
while(Mouse.next()) {
|
||||
i = Mouse.getEventButton();
|
||||
KeyBinding.setKeyBindState(i - 100, Mouse.getEventButtonState());
|
||||
|
||||
if (Mouse.getEventButtonState())
|
||||
{
|
||||
if (mc.thePlayer.isSpectator() && i == 2)
|
||||
{
|
||||
mc.ingameGUI.func_175187_g().func_175261_b();
|
||||
}
|
||||
else
|
||||
{
|
||||
KeyBinding.onTick(i - 100);
|
||||
}
|
||||
}
|
||||
|
||||
long k = (Long)getSystemTime.invoke(mc) - (Long)systemTime.get(mc);
|
||||
|
||||
if (k <= 200L)
|
||||
{
|
||||
int j = Mouse.getEventDWheel();
|
||||
|
||||
if (j != 0)
|
||||
{
|
||||
if (mc.thePlayer.isSpectator())
|
||||
{
|
||||
j = j < 0 ? -1 : 1;
|
||||
|
||||
if (mc.ingameGUI.func_175187_g().func_175262_a())
|
||||
{
|
||||
mc.ingameGUI.func_175187_g().func_175259_b(-j);
|
||||
}
|
||||
else
|
||||
{
|
||||
float f = MathHelper.clamp_float(mc.thePlayer.capabilities.getFlySpeed() + (float)j * 0.005F, 0.0F, 0.2F);
|
||||
mc.thePlayer.capabilities.setFlySpeed(f);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mc.thePlayer.inventory.changeCurrentItem(j);
|
||||
}
|
||||
}
|
||||
|
||||
if (mc.currentScreen == null)
|
||||
{
|
||||
if (!mc.inGameHasFocus && Mouse.getEventButtonState())
|
||||
{
|
||||
mc.setIngameFocus();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mc.currentScreen.handleMouseInput();
|
||||
}
|
||||
}
|
||||
net.minecraftforge.fml.common.FMLCommonHandler.instance().fireMouseInput();
|
||||
}
|
||||
|
||||
if ((Integer)leftClickCounter.get(mc) > 0)
|
||||
{
|
||||
leftClickCounter.set(mc, (Integer)leftClickCounter.get(mc) - 1);
|
||||
}
|
||||
mc.mcProfiler.endStartSection("keyboard");
|
||||
|
||||
while (Keyboard.next())
|
||||
{
|
||||
i = Keyboard.getEventKey() == 0 ? Keyboard.getEventCharacter() + 256 : Keyboard.getEventKey();
|
||||
KeyBinding.setKeyBindState(i, Keyboard.getEventKeyState());
|
||||
|
||||
if (Keyboard.getEventKeyState())
|
||||
{
|
||||
KeyBinding.onTick(i);
|
||||
}
|
||||
|
||||
if ((Long)debugCrashKeyPressTime.get(mc) > 0L)
|
||||
{
|
||||
if ((Long)getSystemTime.invoke(mc) - (Long)debugCrashKeyPressTime.get(mc) >= 6000L)
|
||||
{
|
||||
throw new ReportedException(new CrashReport("Manually triggered debug crash", new Throwable()));
|
||||
}
|
||||
|
||||
if (!Keyboard.isKeyDown(46) || !Keyboard.isKeyDown(61))
|
||||
{
|
||||
debugCrashKeyPressTime.set(mc, -1);
|
||||
}
|
||||
}
|
||||
else if (Keyboard.isKeyDown(46) && Keyboard.isKeyDown(61))
|
||||
{
|
||||
debugCrashKeyPressTime.set(mc, getSystemTime.invoke(mc));
|
||||
}
|
||||
|
||||
mc.dispatchKeypresses();
|
||||
|
||||
if (Keyboard.getEventKeyState())
|
||||
{
|
||||
if (i == 62 && mc.entityRenderer != null)
|
||||
{
|
||||
mc.entityRenderer.switchUseShader();
|
||||
}
|
||||
|
||||
if (mc.currentScreen != null)
|
||||
{
|
||||
mc.currentScreen.handleKeyboardInput();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i == 1)
|
||||
{
|
||||
mc.displayInGameMenu();
|
||||
}
|
||||
|
||||
if (i == 32 && Keyboard.isKeyDown(61) && mc.ingameGUI != null)
|
||||
{
|
||||
mc.ingameGUI.getChatGUI().clearChatMessages();
|
||||
}
|
||||
|
||||
if (i == 31 && Keyboard.isKeyDown(61))
|
||||
{
|
||||
mc.refreshResources();
|
||||
}
|
||||
|
||||
if (i == 17 && Keyboard.isKeyDown(61))
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
if (i == 18 && Keyboard.isKeyDown(61))
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
if (i == 47 && Keyboard.isKeyDown(61))
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
if (i == 38 && Keyboard.isKeyDown(61))
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
if (i == 22 && Keyboard.isKeyDown(61))
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
if (i == 20 && Keyboard.isKeyDown(61))
|
||||
{
|
||||
mc.refreshResources();
|
||||
}
|
||||
|
||||
if (i == 33 && Keyboard.isKeyDown(61))
|
||||
{
|
||||
boolean flag1 = Keyboard.isKeyDown(42) | Keyboard.isKeyDown(54);
|
||||
mc.gameSettings.setOptionValue(GameSettings.Options.RENDER_DISTANCE, flag1 ? -1 : 1);
|
||||
}
|
||||
|
||||
if (i == 30 && Keyboard.isKeyDown(61))
|
||||
{
|
||||
mc.renderGlobal.loadRenderers();
|
||||
}
|
||||
|
||||
if (i == 35 && Keyboard.isKeyDown(61))
|
||||
{
|
||||
mc.gameSettings.advancedItemTooltips = !mc.gameSettings.advancedItemTooltips;
|
||||
mc.gameSettings.saveOptions();
|
||||
}
|
||||
|
||||
if (i == 48 && Keyboard.isKeyDown(61))
|
||||
{
|
||||
mc.getRenderManager().setDebugBoundingBox(!mc.getRenderManager().isDebugBoundingBox());
|
||||
}
|
||||
|
||||
if (i == 25 && Keyboard.isKeyDown(61))
|
||||
{
|
||||
mc.gameSettings.pauseOnLostFocus = !mc.gameSettings.pauseOnLostFocus;
|
||||
mc.gameSettings.saveOptions();
|
||||
}
|
||||
|
||||
if (i == 59)
|
||||
{
|
||||
mc.gameSettings.hideGUI = !mc.gameSettings.hideGUI;
|
||||
}
|
||||
|
||||
if (i == 61)
|
||||
{
|
||||
mc.gameSettings.showDebugInfo = !mc.gameSettings.showDebugInfo;
|
||||
mc.gameSettings.showDebugProfilerChart = GuiScreen.isShiftKeyDown();
|
||||
}
|
||||
|
||||
if (mc.gameSettings.keyBindTogglePerspective.isPressed())
|
||||
{
|
||||
++mc.gameSettings.thirdPersonView;
|
||||
|
||||
if (mc.gameSettings.thirdPersonView > 2)
|
||||
{
|
||||
mc.gameSettings.thirdPersonView = 0;
|
||||
}
|
||||
|
||||
if (mc.gameSettings.thirdPersonView == 0)
|
||||
{
|
||||
mc.entityRenderer.loadEntityShader(mc.getRenderViewEntity());
|
||||
}
|
||||
else if (mc.gameSettings.thirdPersonView == 1)
|
||||
{
|
||||
mc.entityRenderer.loadEntityShader((Entity)null);
|
||||
}
|
||||
}
|
||||
|
||||
if (mc.gameSettings.keyBindSmoothCamera.isPressed())
|
||||
{
|
||||
mc.gameSettings.smoothCamera = !mc.gameSettings.smoothCamera;
|
||||
}
|
||||
}
|
||||
|
||||
if (mc.gameSettings.showDebugInfo && mc.gameSettings.showDebugProfilerChart)
|
||||
{
|
||||
if (i == 11)
|
||||
{
|
||||
updateDebugProfilerName.invoke(mc, 0);
|
||||
}
|
||||
|
||||
for (int l = 0; l < 9; ++l)
|
||||
{
|
||||
if (i == 2 + l)
|
||||
{
|
||||
updateDebugProfilerName.invoke(mc, l+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
net.minecraftforge.fml.common.FMLCommonHandler.instance().fireKeyInput();
|
||||
}
|
||||
|
||||
for (i = 0; i < 9; ++i)
|
||||
{
|
||||
if (mc.gameSettings.keyBindsHotbar[i].isPressed())
|
||||
{
|
||||
if (mc.thePlayer.isSpectator())
|
||||
{
|
||||
mc.ingameGUI.func_175187_g().func_175260_a(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
mc.thePlayer.inventory.currentItem = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean flag = mc.gameSettings.chatVisibility != EntityPlayer.EnumChatVisibility.HIDDEN;
|
||||
|
||||
while (mc.gameSettings.keyBindInventory.isPressed())
|
||||
{
|
||||
if (mc.playerController.isRidingHorse())
|
||||
{
|
||||
mc.thePlayer.sendHorseInventory();
|
||||
}
|
||||
else
|
||||
{
|
||||
mc.getNetHandler().addToSendQueue(new C16PacketClientStatus(C16PacketClientStatus.EnumState.OPEN_INVENTORY_ACHIEVEMENT));
|
||||
mc.displayGuiScreen(new GuiInventory(mc.thePlayer));
|
||||
}
|
||||
}
|
||||
|
||||
while (mc.gameSettings.keyBindDrop.isPressed())
|
||||
{
|
||||
if (!mc.thePlayer.isSpectator())
|
||||
{
|
||||
mc.thePlayer.dropOneItem(GuiScreen.isCtrlKeyDown());
|
||||
}
|
||||
}
|
||||
|
||||
while (mc.gameSettings.keyBindChat.isPressed() && flag)
|
||||
{
|
||||
mc.displayGuiScreen(new GuiChat());
|
||||
}
|
||||
|
||||
if (mc.currentScreen == null && mc.gameSettings.keyBindCommand.isPressed() && flag)
|
||||
{
|
||||
mc.displayGuiScreen(new GuiChat("/"));
|
||||
}
|
||||
|
||||
if (mc.thePlayer != null && mc.thePlayer.isUsingItem())
|
||||
{
|
||||
if (!mc.gameSettings.keyBindUseItem.isKeyDown())
|
||||
{
|
||||
mc.playerController.onStoppedUsingItem(mc.thePlayer);
|
||||
}
|
||||
|
||||
label435:
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (!mc.gameSettings.keyBindAttack.isPressed())
|
||||
{
|
||||
while (mc.gameSettings.keyBindUseItem.isPressed())
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (mc.gameSettings.keyBindPickBlock.isPressed())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
break label435;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (mc.gameSettings.keyBindAttack.isPressed())
|
||||
{
|
||||
if(mc != null)
|
||||
try {
|
||||
clickMouse.invoke(mc);
|
||||
} catch(Exception e) {}
|
||||
|
||||
}
|
||||
|
||||
while (mc.gameSettings.keyBindUseItem.isPressed())
|
||||
{
|
||||
if(mc != null)
|
||||
try {
|
||||
rightClickMouse.invoke(mc);
|
||||
} catch(Exception e) {}
|
||||
}
|
||||
|
||||
while (mc.gameSettings.keyBindPickBlock.isPressed())
|
||||
{
|
||||
if(mc != null)
|
||||
try {
|
||||
middleClickMouse.invoke(mc);
|
||||
} catch(Exception e) {}
|
||||
}
|
||||
}
|
||||
|
||||
if (mc.gameSettings.keyBindUseItem.isKeyDown() && (Integer)rightClickDelayTimer.get(mc) == 0 && !mc.thePlayer.isUsingItem())
|
||||
{
|
||||
if(mc != null)
|
||||
try {
|
||||
rightClickMouse.invoke(mc);
|
||||
} catch(Exception e) {}
|
||||
}
|
||||
|
||||
if(mc != null)
|
||||
try {
|
||||
sendClickBlockToController.invoke(mc, mc.currentScreen == null && mc.gameSettings.keyBindAttack.isKeyDown() && mc.inGameHasFocus);
|
||||
} catch(Exception e) {}
|
||||
|
||||
if(mc != null)
|
||||
systemTime.set(mc, getSystemTime.invoke(mc));
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package eu.crushedpixel.replaymod.events;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.network.Packet;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
import net.minecraft.network.play.server.S0CPacketSpawnPlayer;
|
||||
import net.minecraft.network.play.server.S14PacketEntity.S17PacketEntityLookMove;
|
||||
import net.minecraft.network.play.server.S18PacketEntityTeleport;
|
||||
import net.minecraft.util.MathHelper;
|
||||
import net.minecraftforge.event.entity.EntityJoinWorldEvent;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.gameevent.TickEvent.PlayerTickEvent;
|
||||
import eu.crushedpixel.replaymod.recording.ConnectionEventHandler;
|
||||
|
||||
public class RecordingHandler {
|
||||
|
||||
private Minecraft mc = Minecraft.getMinecraft();
|
||||
|
||||
private int entityID = Integer.MAX_VALUE;
|
||||
|
||||
@SubscribeEvent
|
||||
public void onPlayerJoin(EntityJoinWorldEvent e) throws IOException {
|
||||
if(e.entity != mc.thePlayer) return;
|
||||
if(!ConnectionEventHandler.isRecording()) return;
|
||||
|
||||
//Packet packet = new S0CPacketSpawnPlayer((EntityPlayer)e.entity);
|
||||
Packet packet = new S0CPacketSpawnPlayer();
|
||||
|
||||
ByteBuf bb = Unpooled.buffer();
|
||||
PacketBuffer pb = new PacketBuffer(bb);
|
||||
|
||||
pb.writeVarIntToBuffer(entityID);
|
||||
//pb.writeUuid((e.entity).getUniqueID());
|
||||
pb.writeUuid(UUID.randomUUID());
|
||||
|
||||
pb.writeInt(MathHelper.floor_double(e.entity.posX * 32.0D));
|
||||
pb.writeInt(MathHelper.floor_double(e.entity.posY * 32.0D));
|
||||
pb.writeInt(MathHelper.floor_double(e.entity.posZ * 32.0D));
|
||||
pb.writeByte((byte)((int)(((EntityPlayer)e.entity).rotationYaw * 256.0F / 360.0F)));
|
||||
pb.writeByte((byte)((int)(((EntityPlayer)e.entity).rotationPitch * 256.0F / 360.0F)));
|
||||
|
||||
ItemStack itemstack = ((EntityPlayer)e.entity).inventory.getCurrentItem();
|
||||
pb.writeInt(itemstack == null ? 0 : Item.getIdFromItem(itemstack.getItem()));
|
||||
((EntityPlayer)e.entity).getDataWatcher().writeTo(pb);
|
||||
|
||||
packet.readPacketData(pb);
|
||||
|
||||
ConnectionEventHandler.insertPacket(packet);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onPlayerTick(PlayerTickEvent e) {
|
||||
if(e.player != mc.thePlayer) return;
|
||||
if(!ConnectionEventHandler.isRecording()) return;
|
||||
|
||||
double dx = e.player.prevPosX - e.player.posX;
|
||||
double dy = e.player.prevPosY - e.player.posY;
|
||||
double dz = e.player.prevPosZ - e.player.posZ;
|
||||
|
||||
Packet packet;
|
||||
if(Math.abs(dx) > 4.0 || Math.abs(dy) > 4.0 || Math.abs(dz) > 4.0) {
|
||||
packet = new S18PacketEntityTeleport(e.player);
|
||||
} else {
|
||||
byte oldYaw = (byte)((int)(e.player.prevRotationYaw * 256.0F / 360.0F));
|
||||
byte newYaw = (byte)((int)(e.player.rotationYaw * 256.0F / 360.0F));
|
||||
byte oldPitch = (byte)((int)(e.player.prevRotationPitch * 256.0F / 360.0F));
|
||||
byte newPitch = (byte)((int)(e.player.rotationPitch * 256.0F / 360.0F));
|
||||
|
||||
byte dPitch = (byte)(newPitch-oldPitch);
|
||||
byte dYaw = (byte)(newYaw-oldYaw);
|
||||
|
||||
packet = new S17PacketEntityLookMove(entityID,
|
||||
(byte)Math.round(dx*32), (byte)Math.round(dy*32), (byte)Math.round(dz*32),
|
||||
dYaw, dPitch, e.player.onGround);
|
||||
}
|
||||
|
||||
ConnectionEventHandler.insertPacket(packet);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package eu.crushedpixel.replaymod.events;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.WorldRenderer;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.world.WorldSettings.GameType;
|
||||
import net.minecraftforge.client.event.FOVUpdateEvent;
|
||||
import net.minecraftforge.client.event.RenderWorldEvent;
|
||||
import net.minecraftforge.event.entity.player.AchievementEvent;
|
||||
import net.minecraftforge.event.entity.player.PlayerEvent;
|
||||
import net.minecraftforge.event.entity.player.PlayerFlyableFallEvent;
|
||||
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
|
||||
import net.minecraftforge.fml.common.eventhandler.Event;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.gameevent.TickEvent.PlayerTickEvent;
|
||||
import net.minecraftforge.fml.common.gameevent.TickEvent.ServerTickEvent;
|
||||
import eu.crushedpixel.replaymod.reflection.MCPNames;
|
||||
import eu.crushedpixel.replaymod.replay.ReplayHandler;
|
||||
|
||||
public class ReplayTickHandler {
|
||||
|
||||
private Minecraft mc = Minecraft.getMinecraft();
|
||||
|
||||
@SubscribeEvent
|
||||
public void onPlayer(PlayerEvent event) {
|
||||
if(ReplayHandler.replayActive() && event.isCancelable()) {
|
||||
event.setCanceled(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SubscribeEvent
|
||||
public void onFovChange(FOVUpdateEvent event) {
|
||||
if(ReplayHandler.replayActive()) {
|
||||
event.newfov = 1f;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package eu.crushedpixel.replaymod.gui;
|
||||
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
||||
import eu.crushedpixel.replaymod.reflection.MCPNames;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.client.gui.GuiButtonLanguage;
|
||||
import net.minecraft.client.gui.GuiMainMenu;
|
||||
import net.minecraft.client.gui.GuiSelectWorld;
|
||||
import net.minecraft.client.renderer.texture.DynamicTexture;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.network.Packet;
|
||||
|
||||
public class GuiCustomMainMenu extends GuiMainMenu {
|
||||
|
||||
private static final int REPLAY_MANAGER_ID = 9001;
|
||||
|
||||
@Override
|
||||
public void initGui() {
|
||||
Class<? extends GuiMainMenu> clazz = GuiMainMenu.class;
|
||||
|
||||
int i1 = this.height / 4 + 48;
|
||||
this.buttonList.add(new GuiButton(REPLAY_MANAGER_ID, this.width / 2 - 100, i1 + 2*24, I18n.format("Replay Manager", new Object[0])));
|
||||
|
||||
try {
|
||||
Field viewportTexture = clazz.getDeclaredField(MCPNames.field("field_73977_n"));
|
||||
viewportTexture.setAccessible(true);
|
||||
viewportTexture.set(this, new DynamicTexture(256, 256));
|
||||
|
||||
Field field_110351_G = clazz.getDeclaredField(MCPNames.field("field_110351_G"));
|
||||
field_110351_G.setAccessible(true);
|
||||
field_110351_G.set(this, this.mc.getTextureManager().getDynamicTextureLocation("background", (DynamicTexture)viewportTexture.get(this)));
|
||||
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTime(new Date());
|
||||
|
||||
Field splashText = clazz.getDeclaredField(MCPNames.field("field_73975_c"));
|
||||
splashText.setAccessible(true);
|
||||
|
||||
if (calendar.get(2) + 1 == 11 && calendar.get(5) == 9)
|
||||
{
|
||||
splashText.set(this, "Happy birthday, ez!");
|
||||
}
|
||||
else if (calendar.get(2) + 1 == 6 && calendar.get(5) == 1)
|
||||
{
|
||||
splashText.set(this, "Happy birthday, Notch!");
|
||||
}
|
||||
else if (calendar.get(2) + 1 == 12 && calendar.get(5) == 24)
|
||||
{
|
||||
splashText.set(this, "Merry X-Mas!");
|
||||
}
|
||||
else if (calendar.get(2) + 1 == 1 && calendar.get(5) == 1)
|
||||
{
|
||||
splashText.set(this, "Happy new year!");
|
||||
}
|
||||
else if (calendar.get(2) + 1 == 10 && calendar.get(5) == 31)
|
||||
{
|
||||
splashText.set(this, "OOoooOOOoooo! Spooky!");
|
||||
}
|
||||
|
||||
boolean flag = true;
|
||||
int i = this.height / 4 + 48;
|
||||
|
||||
if (this.mc.isDemo())
|
||||
{
|
||||
Method addDemoButtons = clazz.getDeclaredMethod(MCPNames.method("func_73972_b"), int.class, int.class);
|
||||
addDemoButtons.setAccessible(true);
|
||||
addDemoButtons.invoke(this, i, 24);
|
||||
}
|
||||
else
|
||||
{
|
||||
Method addSingleplayerMultiplayerButtons = clazz.getDeclaredMethod(MCPNames.method("func_73969_a"), int.class, int.class);
|
||||
addSingleplayerMultiplayerButtons.setAccessible(true);
|
||||
addSingleplayerMultiplayerButtons.invoke(this, i - 24, 24);
|
||||
}
|
||||
|
||||
this.buttonList.add(new GuiButton(0, this.width / 2 - 100, i + 72 + 12, 98, 20, I18n.format("menu.options", new Object[0])));
|
||||
this.buttonList.add(new GuiButton(4, this.width / 2 + 2, i + 72 + 12, 98, 20, I18n.format("menu.quit", new Object[0])));
|
||||
this.buttonList.add(new GuiButtonLanguage(5, this.width / 2 - 124, i + 72 + 12));
|
||||
|
||||
Field threadLock = clazz.getDeclaredField(MCPNames.field("field_104025_t"));
|
||||
threadLock.setAccessible(true);
|
||||
Object object = threadLock.get(this);
|
||||
|
||||
Field field_92023_s = clazz.getDeclaredField(MCPNames.field("field_92023_s"));
|
||||
field_92023_s.setAccessible(true);
|
||||
|
||||
Field openGLWarning1 = clazz.getDeclaredField(MCPNames.field("openGLWarning1"));
|
||||
openGLWarning1.setAccessible(true);
|
||||
|
||||
Field openGLWarning2 = clazz.getDeclaredField(MCPNames.field("openGLWarning2"));
|
||||
openGLWarning2.setAccessible(true);
|
||||
|
||||
Field field_92024_r = clazz.getDeclaredField(MCPNames.field("field_92024_r"));
|
||||
field_92024_r.setAccessible(true);
|
||||
|
||||
Field field_92022_t = clazz.getDeclaredField(MCPNames.field("field_92022_t"));
|
||||
field_92022_t.setAccessible(true);
|
||||
|
||||
Field field_92021_u = clazz.getDeclaredField(MCPNames.field("field_92021_u"));
|
||||
field_92021_u.setAccessible(true);
|
||||
|
||||
Field field_92020_v = clazz.getDeclaredField(MCPNames.field("field_92020_v"));
|
||||
field_92020_v.setAccessible(true);
|
||||
|
||||
Field field_92019_w = clazz.getDeclaredField(MCPNames.field("field_92019_w"));
|
||||
field_92019_w.setAccessible(true);
|
||||
|
||||
synchronized (object)
|
||||
{
|
||||
field_92023_s.set(this, this.fontRendererObj.getStringWidth((String)openGLWarning1.get(this)));
|
||||
field_92024_r.set(this, this.fontRendererObj.getStringWidth((String)openGLWarning2.get(this)));
|
||||
int j = Math.max((Integer)(field_92023_s.get(this)), (Integer)(field_92024_r.get(this)));
|
||||
field_92022_t.set(this, (this.width - j) / 2);
|
||||
field_92021_u.set(this, ((GuiButton)this.buttonList.get(0)).yPosition - 24);
|
||||
field_92020_v.set(this, (Integer)field_92022_t.get(this) + j);
|
||||
field_92019_w.set(this, (Integer)field_92021_u.get(this) + 24);
|
||||
}
|
||||
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void actionPerformed(GuiButton button) throws IOException {
|
||||
if(button.id == REPLAY_MANAGER_ID) {
|
||||
this.mc.displayGuiScreen(new GuiReplayManager());
|
||||
}
|
||||
super.actionPerformed(button);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package eu.crushedpixel.replaymod.gui;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import com.mojang.realmsclient.util.Pair;
|
||||
|
||||
import eu.crushedpixel.replaymod.reflection.MCPNames;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.client.gui.GuiCustomizeSkin;
|
||||
import net.minecraft.client.gui.GuiOptionButton;
|
||||
import net.minecraft.client.gui.GuiOptions;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.renderer.texture.DynamicTexture;
|
||||
import net.minecraft.client.settings.GameSettings;
|
||||
|
||||
public class GuiCustomOptions extends GuiOptions {
|
||||
|
||||
public static GuiScreen getGuiScreen(GuiOptions go) {
|
||||
try {
|
||||
Class<GuiOptions> clazz = GuiOptions.class;
|
||||
Field guiScreen = clazz.getDeclaredField(MCPNames.field(MCPNames.field("field_146441_g")));
|
||||
guiScreen.setAccessible(true);
|
||||
Object obj = guiScreen.get(go);
|
||||
return (GuiScreen)obj;
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static GameSettings getGameSettings(GuiOptions go) {
|
||||
try {
|
||||
Class<GuiOptions> clazz = GuiOptions.class;
|
||||
Field gameSettings = clazz.getDeclaredField(MCPNames.field(MCPNames.field("field_146443_h")));
|
||||
gameSettings.setAccessible(true);
|
||||
Object obj = gameSettings.get(go);
|
||||
return (GameSettings)obj;
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public GuiCustomOptions(GuiScreen guiScreen, GameSettings gameSettings) {
|
||||
super(guiScreen, gameSettings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initGui() {
|
||||
super.initGui();
|
||||
buttonList.add(new GuiButton(9001, this.width / 2 - 155, this.height / 6 + 48 - 6 - 24, 310, 20, "Replay Mod Settings..."));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(GuiButton button) throws IOException {
|
||||
super.actionPerformed(button);
|
||||
if (button.enabled && button.id == 9001) { //Replay Mod Settings...
|
||||
this.mc.displayGuiScreen(new GuiReplaySettings(this));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package eu.crushedpixel.replaymod.gui;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import eu.crushedpixel.replaymod.replay.ReplayHandler;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.client.gui.GuiIngameMenu;
|
||||
import net.minecraft.client.gui.GuiMainMenu;
|
||||
import net.minecraft.client.multiplayer.WorldClient;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
|
||||
public class GuiExitReplay extends GuiIngameMenu {
|
||||
|
||||
@Override
|
||||
public void initGui()
|
||||
{
|
||||
this.buttonList.clear();
|
||||
byte b0 = -16;
|
||||
boolean flag = true;
|
||||
this.buttonList.add(new GuiButton(1, this.width / 2 - 100, this.height / 4 + 120 + b0, I18n.format("menu.returnToMenu", new Object[0])));
|
||||
|
||||
if (!this.mc.isIntegratedServerRunning())
|
||||
{
|
||||
((GuiButton)this.buttonList.get(0)).displayString = I18n.format("Exit Replay", new Object[0]);
|
||||
}
|
||||
|
||||
this.buttonList.add(new GuiButton(4, this.width / 2 - 100, this.height / 4 + 24 + b0, I18n.format("menu.returnToGame", new Object[0])));
|
||||
this.buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height / 4 + 96 + b0, 98, 20, I18n.format("menu.options", new Object[0])));
|
||||
this.buttonList.add(new GuiButton(12, this.width / 2 + 2, this.height / 4 + 96 + b0, 98, 20, I18n.format("fml.menu.modoptions")));
|
||||
GuiButton guibutton;
|
||||
this.buttonList.add(guibutton = new GuiButton(7, this.width / 2 - 100, this.height / 4 + 72 + b0, 200, 20, I18n.format("menu.shareToLan", new Object[0])));
|
||||
this.buttonList.add(new GuiButton(5, this.width / 2 - 100, this.height / 4 + 48 + b0, 98, 20, I18n.format("gui.achievements", new Object[0])));
|
||||
this.buttonList.add(new GuiButton(6, this.width / 2 + 2, this.height / 4 + 48 + b0, 98, 20, I18n.format("gui.stats", new Object[0])));
|
||||
guibutton.enabled = this.mc.isSingleplayer() && !this.mc.getIntegratedServer().getPublic();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void actionPerformed(GuiButton button) throws IOException
|
||||
{
|
||||
if(button.id == 1) {
|
||||
button.enabled = false;
|
||||
|
||||
Thread t = new Thread(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
ReplayHandler.endReplay();
|
||||
ReplayHandler.setSpeed(1f);
|
||||
|
||||
ReplayHandler.lastExit = System.currentTimeMillis();
|
||||
mc.theWorld.sendQuittingDisconnectingPacket();
|
||||
mc.displayGuiScreen(new GuiMainMenu());
|
||||
//mc.loadWorld((WorldClient)null);
|
||||
}
|
||||
});
|
||||
|
||||
t.run();
|
||||
|
||||
|
||||
} else {
|
||||
super.actionPerformed(button);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package eu.crushedpixel.replaymod.gui;
|
||||
|
||||
import org.lwjgl.input.Keyboard;
|
||||
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.gui.GuiTextField;
|
||||
|
||||
public class GuiMouseInput extends GuiScreen {
|
||||
|
||||
}
|
||||
102
src/main/java/eu/crushedpixel/replaymod/gui/GuiRenameReplay.java
Normal file
102
src/main/java/eu/crushedpixel/replaymod/gui/GuiRenameReplay.java
Normal file
@@ -0,0 +1,102 @@
|
||||
package eu.crushedpixel.replaymod.gui;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.gui.GuiTextField;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
|
||||
import eu.crushedpixel.replaymod.recording.ConnectionEventHandler;
|
||||
|
||||
public class GuiRenameReplay extends GuiScreen
|
||||
{
|
||||
private GuiScreen field_146585_a;
|
||||
private GuiTextField field_146583_f;
|
||||
private static final String __OBFID = "CL_00000709";
|
||||
|
||||
private File file;
|
||||
|
||||
public GuiRenameReplay(GuiScreen parent, File file)
|
||||
{
|
||||
this.field_146585_a = parent;
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
public void updateScreen()
|
||||
{
|
||||
this.field_146583_f.updateCursorCounter();
|
||||
}
|
||||
|
||||
public void initGui()
|
||||
{
|
||||
Keyboard.enableRepeatEvents(true);
|
||||
this.buttonList.clear();
|
||||
this.buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height / 4 + 96 + 12, I18n.format("Rename", new Object[0])));
|
||||
this.buttonList.add(new GuiButton(1, this.width / 2 - 100, this.height / 4 + 120 + 12, I18n.format("Cancel", new Object[0])));
|
||||
String s = FilenameUtils.getBaseName(file.getAbsolutePath());
|
||||
this.field_146583_f = new GuiTextField(2, this.fontRendererObj, this.width / 2 - 100, 60, 200, 20);
|
||||
this.field_146583_f.setFocused(true);
|
||||
this.field_146583_f.setText(s);
|
||||
}
|
||||
|
||||
public void onGuiClosed()
|
||||
{
|
||||
Keyboard.enableRepeatEvents(false);
|
||||
}
|
||||
|
||||
protected void actionPerformed(GuiButton button) throws IOException
|
||||
{
|
||||
if (button.enabled)
|
||||
{
|
||||
if (button.id == 1)
|
||||
{
|
||||
this.mc.displayGuiScreen(this.field_146585_a);
|
||||
}
|
||||
else if (button.id == 0)
|
||||
{
|
||||
File folder = new File("./replay_recordings/");
|
||||
folder.mkdirs();
|
||||
File initRenamed = new File(folder, this.field_146583_f.getText().trim()+ConnectionEventHandler.ZIP_FILE_EXTENSION.replaceAll("[^a-zA-Z0-9\\.\\-]", "_"));
|
||||
File renamed = initRenamed;
|
||||
int i=1;
|
||||
while(renamed.isFile()) {
|
||||
renamed = new File(initRenamed.getAbsolutePath()+"_"+i);
|
||||
i++;
|
||||
}
|
||||
file.renameTo(renamed);
|
||||
this.mc.displayGuiScreen(this.field_146585_a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void keyTyped(char typedChar, int keyCode) throws IOException
|
||||
{
|
||||
this.field_146583_f.textboxKeyTyped(typedChar, keyCode);
|
||||
((GuiButton)this.buttonList.get(0)).enabled = this.field_146583_f.getText().trim().length() > 0;
|
||||
|
||||
if (keyCode == 28 || keyCode == 156)
|
||||
{
|
||||
this.actionPerformed((GuiButton)this.buttonList.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException
|
||||
{
|
||||
super.mouseClicked(mouseX, mouseY, mouseButton);
|
||||
this.field_146583_f.mouseClicked(mouseX, mouseY, mouseButton);
|
||||
}
|
||||
|
||||
public void drawScreen(int mouseX, int mouseY, float partialTicks)
|
||||
{
|
||||
this.drawDefaultBackground();
|
||||
this.drawCenteredString(this.fontRendererObj, I18n.format("Rename World", new Object[0]), this.width / 2, 20, 16777215);
|
||||
this.drawString(this.fontRendererObj, I18n.format("Replay Name", new Object[0]), this.width / 2 - 100, 47, 10526880);
|
||||
this.field_146583_f.drawTextBox();
|
||||
super.drawScreen(mouseX, mouseY, partialTicks);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package eu.crushedpixel.replaymod.gui;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.Gui;
|
||||
import net.minecraft.client.gui.GuiListExtended.IGuiListEntry;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.WorldRenderer;
|
||||
import eu.crushedpixel.replaymod.recording.ReplayMetaData;
|
||||
|
||||
public class GuiReplayListEntry implements IGuiListEntry {
|
||||
|
||||
private Minecraft minecraft = Minecraft.getMinecraft();
|
||||
private final DateFormat dateFormat = new SimpleDateFormat();
|
||||
|
||||
private ReplayMetaData metaData;
|
||||
private String fileName;
|
||||
|
||||
|
||||
|
||||
public ReplayMetaData getMetaData() {
|
||||
return metaData;
|
||||
}
|
||||
|
||||
public void setMetaData(ReplayMetaData metaData) {
|
||||
this.metaData = metaData;
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public void setFileName(String fileName) {
|
||||
this.fileName = fileName;
|
||||
}
|
||||
|
||||
private boolean selected = false;
|
||||
private GuiReplayListExtended parent;
|
||||
|
||||
public GuiReplayListEntry(GuiReplayListExtended parent, String fileName, ReplayMetaData metaData) {
|
||||
this.metaData = metaData;
|
||||
this.fileName = fileName;
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawEntry(int slotIndex, int x, int y, int listWidth, int slotHeight, int mouseX, int mouseY, boolean isSelected)
|
||||
{
|
||||
minecraft.fontRendererObj.drawString(fileName, x + 3, y + 1, 16777215);
|
||||
|
||||
List<String> list = new ArrayList<String>();
|
||||
list.add(metaData.getServerName()+" ("+dateFormat.format(new Date(metaData.getDate()))+")");
|
||||
|
||||
list.add(String.format("%02dm%02ds",
|
||||
TimeUnit.MILLISECONDS.toMinutes(metaData.getDuration()),
|
||||
TimeUnit.MILLISECONDS.toSeconds(metaData.getDuration()) -
|
||||
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(metaData.getDuration()))
|
||||
));
|
||||
|
||||
for (int l1 = 0; l1 < Math.min(list.size(), 2); ++l1) {
|
||||
minecraft.fontRendererObj.drawString((String)list.get(l1), x + 3, y + 12 + minecraft.fontRendererObj.FONT_HEIGHT * l1, 8421504);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelected(int p_178011_1_, int p_178011_2_, int p_178011_3_) {}
|
||||
|
||||
@Override
|
||||
public boolean mousePressed(int p_148278_1_, int p_148278_2_,
|
||||
int p_148278_3_, int p_148278_4_, int p_148278_5_, int p_148278_6_) {
|
||||
for(int slot = 0; slot<parent.getSize(); slot++) {
|
||||
if(parent.getListEntry(slot) == this) {
|
||||
parent.elementClicked(slot, false, p_148278_5_, p_148278_6_);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(int slotIndex, int x, int y, int mouseEvent,
|
||||
int relativeX, int relativeY) {}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
package eu.crushedpixel.replaymod.gui;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.lwjgl.input.Mouse;
|
||||
|
||||
import eu.crushedpixel.replaymod.recording.ReplayMetaData;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiListExtended;
|
||||
import net.minecraft.client.gui.ServerListEntryLanScan;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.WorldRenderer;
|
||||
import net.minecraft.client.resources.ResourcePackListEntry;
|
||||
import net.minecraft.util.MathHelper;
|
||||
|
||||
public class GuiReplayListExtended extends GuiListExtended {
|
||||
|
||||
private GuiReplayManager parent;
|
||||
public int selected = -1;
|
||||
|
||||
public GuiReplayListExtended(GuiReplayManager parent, Minecraft mcIn, int p_i45010_2_,
|
||||
int p_i45010_3_, int p_i45010_4_, int p_i45010_5_, int p_i45010_6_) {
|
||||
super(mcIn, p_i45010_2_, p_i45010_3_, p_i45010_4_, p_i45010_5_, p_i45010_6_);
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void elementClicked(int slotIndex, boolean isDoubleClick,
|
||||
int mouseX, int mouseY) {
|
||||
super.elementClicked(slotIndex, isDoubleClick, mouseX, mouseY);
|
||||
this.selected = slotIndex;
|
||||
parent.setButtonsEnabled(true);
|
||||
if(isDoubleClick) {
|
||||
parent.loadReplay(slotIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void drawSelectionBox(int p_148120_1_, int p_148120_2_, int p_148120_3_, int p_148120_4_)
|
||||
{
|
||||
int i1 = this.getSize();
|
||||
Tessellator tessellator = Tessellator.getInstance();
|
||||
WorldRenderer worldrenderer = tessellator.getWorldRenderer();
|
||||
|
||||
for (int j1 = 0; j1 < i1; ++j1)
|
||||
{
|
||||
int k1 = p_148120_2_ + j1 * this.slotHeight + this.headerPadding;
|
||||
int l1 = this.slotHeight - 4;
|
||||
|
||||
if (k1 > this.bottom || k1 + l1 < this.top)
|
||||
{
|
||||
this.func_178040_a(j1, p_148120_1_, k1);
|
||||
}
|
||||
|
||||
if (this.showSelectionBox && selected == j1)
|
||||
{
|
||||
int i2 = this.left + (this.width / 2 - this.getListWidth() / 2);
|
||||
int j2 = this.left + this.width / 2 + this.getListWidth() / 2;
|
||||
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
GlStateManager.disableTexture2D();
|
||||
worldrenderer.startDrawingQuads();
|
||||
worldrenderer.setColorOpaque_I(8421504);
|
||||
worldrenderer.addVertexWithUV((double)i2, (double)(k1 + l1 + 2), 0.0D, 0.0D, 1.0D);
|
||||
worldrenderer.addVertexWithUV((double)j2, (double)(k1 + l1 + 2), 0.0D, 1.0D, 1.0D);
|
||||
worldrenderer.addVertexWithUV((double)j2, (double)(k1 - 2), 0.0D, 1.0D, 0.0D);
|
||||
worldrenderer.addVertexWithUV((double)i2, (double)(k1 - 2), 0.0D, 0.0D, 0.0D);
|
||||
worldrenderer.setColorOpaque_I(0);
|
||||
worldrenderer.addVertexWithUV((double)(i2 + 1), (double)(k1 + l1 + 1), 0.0D, 0.0D, 1.0D);
|
||||
worldrenderer.addVertexWithUV((double)(j2 - 1), (double)(k1 + l1 + 1), 0.0D, 1.0D, 1.0D);
|
||||
worldrenderer.addVertexWithUV((double)(j2 - 1), (double)(k1 - 1), 0.0D, 1.0D, 0.0D);
|
||||
worldrenderer.addVertexWithUV((double)(i2 + 1), (double)(k1 - 1), 0.0D, 0.0D, 0.0D);
|
||||
tessellator.draw();
|
||||
GlStateManager.enableTexture2D();
|
||||
}
|
||||
|
||||
this.drawSlot(j1, p_148120_1_, k1, l1, p_148120_3_, p_148120_4_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private List<GuiReplayListEntry> entries = new ArrayList<GuiReplayListEntry>();
|
||||
|
||||
public void clearEntries() {
|
||||
entries = new ArrayList<GuiReplayListEntry>();
|
||||
}
|
||||
|
||||
public void addEntry(String fileName, ReplayMetaData metaData) {
|
||||
entries.add(new GuiReplayListEntry(this, fileName, metaData));
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuiReplayListEntry getListEntry(int index) {
|
||||
return entries.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getSize() {
|
||||
return entries.size();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void handleMouseInput()
|
||||
{
|
||||
if (this.isMouseYWithinSlotBounds(this.mouseY))
|
||||
{
|
||||
if (Mouse.isButtonDown(0))
|
||||
{
|
||||
if (this.initialClickY == -1.0F)
|
||||
{
|
||||
int i2 = this.getScrollBarX();
|
||||
int i1 = i2 + 6;
|
||||
|
||||
boolean flag = true;
|
||||
|
||||
if (this.mouseY >= this.top && this.mouseY <= this.bottom && this.mouseX <= i1)
|
||||
{
|
||||
int i = this.width / 2 - this.getListWidth() / 2;
|
||||
int j = this.width / 2 + this.getListWidth() / 2;
|
||||
int k = this.mouseY - this.top - this.headerPadding + (int)this.amountScrolled - 4;
|
||||
int l = k / this.slotHeight;
|
||||
|
||||
if (this.mouseX >= i && this.mouseX <= j && l >= 0 && k >= 0 && l < this.getSize())
|
||||
{
|
||||
boolean flag1 = l == this.selectedElement && Minecraft.getSystemTime() - this.lastClicked < 250L;
|
||||
this.elementClicked(l, flag1, this.mouseX, this.mouseY);
|
||||
this.selectedElement = l;
|
||||
this.lastClicked = Minecraft.getSystemTime();
|
||||
}
|
||||
else if (this.mouseX >= i && this.mouseX <= j && k < 0)
|
||||
{
|
||||
this.func_148132_a(this.mouseX - i, this.mouseY - this.top + (int)this.amountScrolled - 4);
|
||||
flag = false;
|
||||
}
|
||||
|
||||
if (this.mouseX >= i2 && this.mouseX <= i1)
|
||||
{
|
||||
this.scrollMultiplier = -1.0F;
|
||||
int j1 = this.func_148135_f();
|
||||
|
||||
if (j1 < 1)
|
||||
{
|
||||
j1 = 1;
|
||||
}
|
||||
|
||||
int k1 = (int)((float)((this.bottom - this.top) * (this.bottom - this.top)) / (float)this.getContentHeight());
|
||||
k1 = MathHelper.clamp_int(k1, 32, this.bottom - this.top - 8);
|
||||
this.scrollMultiplier /= (float)(this.bottom - this.top - k1) / (float)j1;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.scrollMultiplier = 1.0F;
|
||||
}
|
||||
|
||||
if (flag)
|
||||
{
|
||||
this.initialClickY = (float)this.mouseY;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.initialClickY = -2.0F;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.initialClickY = -2.0F;
|
||||
}
|
||||
}
|
||||
else if (this.initialClickY >= 0.0F)
|
||||
{
|
||||
this.amountScrolled -= ((float)this.mouseY - this.initialClickY) * this.scrollMultiplier;
|
||||
this.initialClickY = (float)this.mouseY;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.initialClickY = -1.0F;
|
||||
}
|
||||
|
||||
int l1 = Mouse.getEventDWheel();
|
||||
|
||||
if (l1 != 0)
|
||||
{
|
||||
if (l1 > 0)
|
||||
{
|
||||
l1 = -1;
|
||||
}
|
||||
else if (l1 < 0)
|
||||
{
|
||||
l1 = 1;
|
||||
}
|
||||
|
||||
this.amountScrolled += (float)(l1 * this.slotHeight / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,286 @@
|
||||
package eu.crushedpixel.replaymod.gui;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.gui.GuiYesNo;
|
||||
import net.minecraft.client.gui.GuiYesNoCallback;
|
||||
import net.minecraft.client.network.NetHandlerLoginClient;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.network.EnumConnectionState;
|
||||
import net.minecraft.network.NetworkManager;
|
||||
import net.minecraft.network.handshake.client.C00Handshake;
|
||||
import net.minecraft.network.login.client.C00PacketLoginStart;
|
||||
import net.minecraft.util.Util;
|
||||
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.lwjgl.Sys;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.mojang.realmsclient.util.Pair;
|
||||
|
||||
import eu.crushedpixel.replaymod.recording.ConnectionEventHandler;
|
||||
import eu.crushedpixel.replaymod.recording.ReplayMetaData;
|
||||
import eu.crushedpixel.replaymod.replay.ReplayHandler;
|
||||
import eu.crushedpixel.replaymod.replay.ReplaySender;
|
||||
|
||||
public class GuiReplayManager extends GuiScreen implements GuiYesNoCallback {
|
||||
|
||||
private GuiScreen parentScreen;
|
||||
private GuiButton btnEditServer;
|
||||
private GuiButton btnSelectServer;
|
||||
private GuiButton btnDeleteServer;
|
||||
private String hoveringText;
|
||||
private boolean initialized;
|
||||
private GuiReplayListExtended replayGuiList;
|
||||
private List<Pair<File, ReplayMetaData>> replayFileList = new ArrayList<Pair<File, ReplayMetaData>>();
|
||||
private GuiButton loadButton, folderButton, renameButton, deleteButton, cancelButton, settingsButton;
|
||||
|
||||
private static Gson gson = new Gson();
|
||||
private boolean replaying = false;
|
||||
|
||||
private static final int LOAD_BUTTON_ID = 9001;
|
||||
private static final int FOLDER_BUTTON_ID = 9002;
|
||||
private static final int RENAME_BUTTON_ID = 9003;
|
||||
private static final int DELETE_BUTTON_ID = 9004;
|
||||
private static final int SETTINGS_BUTTON_ID = 9005;
|
||||
private static final int CANCEL_BUTTON_ID = 9006;
|
||||
|
||||
private boolean delete_file = false;
|
||||
|
||||
private void reloadFiles() {
|
||||
replayGuiList.clearEntries();
|
||||
replayFileList = new ArrayList<Pair<File, ReplayMetaData>>();
|
||||
|
||||
File folder = new File("./replay_recordings/");
|
||||
folder.mkdirs();
|
||||
|
||||
for(File file : folder.listFiles()) {
|
||||
if(("."+FilenameUtils.getExtension(file.getAbsolutePath())).equals(ConnectionEventHandler.ZIP_FILE_EXTENSION)) {
|
||||
try {
|
||||
ZipFile archive = new ZipFile(file);
|
||||
ZipArchiveEntry recfile = archive.getEntry("recording"+ConnectionEventHandler.TEMP_FILE_EXTENSION);
|
||||
ZipArchiveEntry metadata = archive.getEntry("metaData"+ConnectionEventHandler.JSON_FILE_EXTENSION);
|
||||
|
||||
InputStream is = archive.getInputStream(metadata);
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(is));
|
||||
|
||||
String json = br.readLine();
|
||||
|
||||
ReplayMetaData metaData = gson.fromJson(json, ReplayMetaData.class);
|
||||
|
||||
replayFileList.add(Pair.of(file, metaData));
|
||||
|
||||
archive.close();
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(replayFileList, new FileAgeComparator());
|
||||
|
||||
for(Pair<File, ReplayMetaData> p : replayFileList) {
|
||||
replayGuiList.addEntry(FilenameUtils.getBaseName(p.first().getName()), p.second());
|
||||
}
|
||||
}
|
||||
|
||||
public class FileAgeComparator implements Comparator<Pair<File, ReplayMetaData>> {
|
||||
|
||||
@Override
|
||||
public int compare(Pair<File, ReplayMetaData> o1, Pair<File, ReplayMetaData> o2) {
|
||||
return (int) (o2.second().getDate() - o1.second().getDate());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void initGui()
|
||||
{
|
||||
replayGuiList = new GuiReplayListExtended(this, this.mc, this.width, this.height, 32, this.height - 64, 36);
|
||||
Keyboard.enableRepeatEvents(true);
|
||||
this.buttonList.clear();
|
||||
|
||||
if (!this.initialized) {
|
||||
this.initialized = true;
|
||||
}
|
||||
else {
|
||||
this.replayGuiList.setDimensions(this.width, this.height, 32, this.height - 64);
|
||||
}
|
||||
|
||||
reloadFiles();
|
||||
this.createButtons();
|
||||
}
|
||||
|
||||
private void createButtons() {
|
||||
this.buttonList.add(loadButton = new GuiButton(LOAD_BUTTON_ID, this.width / 2 - 154, this.height - 52, 150, 20, I18n.format("Load Replay", new Object[0])));
|
||||
this.buttonList.add(folderButton = new GuiButton(FOLDER_BUTTON_ID, this.width / 2 + 4, this.height - 52, 150, 20, I18n.format("Open Replay Folder...", new Object[0])));
|
||||
this.buttonList.add(renameButton = new GuiButton(RENAME_BUTTON_ID, this.width / 2 - 154, this.height - 28, 72, 20, I18n.format("Rename", new Object[0])));
|
||||
this.buttonList.add(deleteButton = new GuiButton(DELETE_BUTTON_ID, this.width / 2 - 76, this.height - 28, 72, 20, I18n.format("Delete", new Object[0])));
|
||||
this.buttonList.add(settingsButton = new GuiButton(SETTINGS_BUTTON_ID, this.width / 2 + 4, this.height - 28, 72, 20, I18n.format("Settings", new Object[0])));
|
||||
this.buttonList.add(cancelButton = new GuiButton(CANCEL_BUTTON_ID, this.width / 2 + 4 + 78, this.height - 28, 72, 20, I18n.format("Cancel", new Object[0])));
|
||||
setButtonsEnabled(false);
|
||||
}
|
||||
|
||||
public void handleMouseInput() throws IOException
|
||||
{
|
||||
super.handleMouseInput();
|
||||
this.replayGuiList.handleMouseInput();
|
||||
}
|
||||
|
||||
protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException
|
||||
{
|
||||
super.mouseClicked(mouseX, mouseY, mouseButton);
|
||||
this.replayGuiList.mouseClicked(mouseX, mouseY, mouseButton);
|
||||
}
|
||||
|
||||
protected void mouseReleased(int mouseX, int mouseY, int state)
|
||||
{
|
||||
super.mouseReleased(mouseX, mouseY, state);
|
||||
this.replayGuiList.mouseReleased(mouseX, mouseY, state);
|
||||
}
|
||||
|
||||
public void drawScreen(int mouseX, int mouseY, float partialTicks)
|
||||
{
|
||||
this.hoveringText = null;
|
||||
this.drawDefaultBackground();
|
||||
this.replayGuiList.drawScreen(mouseX, mouseY, partialTicks);
|
||||
this.drawCenteredString(this.fontRendererObj, I18n.format("Replay Manager", new Object[0]), this.width / 2, 20, 16777215);
|
||||
super.drawScreen(mouseX, mouseY, partialTicks);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void actionPerformed(GuiButton button) throws IOException
|
||||
{
|
||||
if (button.enabled) {
|
||||
if(button.id == LOAD_BUTTON_ID) {
|
||||
loadReplay(replayGuiList.selected);
|
||||
}
|
||||
else if (button.id == CANCEL_BUTTON_ID)
|
||||
{
|
||||
mc.displayGuiScreen(parentScreen);
|
||||
}
|
||||
else if (button.id == DELETE_BUTTON_ID)
|
||||
{
|
||||
String s = replayGuiList.getListEntry(replayGuiList.selected).getFileName();
|
||||
|
||||
if (s != null)
|
||||
{
|
||||
delete_file = true;
|
||||
GuiYesNo guiyesno = getYesNoGui(this, s, 1);
|
||||
this.mc.displayGuiScreen(guiyesno);
|
||||
}
|
||||
}
|
||||
else if(button.id == SETTINGS_BUTTON_ID) {
|
||||
this.mc.displayGuiScreen(new GuiReplaySettings(this));
|
||||
}
|
||||
else if(button.id == RENAME_BUTTON_ID)
|
||||
{
|
||||
File file = replayFileList.get(replayGuiList.selected).first();
|
||||
this.mc.displayGuiScreen(new GuiRenameReplay(this, file));
|
||||
}
|
||||
else if(button.id == FOLDER_BUTTON_ID)
|
||||
{
|
||||
File file1 = new File("./replay_recordings/");
|
||||
file1.mkdirs();
|
||||
String s = file1.getAbsolutePath();
|
||||
|
||||
if (Util.getOSType() == Util.EnumOS.OSX)
|
||||
{
|
||||
try
|
||||
{
|
||||
Runtime.getRuntime().exec(new String[] {"/usr/bin/open", s});
|
||||
return;
|
||||
}
|
||||
catch (IOException ioexception1) {}
|
||||
}
|
||||
else if (Util.getOSType() == Util.EnumOS.WINDOWS)
|
||||
{
|
||||
String s1 = String.format("cmd.exe /C start \"Open file\" \"%s\"", new Object[] {s});
|
||||
|
||||
try
|
||||
{
|
||||
Runtime.getRuntime().exec(s1);
|
||||
return;
|
||||
}
|
||||
catch (IOException ioexception) {}
|
||||
}
|
||||
|
||||
boolean flag = false;
|
||||
|
||||
try
|
||||
{
|
||||
Class oclass = Class.forName("java.awt.Desktop");
|
||||
Object object = oclass.getMethod("getDesktop", new Class[0]).invoke((Object)null, new Object[0]);
|
||||
oclass.getMethod("browse", new Class[] {URI.class}).invoke(object, new Object[] {file1.toURI()});
|
||||
}
|
||||
catch (Throwable throwable)
|
||||
{
|
||||
flag = true;
|
||||
}
|
||||
|
||||
if (flag)
|
||||
{
|
||||
Sys.openURL("file://" + s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void confirmClicked(boolean result, int id) {
|
||||
if (this.delete_file)
|
||||
{
|
||||
this.delete_file = false;
|
||||
|
||||
if (result)
|
||||
{
|
||||
replayFileList.get(replayGuiList.selected).first().delete();
|
||||
replayFileList.remove(replayGuiList.selected);
|
||||
}
|
||||
|
||||
this.mc.displayGuiScreen(this);
|
||||
}
|
||||
}
|
||||
|
||||
public static GuiYesNo getYesNoGui(GuiYesNoCallback p_152129_0_, String file, int p_152129_2_)
|
||||
{
|
||||
String s1 = I18n.format("Are you sure you want to delete this replay?", new Object[0]);
|
||||
String s2 = "\'" + file + "\' " + I18n.format("will be lost forever! (A long time!)", new Object[0]);
|
||||
String s3 = I18n.format("Delete", new Object[0]);
|
||||
String s4 = I18n.format("Cancel", new Object[0]);
|
||||
GuiYesNo guiyesno = new GuiYesNo(p_152129_0_, s1, s2, s3, s4, p_152129_2_);
|
||||
return guiyesno;
|
||||
}
|
||||
|
||||
public void setButtonsEnabled(boolean b) {
|
||||
loadButton.enabled = b;
|
||||
renameButton.enabled = b;
|
||||
deleteButton.enabled = b;
|
||||
}
|
||||
|
||||
public void loadReplay(int id)
|
||||
{
|
||||
mc.displayGuiScreen((GuiScreen)null);
|
||||
|
||||
try {
|
||||
ReplayHandler.startReplay(replayFileList.get(id).first());
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
package eu.crushedpixel.replaymod.gui;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.client.gui.GuiOptionButton;
|
||||
import net.minecraft.client.gui.GuiOptionSlider;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraftforge.fml.client.FMLClientHandler;
|
||||
import eu.crushedpixel.replaymod.ReplayMod;
|
||||
import eu.crushedpixel.replaymod.settings.ReplaySettings;
|
||||
|
||||
public class GuiReplaySettings extends GuiScreen {
|
||||
|
||||
private GuiScreen parentGuiScreen;
|
||||
protected String screenTitle = "Replay Mod Settings";
|
||||
|
||||
private static final int MAXSIZE_SLIDER_ID = 9003;
|
||||
private static final int RECORDSERVER_ID = 9004;
|
||||
private static final int RECORDSP_ID = 9005;
|
||||
private static final int SEND_CHAT = 9006;
|
||||
private static final int FORCE_LINEAR = 9007;
|
||||
|
||||
private GuiButton recordServerButton, recordSPButton, sendChatButton, linearButton;
|
||||
|
||||
public GuiReplaySettings(GuiScreen parentGuiScreen)
|
||||
{
|
||||
this.parentGuiScreen = parentGuiScreen;
|
||||
}
|
||||
|
||||
public void initGui() {
|
||||
this.screenTitle = I18n.format("Replay Mod Settings", new Object[0]);
|
||||
this.buttonList.clear();
|
||||
this.buttonList.add(new GuiButton(200, this.width / 2 - 100, this.height - 27, I18n.format("gui.done", new Object[0])));
|
||||
|
||||
Map<String, Object> aoptions = ReplayMod.replaySettings.getOptions();
|
||||
|
||||
int k = 0;
|
||||
int i = 0;
|
||||
for (Entry<String, Object> e : aoptions.entrySet()) {
|
||||
if(e.getKey().equals("Maximum File Size")) {
|
||||
float minValue = -1;
|
||||
float maxValue = 10000;
|
||||
float valueSteps = 1;
|
||||
|
||||
int val = (Integer)e.getValue();
|
||||
this.buttonList.add(new GuiSizeLimitOptionSlider(MAXSIZE_SLIDER_ID, this.width / 2 - 155 + i % 2 * 160, this.height / 6 + 24 * (i >> 1), 0, 39, 1, val, e.getKey()));
|
||||
|
||||
} else if(e.getKey().equals("Enable Notifications")) {
|
||||
sendChatButton = new GuiButton(SEND_CHAT, this.width / 2 - 155 + i % 2 * 160, this.height / 6 + 24 * (i >> 1), 150, 20, "Enable Notifications: "+onOff((Boolean)e.getValue()));
|
||||
this.buttonList.add(sendChatButton);
|
||||
} else if(e.getKey().equals("Record Server")) {
|
||||
recordServerButton = new GuiButton(RECORDSERVER_ID, this.width / 2 - 155 + i % 2 * 160, this.height / 6 + 24 * (i >> 1), 150, 20, "Record Server: "+onOff((Boolean)e.getValue()));
|
||||
this.buttonList.add(recordServerButton);
|
||||
} else if(e.getKey().equals("Record Singleplayer")) {
|
||||
recordSPButton = new GuiButton(RECORDSP_ID, this.width / 2 - 155 + i % 2 * 160, this.height / 6 + 24 * (i >> 1), 150, 20, "Record Singleplayer: "+onOff((Boolean)e.getValue()));
|
||||
this.buttonList.add(recordSPButton);
|
||||
} else if(e.getKey().equals("Force Linear Movement")) {
|
||||
linearButton = new GuiButton(FORCE_LINEAR, this.width / 2 - 155 + i % 2 * 160, this.height / 6 + 24 * (i >> 1), 150, 20, "Camera Path: "+linearOnOff((Boolean)e.getValue()));
|
||||
this.buttonList.add(linearButton);
|
||||
}
|
||||
|
||||
++i;
|
||||
++k;
|
||||
}
|
||||
|
||||
if (i % 2 == 1)
|
||||
{
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
private String onOff(boolean on) {
|
||||
return on ? "ON" : "OFF";
|
||||
}
|
||||
|
||||
private String linearOnOff(boolean on) {
|
||||
return on ? "Linear" : "Cubic";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawScreen(int mouseX, int mouseY, float partialTicks)
|
||||
{
|
||||
this.drawDefaultBackground();
|
||||
this.drawCenteredString(this.fontRendererObj, "Replay Mod Settings", this.width / 2, 20, 16777215);
|
||||
if (FMLClientHandler.instance().getClient().thePlayer != null) {
|
||||
this.drawCenteredString(this.fontRendererObj, "WARNING: These settings are going to be", this.width / 2, 180, Color.RED.getRGB());
|
||||
this.drawCenteredString(this.fontRendererObj, "applied the next time you join a world.", this.width / 2, 190, Color.RED.getRGB());
|
||||
}
|
||||
super.drawScreen(mouseX, mouseY, partialTicks);
|
||||
}
|
||||
|
||||
|
||||
protected void actionPerformed(GuiButton button) throws IOException
|
||||
{
|
||||
if (button.enabled)
|
||||
{
|
||||
switch(button.id) {
|
||||
case 200:
|
||||
this.mc.displayGuiScreen(this.parentGuiScreen);
|
||||
break;
|
||||
case RECORDSERVER_ID:
|
||||
boolean enabled = ReplayMod.replaySettings.isEnableRecordingServer();
|
||||
enabled = !enabled;
|
||||
recordServerButton.displayString = "Record Server: "+onOff(enabled);
|
||||
ReplayMod.replaySettings.setEnableRecordingServer(enabled);
|
||||
break;
|
||||
case RECORDSP_ID:
|
||||
enabled = ReplayMod.replaySettings.isEnableRecordingSingleplayer();
|
||||
enabled = !enabled;
|
||||
recordSPButton.displayString = "Record Singleplayer: "+onOff(enabled);
|
||||
ReplayMod.replaySettings.setEnableRecordingSingleplayer(enabled);
|
||||
break;
|
||||
case SEND_CHAT:
|
||||
enabled = ReplayMod.replaySettings.isShowNotifications();
|
||||
enabled = !enabled;
|
||||
sendChatButton.displayString = "Enable Notifications: "+onOff(enabled);
|
||||
ReplayMod.replaySettings.setShowNotifications(enabled);
|
||||
break;
|
||||
case FORCE_LINEAR:
|
||||
enabled = ReplayMod.replaySettings.isLinearMovement();
|
||||
enabled = !enabled;
|
||||
linearButton.displayString = "Camera Path: "+linearOnOff(enabled);
|
||||
ReplayMod.replaySettings.setLinearMovement(enabled);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
package eu.crushedpixel.replaymod.gui;
|
||||
|
||||
import eu.crushedpixel.replaymod.ReplayMod;
|
||||
import eu.crushedpixel.replaymod.replay.ReplayHandler;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.settings.GameSettings;
|
||||
import net.minecraft.util.MathHelper;
|
||||
import net.minecraftforge.fml.client.FMLClientHandler;
|
||||
|
||||
public class GuiReplaySpeedSlider extends GuiButton {
|
||||
|
||||
private float sliderValue;
|
||||
|
||||
private float valueStep, valueMin, valueMax;
|
||||
private String displayKey;
|
||||
private boolean dragging = false;
|
||||
|
||||
public GuiReplaySpeedSlider(int buttonId, int p_i45017_2_, int p_i45017_3_, String displayKey)
|
||||
{
|
||||
super(buttonId, p_i45017_2_, p_i45017_3_, 150, 20, "");
|
||||
sliderValue = (9f/38f);
|
||||
|
||||
this.width = 100;
|
||||
this.valueMin = 1;
|
||||
this.valueMax = 38;
|
||||
this.valueStep = 1;
|
||||
this.displayString = displayKey+": 1x";
|
||||
this.displayKey = displayKey;
|
||||
|
||||
Minecraft.getMinecraft().getTextureManager().bindTexture(buttonTextures);
|
||||
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
this.drawTexturedModalRect(this.xPosition + (int)(sliderValue * (float)(this.width - 8)), this.yPosition, 0, 66, 4, 20);
|
||||
this.drawTexturedModalRect(this.xPosition + (int)(sliderValue * (float)(this.width - 8)) + 4, this.yPosition, 196, 66, 4, 20);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getHoverState(boolean mouseOver) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawButton(Minecraft mc, int mouseX, int mouseY)
|
||||
{
|
||||
if (this.visible)
|
||||
{
|
||||
try {
|
||||
FontRenderer fontrenderer = mc.fontRendererObj;
|
||||
mc.getTextureManager().bindTexture(buttonTextures);
|
||||
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
this.hovered = mouseX >= this.xPosition && mouseY >= this.yPosition && mouseX < this.xPosition + this.width && mouseY < this.yPosition + this.height;
|
||||
int k = this.getHoverState(this.hovered);
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.tryBlendFuncSeparate(770, 771, 1, 0);
|
||||
GlStateManager.blendFunc(770, 771);
|
||||
this.drawTexturedModalRect(this.xPosition, this.yPosition, 0, 46 + k * 20, this.width / 2, this.height);
|
||||
this.drawTexturedModalRect(this.xPosition + this.width / 2, this.yPosition, 200 - this.width / 2, 46 + k * 20, this.width / 2, this.height);
|
||||
this.mouseDragged(mc, mouseX, mouseY);
|
||||
int l = 14737632;
|
||||
|
||||
if (packedFGColour != 0)
|
||||
{
|
||||
l = packedFGColour;
|
||||
}
|
||||
else if (!this.enabled)
|
||||
{
|
||||
l = 10526880;
|
||||
}
|
||||
else if (this.hovered && FMLClientHandler.instance().isGUIOpen(GuiMouseInput.class))
|
||||
{
|
||||
l = 16777120;
|
||||
}
|
||||
|
||||
this.drawCenteredString(fontrenderer, this.displayString, this.xPosition + this.width / 2, this.yPosition + (this.height - 8) / 2, l);
|
||||
} catch(Exception e) {}
|
||||
}
|
||||
}
|
||||
|
||||
private String translate(float f) {
|
||||
return f+"x";
|
||||
}
|
||||
|
||||
public static float convertScaleRet(float value) {
|
||||
if(value <= 1) {
|
||||
return Math.round(value*10);
|
||||
}
|
||||
float steps = value-1;
|
||||
return Math.round(steps/0.25f);
|
||||
}
|
||||
|
||||
public static float convertScale(float value) {
|
||||
if(value == 10) {
|
||||
return 1;
|
||||
}
|
||||
if(value <= 9) {
|
||||
return value/10f;
|
||||
}
|
||||
return 1+(0.25f*(value-10));
|
||||
}
|
||||
|
||||
|
||||
public double getSliderValue() {
|
||||
return convertScale(normalizedToReal(sliderValue));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void mouseDragged(Minecraft mc, int mouseX, int mouseY) {
|
||||
if (this.visible) {
|
||||
try {
|
||||
if(this.dragging) {
|
||||
sliderValue = (float)(mouseX - (this.xPosition + 4)) / (float)(this.width - 8);
|
||||
sliderValue = MathHelper.clamp_float(sliderValue, 0.0F, 1.0F);
|
||||
float f = denormalizeValue(sliderValue);
|
||||
sliderValue = normalizeValue(f);
|
||||
if(ReplayHandler.getSpeed() != 0) {
|
||||
ReplayHandler.setSpeed(convertScale(normalizedToReal(sliderValue)));
|
||||
}
|
||||
this.displayString = displayKey+": "+translate(convertScale(normalizedToReal(sliderValue)));
|
||||
}
|
||||
|
||||
mc.getTextureManager().bindTexture(buttonTextures);
|
||||
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
this.drawTexturedModalRect(this.xPosition + (int)(sliderValue * (float)(this.width - 8)), this.yPosition, 0, 66, 4, 20);
|
||||
this.drawTexturedModalRect(this.xPosition + (int)(sliderValue * (float)(this.width - 8)) + 4, this.yPosition, 196, 66, 4, 20);
|
||||
} catch(Exception e) {}
|
||||
}
|
||||
}
|
||||
|
||||
public float normalizeValue(float p_148266_1_)
|
||||
{
|
||||
return MathHelper.clamp_float((this.snapToStepClamp(p_148266_1_) - this.valueMin) / (this.valueMax - this.valueMin), 0.0F, 1.0F);
|
||||
}
|
||||
|
||||
public float realToNormalized(float value) {
|
||||
float min = 0 - valueMin;
|
||||
float max = valueMax + min;
|
||||
|
||||
return value/(max) - min;
|
||||
}
|
||||
|
||||
public float denormalizeValue(float p_148262_1_)
|
||||
{
|
||||
return this.snapToStepClamp(this.valueMin + (this.valueMax - this.valueMin) * MathHelper.clamp_float(p_148262_1_, 0.0F, 1.0F));
|
||||
}
|
||||
|
||||
public float snapToStepClamp(float p_148268_1_)
|
||||
{
|
||||
p_148268_1_ = this.snapToStep(p_148268_1_);
|
||||
return MathHelper.clamp_float(p_148268_1_, this.valueMin, this.valueMax);
|
||||
}
|
||||
|
||||
protected float snapToStep(float p_148264_1_)
|
||||
{
|
||||
if (this.valueStep > 0.0F)
|
||||
{
|
||||
p_148264_1_ = this.valueStep * (float)Math.round(p_148264_1_ / this.valueStep);
|
||||
}
|
||||
|
||||
return p_148264_1_;
|
||||
}
|
||||
|
||||
public float normalizedToReal(float value) {
|
||||
float min = 0 - valueMin;
|
||||
float max = valueMax + min;
|
||||
|
||||
return Math.round(value*(max) - min);
|
||||
}
|
||||
|
||||
public boolean mousePressed(Minecraft mc, int mouseX, int mouseY)
|
||||
{
|
||||
if (super.mousePressed(mc, mouseX, mouseY))
|
||||
{
|
||||
this.dragging = true;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void mouseReleased(int mouseX, int mouseY)
|
||||
{
|
||||
this.dragging = false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
package eu.crushedpixel.replaymod.gui;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.settings.GameSettings;
|
||||
import net.minecraft.util.MathHelper;
|
||||
import eu.crushedpixel.replaymod.ReplayMod;
|
||||
|
||||
public class GuiSizeLimitOptionSlider extends GuiButton {
|
||||
|
||||
public GuiSizeLimitOptionSlider(int buttonId, int p_i45017_2_, int p_i45017_3_, float valueMin, float valueMax, float valueStep, int sliderValue, String displayKey)
|
||||
{
|
||||
super(buttonId, p_i45017_2_, p_i45017_3_, 150, 20, "");
|
||||
this.valueMin = valueMin;
|
||||
this.valueMax = valueMax;
|
||||
this.valueStep = valueStep;
|
||||
this.sliderValue = sliderValue;
|
||||
this.displayString = displayKey+": "+translate(sliderValue);
|
||||
this.displayKey = displayKey;
|
||||
|
||||
float val = realToNormalized(convertScaleRet(sliderValue));
|
||||
|
||||
|
||||
this.sliderValue = val;
|
||||
}
|
||||
|
||||
private float sliderValue;
|
||||
public boolean dragging;
|
||||
private GameSettings.Options options;
|
||||
|
||||
private float valueStep, valueMin, valueMax;
|
||||
private String displayKey;
|
||||
|
||||
|
||||
protected int getHoverState(boolean mouseOver) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void mouseDragged(Minecraft mc, int mouseX, int mouseY) {
|
||||
if (this.visible) {
|
||||
if (this.dragging) {
|
||||
sliderValue = (float)(mouseX - (this.xPosition + 4)) / (float)(this.width - 8);
|
||||
sliderValue = MathHelper.clamp_float(sliderValue, 0.0F, 1.0F);
|
||||
float f = denormalizeValue(sliderValue);
|
||||
sliderValue = normalizeValue(f);
|
||||
this.displayString = displayKey+": "+translate(convertScale(normalizedToReal(sliderValue)));
|
||||
ReplayMod.replaySettings.setMaximumFileSize(convertScale(normalizedToReal(sliderValue)));
|
||||
}
|
||||
|
||||
mc.getTextureManager().bindTexture(buttonTextures);
|
||||
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
this.drawTexturedModalRect(this.xPosition + (int)(sliderValue * (float)(this.width - 8)), this.yPosition, 0, 66, 4, 20);
|
||||
this.drawTexturedModalRect(this.xPosition + (int)(sliderValue * (float)(this.width - 8)) + 4, this.yPosition, 196, 66, 4, 20);
|
||||
}
|
||||
}
|
||||
|
||||
public static String translate(int value) {
|
||||
if(value == 0) {
|
||||
return "Unlimited";
|
||||
} else {
|
||||
return convertToStringRepresentation(value);
|
||||
}
|
||||
}
|
||||
|
||||
public static int convertScale(float value) {
|
||||
if(value == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
if(value > 20) {
|
||||
value = 1024+((value-20)*1024);
|
||||
} else {
|
||||
value = value*50;
|
||||
}
|
||||
if(value == 1000) {
|
||||
value = 1024;
|
||||
}
|
||||
return Math.round(value);
|
||||
}
|
||||
}
|
||||
|
||||
public static int convertScaleRet(float value) {
|
||||
if(value == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
if(value >= 1024) {
|
||||
value = 20+((value-1024)/1024);
|
||||
} else {
|
||||
value = value/50;
|
||||
}
|
||||
if(value == 1024) {
|
||||
value = 1000;
|
||||
}
|
||||
return Math.round(value);
|
||||
}
|
||||
}
|
||||
|
||||
public static String convertToStringRepresentation(final long value){
|
||||
long M = 1;
|
||||
long G = 1024;
|
||||
long T = G * 1024;
|
||||
|
||||
final long[] dividers = new long[] { T, G, M};
|
||||
final String[] units = new String[] { "TB", "GB", "MB"};
|
||||
if(value < 1)
|
||||
throw new IllegalArgumentException("Invalid file size: " + value);
|
||||
String result = null;
|
||||
for(int i = 0; i < dividers.length; i++){
|
||||
final long divider = dividers[i];
|
||||
if(value >= divider){
|
||||
result = format(value, divider, units[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static String format(final long value,
|
||||
final long divider,
|
||||
final String unit){
|
||||
final double result =
|
||||
divider > 1 ? (double) value / (double) divider : (double) value;
|
||||
return String.format("%.0f %s", Double.valueOf(result), unit);
|
||||
}
|
||||
|
||||
public float normalizedToReal(float value) {
|
||||
float min = 0 - valueMin;
|
||||
float max = valueMax + min;
|
||||
|
||||
return Math.round(value*(max) - min);
|
||||
}
|
||||
|
||||
public float realToNormalized(float value) {
|
||||
float min = 0 - valueMin;
|
||||
float max = valueMax + min;
|
||||
|
||||
return value/(max) - min;
|
||||
}
|
||||
|
||||
public float normalizeValue(float p_148266_1_)
|
||||
{
|
||||
return MathHelper.clamp_float((this.snapToStepClamp(p_148266_1_) - this.valueMin) / (this.valueMax - this.valueMin), 0.0F, 1.0F);
|
||||
}
|
||||
|
||||
public float denormalizeValue(float p_148262_1_)
|
||||
{
|
||||
return this.snapToStepClamp(this.valueMin + (this.valueMax - this.valueMin) * MathHelper.clamp_float(p_148262_1_, 0.0F, 1.0F));
|
||||
}
|
||||
|
||||
public float snapToStepClamp(float p_148268_1_)
|
||||
{
|
||||
p_148268_1_ = this.snapToStep(p_148268_1_);
|
||||
return MathHelper.clamp_float(p_148268_1_, this.valueMin, this.valueMax);
|
||||
}
|
||||
|
||||
protected float snapToStep(float p_148264_1_)
|
||||
{
|
||||
if (this.valueStep > 0.0F)
|
||||
{
|
||||
p_148264_1_ = this.valueStep * (float)Math.round(p_148264_1_ / this.valueStep);
|
||||
}
|
||||
|
||||
return p_148264_1_;
|
||||
}
|
||||
|
||||
public boolean mousePressed(Minecraft mc, int mouseX, int mouseY)
|
||||
{
|
||||
if (super.mousePressed(mc, mouseX, mouseY))
|
||||
{
|
||||
this.dragging = true;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void mouseReleased(int mouseX, int mouseY)
|
||||
{
|
||||
this.dragging = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package eu.crushedpixel.replaymod.holders;
|
||||
|
||||
public class Keyframe {
|
||||
|
||||
private int realTimestamp;
|
||||
|
||||
public Keyframe(int realTimestamp) {
|
||||
this.realTimestamp = realTimestamp;
|
||||
}
|
||||
|
||||
public int getRealTimestamp() {
|
||||
return realTimestamp;
|
||||
}
|
||||
|
||||
public void setRealTimestamp(int realTimestamp) {
|
||||
this.realTimestamp = realTimestamp;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package eu.crushedpixel.replaymod.holders;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
import eu.crushedpixel.replaymod.replay.ReplayHandler;
|
||||
|
||||
public class KeyframeComparator implements Comparator<Keyframe> {
|
||||
|
||||
@Override
|
||||
public int compare(Keyframe o1, Keyframe o2) {
|
||||
if(ReplayHandler.isSelected(o1)) return 1;
|
||||
if(ReplayHandler.isSelected(o2)) return -1;
|
||||
return o1.getRealTimestamp()-o2.getRealTimestamp();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package eu.crushedpixel.replaymod.holders;
|
||||
|
||||
public class Position {
|
||||
|
||||
private double x, y, z;
|
||||
private float pitch, yaw;
|
||||
|
||||
public Position(double x, double y, double z, float pitch, float yaw) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.pitch = pitch;
|
||||
this.yaw = yaw;
|
||||
}
|
||||
|
||||
public double getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public void setX(double x) {
|
||||
this.x = x;
|
||||
}
|
||||
|
||||
public double getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public void setY(double y) {
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public double getZ() {
|
||||
return z;
|
||||
}
|
||||
|
||||
public void setZ(double z) {
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
public float getPitch() {
|
||||
return pitch;
|
||||
}
|
||||
|
||||
public void setPitch(float pitch) {
|
||||
this.pitch = pitch;
|
||||
}
|
||||
|
||||
public float getYaw() {
|
||||
return yaw;
|
||||
}
|
||||
|
||||
public void setYaw(float yaw) {
|
||||
this.yaw = yaw;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package eu.crushedpixel.replaymod.holders;
|
||||
|
||||
public class PositionKeyframe extends Keyframe {
|
||||
|
||||
private Position position;
|
||||
|
||||
public PositionKeyframe(int realTime, Position position) {
|
||||
super(realTime);
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
public Position getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public void setPosition(Position position) {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
}
|
||||
100
src/main/java/eu/crushedpixel/replaymod/holders/TimeInfo.java
Normal file
100
src/main/java/eu/crushedpixel/replaymod/holders/TimeInfo.java
Normal file
@@ -0,0 +1,100 @@
|
||||
package eu.crushedpixel.replaymod.holders;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2014 Johni0702
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
**/
|
||||
public final class TimeInfo {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return start+" | "+speedSince+" | "+speed+" | "+jumpTo;
|
||||
}
|
||||
|
||||
public static TimeInfo create() {
|
||||
long now = System.currentTimeMillis();
|
||||
return new TimeInfo(now, now, 1, -1);
|
||||
}
|
||||
|
||||
public TimeInfo(long start, long speedSince, double speed, long jumpTo) {
|
||||
this.start = start;
|
||||
this.speedSince = speedSince;
|
||||
this.speed = speed;
|
||||
this.jumpTo = jumpTo;
|
||||
}
|
||||
|
||||
private final long start;
|
||||
private final long speedSince;
|
||||
private final double speed;
|
||||
private final long jumpTo;
|
||||
|
||||
public long getActualStartTime(long now) {
|
||||
long realTimePassed = now - speedSince;
|
||||
long ingameTimePassed = (long) (realTimePassed * this.speed);
|
||||
return start + realTimePassed - ingameTimePassed;
|
||||
}
|
||||
|
||||
public long getInGameTimePassed(long now) {
|
||||
long realTimePassed = now - speedSince;
|
||||
long ingameTimePassed = (long) (realTimePassed * this.speed);
|
||||
return speedSince - start + ingameTimePassed;
|
||||
}
|
||||
|
||||
public TimeInfo updateSpeed(long now, double speed) {
|
||||
if (isJumping()) {
|
||||
return new TimeInfo(now-jumpTo, now, speed, -1);
|
||||
} else {
|
||||
if (this.speed == speed) {
|
||||
return this;
|
||||
}
|
||||
long start;
|
||||
if (this.speed == 1) {
|
||||
start = this.start;
|
||||
} else {
|
||||
start = getActualStartTime(now);
|
||||
}
|
||||
return new TimeInfo(start, now, speed, -1);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isJumping() {
|
||||
return jumpTo != -1;
|
||||
}
|
||||
|
||||
public TimeInfo jumpTo(long jumpTo) {
|
||||
return new TimeInfo(start, speedSince, speed, jumpTo);
|
||||
}
|
||||
|
||||
public long getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
public long getSpeedSince() {
|
||||
return speedSince;
|
||||
}
|
||||
|
||||
public double getSpeed() {
|
||||
return speed;
|
||||
}
|
||||
|
||||
public long getJumpTo() {
|
||||
return jumpTo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package eu.crushedpixel.replaymod.holders;
|
||||
|
||||
public class TimeKeyframe extends Keyframe {
|
||||
|
||||
private int timestamp;
|
||||
|
||||
public TimeKeyframe(int realTime, int timestamp) {
|
||||
super(realTime);
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public int getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setTimestamp(int timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package eu.crushedpixel.replaymod.interpolation;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class BasicSpline {
|
||||
public void calcNaturalCubic(List valueCollection, Field val, Collection<Cubic> cubicCollection) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
|
||||
int num = valueCollection.size()-1;
|
||||
|
||||
double[] gamma = new double[num+1];
|
||||
double[] delta = new double[num+1];
|
||||
double[] D = new double[num+1];
|
||||
|
||||
int i;
|
||||
/*
|
||||
We solve the equation
|
||||
[2 1 ] [D[0]] [3(x[1] - x[0]) ]
|
||||
|1 4 1 | |D[1]| |3(x[2] - x[0]) |
|
||||
| 1 4 1 | | . | = | . |
|
||||
| ..... | | . | | . |
|
||||
| 1 4 1| | . | |3(x[n] - x[n-2])|
|
||||
[ 1 2] [D[n]] [3(x[n] - x[n-1])]
|
||||
|
||||
by using row operations to convert the matrix to upper triangular
|
||||
and then back sustitution. The D[i] are the derivatives at the knots.
|
||||
*/
|
||||
gamma[0] = 1.0f / 2.0f;
|
||||
for(i=1; i< num; i++) {
|
||||
gamma[i] = 1.0f/(4.0f - gamma[i-1]);
|
||||
}
|
||||
gamma[num] = 1.0f/(2.0f - gamma[num-1]);
|
||||
|
||||
Double p0 = val.getDouble(valueCollection.get(0));
|
||||
Double p1 = val.getDouble(valueCollection.get(1));
|
||||
|
||||
delta[0] = 3.0f * (p1 - p0) * gamma[0];
|
||||
for(i=1; i< num; i++) {
|
||||
p0 = val.getDouble(valueCollection.get(i-1));
|
||||
p1 = val.getDouble(valueCollection.get(i+1));
|
||||
delta[i] = (3.0f * (p1 - p0) - delta[i - 1]) * gamma[i];
|
||||
}
|
||||
p0 = val.getDouble(valueCollection.get(num-1));
|
||||
p1 = val.getDouble(valueCollection.get(num));
|
||||
|
||||
delta[num] = (3.0f * (p1 - p0) - delta[num - 1]) * gamma[num];
|
||||
|
||||
D[num] = delta[num];
|
||||
for(i=num-1; i >= 0; i--) {
|
||||
D[i] = delta[i] - gamma[i] * D[i+1];
|
||||
}
|
||||
|
||||
//now compute the coefficients of the cubics
|
||||
cubicCollection.clear();
|
||||
|
||||
for(i=0; i<num; i++) {
|
||||
p0 = val.getDouble(valueCollection.get(i));
|
||||
p1 = val.getDouble(valueCollection.get(i+1));
|
||||
|
||||
cubicCollection.add(new Cubic(
|
||||
p0,
|
||||
D[i],
|
||||
3*(p1 - p0) - 2*D[i] - D[i+1],
|
||||
2*(p0 - p1) + D[i] + D[i+1]
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package eu.crushedpixel.replaymod.interpolation;
|
||||
|
||||
public class Cubic {
|
||||
private double a,b,c,d;
|
||||
|
||||
public Cubic(double p0, double d2, double e, double f) {
|
||||
this.a =p0;
|
||||
this.b =d2;
|
||||
this.c =e;
|
||||
this.d =f;
|
||||
}
|
||||
|
||||
public double eval(double u) {
|
||||
return (((d*u) + c)*u + b)*u + a;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package eu.crushedpixel.replaymod.interpolation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import akka.japi.Pair;
|
||||
|
||||
public abstract class LinearInterpolation<K> {
|
||||
|
||||
protected List<K> points = new ArrayList<K>();
|
||||
|
||||
public abstract K getPoint(float position);
|
||||
|
||||
public void addPoint(K point) {
|
||||
points.add(point);
|
||||
}
|
||||
|
||||
public void clearPoints() {
|
||||
points = new ArrayList<K>();
|
||||
}
|
||||
|
||||
protected Pair<Float, Pair<K, K>> getCurrentPoints(float position) {
|
||||
if(points.size() == 0) return null;
|
||||
float pos = position * (points.size()-1);
|
||||
int cubicNum = Math.round(pos);
|
||||
float cubicPos = (pos - cubicNum);
|
||||
|
||||
if(cubicNum == points.size()-1) {
|
||||
cubicNum--;
|
||||
cubicPos++;
|
||||
}
|
||||
|
||||
if(cubicNum < 0) {
|
||||
return new Pair<Float, Pair<K,K>>(cubicPos, new Pair<K,K>(points.get(cubicNum+1), points.get(cubicNum+1)));
|
||||
}
|
||||
return new Pair<Float, Pair<K,K>>(cubicPos, new Pair<K,K>(points.get(cubicNum), points.get(cubicNum+1)));
|
||||
}
|
||||
|
||||
protected double getInterpolatedValue(double val1, double val2, float perc) {
|
||||
return val1+((val2-val1)*perc);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package eu.crushedpixel.replaymod.interpolation;
|
||||
|
||||
import akka.japi.Pair;
|
||||
import eu.crushedpixel.replaymod.holders.Position;
|
||||
|
||||
public class LinearPoint extends LinearInterpolation<Position> {
|
||||
|
||||
@Override
|
||||
public Position getPoint(float position) {
|
||||
Pair<Float, Pair<Position, Position>> pair = getCurrentPoints(position);
|
||||
if(pair == null) return null;
|
||||
|
||||
float perc = pair.first();
|
||||
|
||||
Position first = pair.second().first();
|
||||
Position second = pair.second().second();
|
||||
|
||||
double x = getInterpolatedValue(first.getX(), second.getX(), perc);
|
||||
double y = getInterpolatedValue(first.getY(), second.getY(), perc);
|
||||
double z = getInterpolatedValue(first.getZ(), second.getZ(), perc);
|
||||
|
||||
float pitch = (float)getInterpolatedValue(first.getPitch(), second.getPitch(), perc);
|
||||
float yaw = (float)getInterpolatedValue(first.getYaw(), second.getYaw(), perc);
|
||||
|
||||
Position inter = new Position(x, y, z, pitch, yaw);
|
||||
return inter;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package eu.crushedpixel.replaymod.interpolation;
|
||||
|
||||
import akka.japi.Pair;
|
||||
import eu.crushedpixel.replaymod.holders.Position;
|
||||
|
||||
public class LinearTimestamp extends LinearInterpolation<Integer> {
|
||||
|
||||
@Override
|
||||
public Integer getPoint(float position) {
|
||||
Pair<Float, Pair<Integer, Integer>> pair = getCurrentPoints(position);
|
||||
if(pair == null) return null;
|
||||
|
||||
float perc = pair.first();
|
||||
|
||||
int first = pair.second().first();
|
||||
int second = pair.second().second();
|
||||
|
||||
int val = (int)getInterpolatedValue(first, second, perc);
|
||||
|
||||
return val;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package eu.crushedpixel.replaymod.interpolation;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Vector;
|
||||
|
||||
import com.sun.javafx.geom.Vec3d;
|
||||
|
||||
import eu.crushedpixel.replaymod.holders.Position;
|
||||
|
||||
public class SplinePoint extends BasicSpline{
|
||||
private Vector<Position> points;
|
||||
|
||||
private Vector<Cubic> xCubics;
|
||||
private Vector<Cubic> yCubics;
|
||||
private Vector<Cubic> zCubics;
|
||||
private Vector<Cubic> pitchCubics;
|
||||
private Vector<Cubic> yawCubics;
|
||||
|
||||
private Field vectorX;
|
||||
private Field vectorY;
|
||||
private Field vectorZ;
|
||||
private Field vectorPitch;
|
||||
private Field vectorYaw;
|
||||
|
||||
private static final Object[] EMPTYOBJ = new Object[] { };
|
||||
|
||||
public SplinePoint() {
|
||||
this.points = new Vector<Position>();
|
||||
|
||||
this.xCubics = new Vector<Cubic>();
|
||||
this.yCubics = new Vector<Cubic>();
|
||||
this.zCubics = new Vector<Cubic>();
|
||||
this.pitchCubics = new Vector<Cubic>();
|
||||
this.yawCubics = new Vector<Cubic>();
|
||||
|
||||
try {
|
||||
vectorX = Position.class.getDeclaredField("x");
|
||||
vectorY = Position.class.getDeclaredField("y");
|
||||
vectorZ = Position.class.getDeclaredField("z");
|
||||
vectorPitch = Position.class.getDeclaredField("pitch");
|
||||
vectorYaw = Position.class.getDeclaredField("yaw");
|
||||
vectorX.setAccessible(true);
|
||||
vectorY.setAccessible(true);
|
||||
vectorZ.setAccessible(true);
|
||||
vectorPitch.setAccessible(true);
|
||||
vectorYaw.setAccessible(true);
|
||||
} catch (SecurityException e) {
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchFieldException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void addPoint(Position point) {
|
||||
this.points.add(point);
|
||||
}
|
||||
|
||||
public Vector<Position> getPoints() {
|
||||
return points;
|
||||
}
|
||||
|
||||
public void calcSpline() {
|
||||
try {
|
||||
calcNaturalCubic(points, vectorX, xCubics);
|
||||
calcNaturalCubic(points, vectorY, yCubics);
|
||||
calcNaturalCubic(points, vectorZ, zCubics);
|
||||
calcNaturalCubic(points, vectorPitch, pitchCubics);
|
||||
calcNaturalCubic(points, vectorYaw, yawCubics);
|
||||
} catch (IllegalArgumentException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public Position getPoint(float position) {
|
||||
position = position * xCubics.size();
|
||||
int cubicNum = (int)Math.min(xCubics.size()-1, position);
|
||||
float cubicPos = (position - cubicNum);
|
||||
|
||||
return new Position(xCubics.get(cubicNum).eval(cubicPos),
|
||||
yCubics.get(cubicNum).eval(cubicPos),
|
||||
zCubics.get(cubicNum).eval(cubicPos),
|
||||
(float)pitchCubics.get(cubicNum).eval(cubicPos),
|
||||
(float)yawCubics.get(cubicNum).eval(cubicPos));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
package eu.crushedpixel.replaymod.recording;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import net.minecraft.network.NetworkManager;
|
||||
import net.minecraft.network.Packet;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.network.FMLNetworkEvent.ClientConnectedToServerEvent;
|
||||
import net.minecraftforge.fml.common.network.FMLNetworkEvent.ClientDisconnectionFromServerEvent;
|
||||
import eu.crushedpixel.replaymod.ReplayMod;
|
||||
import eu.crushedpixel.replaymod.chat.ChatMessageRequests;
|
||||
import eu.crushedpixel.replaymod.chat.ChatMessageRequests.ChatMessageType;
|
||||
|
||||
public class ConnectionEventHandler {
|
||||
|
||||
private static final String decoderKey = "decoder";
|
||||
private static final String packetHandlerKey = "packet_handler";
|
||||
private static final String DATE_FORMAT = "yyyy_MM_dd_HH_mm_ss";
|
||||
private static final SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
|
||||
public static final String TEMP_FILE_EXTENSION = ".tmcpr";
|
||||
public static final String JSON_FILE_EXTENSION = ".json";
|
||||
public static final String ZIP_FILE_EXTENSION = ".mcpr";
|
||||
|
||||
private File currentFile;
|
||||
private String fileName;
|
||||
|
||||
private static PacketListener packetListener = null;
|
||||
|
||||
private static boolean isRecording = false;
|
||||
|
||||
public static boolean isRecording() {
|
||||
return isRecording;
|
||||
}
|
||||
|
||||
public static void insertPacket(Packet packet) {
|
||||
if(!isRecording || packetListener == null) {
|
||||
System.out.println("Invalid attempt to insert Packet!");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
packetListener.channelRead(null, packet);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onConnectedToServerEvent(ClientConnectedToServerEvent event) {
|
||||
System.out.println("Connected to server");
|
||||
|
||||
ChatMessageRequests.initialize();
|
||||
|
||||
try {
|
||||
if(event.isLocal) {
|
||||
if(!ReplayMod.replaySettings.isEnableRecordingSingleplayer()) {
|
||||
System.out.println("Singleplayer Recording is disabled");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if(!ReplayMod.replaySettings.isEnableRecordingServer()) {
|
||||
System.out.println("Multiplayer Recording is disabled");
|
||||
return;
|
||||
}
|
||||
}
|
||||
NetworkManager nm = event.manager;
|
||||
String worldName = "";
|
||||
if(!event.isLocal) {
|
||||
worldName = ((InetSocketAddress)nm.getRemoteAddress()).getHostName();
|
||||
}
|
||||
Channel channel = nm.channel();
|
||||
ChannelPipeline pipeline = channel.pipeline();
|
||||
|
||||
List<String> channelHandlerKeys = new ArrayList<String>();
|
||||
Iterator<Entry<String, ChannelHandler>> it = pipeline.iterator();
|
||||
while(it.hasNext()) {
|
||||
Entry<String, ChannelHandler> entry = it.next();
|
||||
channelHandlerKeys.add(entry.getKey());
|
||||
}
|
||||
|
||||
File folder = new File("./replay_recordings/");
|
||||
folder.mkdirs();
|
||||
|
||||
fileName = sdf.format(Calendar.getInstance().getTime());
|
||||
currentFile = new File(folder, fileName+TEMP_FILE_EXTENSION);
|
||||
|
||||
currentFile.createNewFile();
|
||||
|
||||
int maxFileSize = ReplayMod.replaySettings.getMaximumFileSize();
|
||||
|
||||
PacketListener insert = null;
|
||||
|
||||
if(event.isLocal) {
|
||||
pipeline.addBefore(packetHandlerKey, "replay_recorder", insert = new PacketListener(currentFile, fileName, worldName, System.currentTimeMillis(), maxFileSize));
|
||||
ChatMessageRequests.addChatMessage("Recording started!", ChatMessageType.INFORMATION);
|
||||
isRecording = true;
|
||||
|
||||
} else {
|
||||
//pipeline.addBefore(decoderKey, "replay_recorder", insert = new DataListener(currentFile, fileName, worldName, System.currentTimeMillis(), maxFileSize));
|
||||
pipeline.addBefore(packetHandlerKey, "replay_recorder", insert = new PacketListener(currentFile, fileName, worldName, System.currentTimeMillis(), maxFileSize));
|
||||
ChatMessageRequests.addChatMessage("Recording started!", ChatMessageType.INFORMATION);
|
||||
isRecording = true;
|
||||
}
|
||||
|
||||
final PacketListener listener = insert;
|
||||
|
||||
if(insert != null && event.isLocal) {
|
||||
new Thread(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
String worldName = null;
|
||||
while(worldName == null) {
|
||||
try {
|
||||
worldName = MinecraftServer.getServer().getWorldName();
|
||||
listener.setWorldName(worldName);
|
||||
return;
|
||||
|
||||
} catch(Exception e) {
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
packetListener = listener;
|
||||
|
||||
} catch(Exception e) {
|
||||
ChatMessageRequests.addChatMessage("Failed to start recording!", ChatMessageType.WARNING);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onDisconnectedFromServerEvent(ClientDisconnectionFromServerEvent event) {
|
||||
System.out.println("Disconnected from server");
|
||||
isRecording = false;
|
||||
packetListener = null;
|
||||
ChatMessageRequests.stop();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
package eu.crushedpixel.replaymod.recording;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import net.minecraft.network.Packet;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import eu.crushedpixel.replaymod.chat.ChatMessageRequests;
|
||||
import eu.crushedpixel.replaymod.chat.ChatMessageRequests.ChatMessageType;
|
||||
|
||||
public class DataListener extends ChannelInboundHandlerAdapter {
|
||||
|
||||
protected File file;
|
||||
protected long startTime;
|
||||
protected long maxSize;
|
||||
protected long totalBytes = 0;
|
||||
protected DataOutputStream out;
|
||||
protected String name;
|
||||
protected String worldName;
|
||||
|
||||
protected long lastSentPacket = 0;
|
||||
|
||||
protected boolean alive = true;
|
||||
protected boolean isPacketListener = false;
|
||||
|
||||
private boolean writeJoinPacket = false;
|
||||
|
||||
private Gson gson = new Gson();
|
||||
|
||||
public void setWorldName(String worldName) {
|
||||
this.worldName = worldName;
|
||||
System.out.println(worldName);
|
||||
}
|
||||
|
||||
public void insertPacket(Packet p) {
|
||||
|
||||
}
|
||||
|
||||
public DataListener(File file, String name, String worldName, long startTime, int maxSize) throws FileNotFoundException {
|
||||
this.file = file;
|
||||
this.startTime = startTime;
|
||||
this.maxSize = maxSize*1024*1024;
|
||||
this.name = name;
|
||||
this.worldName = worldName;
|
||||
|
||||
System.out.println(worldName);
|
||||
|
||||
FileOutputStream fos = new FileOutputStream(file);
|
||||
BufferedOutputStream bos = new BufferedOutputStream(fos);
|
||||
out = new DataOutputStream(bos);
|
||||
|
||||
writeJoinPacket = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
if(!alive || isPacketListener) {
|
||||
lastSentPacket = System.currentTimeMillis();
|
||||
super.channelRead(ctx, msg);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
if(writeJoinPacket) {
|
||||
writeJoinPacket = false;
|
||||
S01PacketJoinGame p = new S01PacketJoinGame();
|
||||
|
||||
int timestamp = 0;
|
||||
|
||||
PacketSerializer ps = new PacketSerializer(EnumPacketDirection.CLIENTBOUND);
|
||||
ByteBuf bb = Unpooled.buffer();
|
||||
|
||||
PacketBuffer pb = new PacketBuffer(bb);
|
||||
pb.writeInt(new Random().nextInt(1000));
|
||||
pb.writeByte(3);
|
||||
pb.writeByte(-1);
|
||||
pb.writeByte(0);
|
||||
pb.writeByte(100);
|
||||
pb.writeString("default");
|
||||
pb.writeBoolean(false);
|
||||
|
||||
p.readPacketData(pb);
|
||||
|
||||
ByteBuf b2 = Unpooled.buffer();
|
||||
ps.encode(ctx, p, b2);
|
||||
|
||||
b2.readerIndex(0);
|
||||
|
||||
byte[] array = new byte[b2.readableBytes()];
|
||||
b2.readBytes(array);
|
||||
|
||||
b2.readerIndex(0);
|
||||
|
||||
out.writeInt(timestamp);
|
||||
out.writeInt(array.length);
|
||||
out.write(array);
|
||||
out.flush();
|
||||
}
|
||||
*/
|
||||
|
||||
int timestamp = (int)(System.currentTimeMillis() - startTime);
|
||||
|
||||
ByteBuf byteBuf = (ByteBuf)msg;
|
||||
|
||||
byteBuf.readerIndex(0);
|
||||
byte[] array = new byte[byteBuf.readableBytes()];
|
||||
byteBuf.readBytes(array);
|
||||
|
||||
totalBytes += (array.length + (2*4)); //two Integer values and the Packet size
|
||||
if(totalBytes >= maxSize && maxSize > 0) {
|
||||
ChatMessageRequests.addChatMessage("Maximum file size exceeded", ChatMessageType.WARNING);
|
||||
ChatMessageRequests.addChatMessage("The Recording has been stopped", ChatMessageType.WARNING);
|
||||
alive = false;
|
||||
}
|
||||
|
||||
byteBuf.readerIndex(0);
|
||||
|
||||
out.writeInt(timestamp);
|
||||
out.writeInt(array.length);
|
||||
out.write(array);
|
||||
out.flush();
|
||||
|
||||
super.channelRead(ctx, msg);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
out.flush();
|
||||
out.close();
|
||||
|
||||
byte[] buffer = new byte[1024];
|
||||
|
||||
try {
|
||||
ReplayMetaData metaData = new ReplayMetaData(isPacketListener, worldName, (int) (lastSentPacket - startTime), startTime);
|
||||
String json = gson.toJson(metaData);
|
||||
|
||||
File folder = new File("./replay_recordings/");
|
||||
folder.mkdirs();
|
||||
|
||||
File archive = new File(folder, name+ConnectionEventHandler.ZIP_FILE_EXTENSION);
|
||||
archive.createNewFile();
|
||||
|
||||
FileOutputStream fos = new FileOutputStream(archive);
|
||||
ZipOutputStream zos = new ZipOutputStream(fos);
|
||||
|
||||
zos.putNextEntry(new ZipEntry("metaData.json"));
|
||||
PrintWriter pw = new PrintWriter(zos);
|
||||
pw.write(json);
|
||||
pw.flush();
|
||||
zos.closeEntry();
|
||||
|
||||
zos.putNextEntry(new ZipEntry("recording"+ConnectionEventHandler.TEMP_FILE_EXTENSION));
|
||||
FileInputStream fis = new FileInputStream(file);
|
||||
int len;
|
||||
while((len = fis.read(buffer)) > 0) {
|
||||
zos.write(buffer, 0, len);
|
||||
}
|
||||
|
||||
fis.close();
|
||||
zos.closeEntry();
|
||||
|
||||
zos.close();
|
||||
|
||||
file.delete();
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package eu.crushedpixel.replaymod.recording;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import net.minecraft.entity.DataWatcher;
|
||||
import net.minecraft.network.EnumPacketDirection;
|
||||
import net.minecraft.network.Packet;
|
||||
import net.minecraft.network.play.server.S0CPacketSpawnPlayer;
|
||||
import net.minecraft.network.play.server.S0FPacketSpawnMob;
|
||||
import eu.crushedpixel.replaymod.chat.ChatMessageRequests;
|
||||
import eu.crushedpixel.replaymod.chat.ChatMessageRequests.ChatMessageType;
|
||||
import eu.crushedpixel.replaymod.reflection.MCPNames;
|
||||
|
||||
public class PacketListener extends DataListener {
|
||||
|
||||
public PacketListener(File file, String name, String worldName, long startTime, int maxSize) throws FileNotFoundException {
|
||||
super(file, name, worldName, startTime, maxSize);
|
||||
isPacketListener = true;
|
||||
}
|
||||
|
||||
private ChannelHandlerContext context = null;
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
if(ctx == null) {
|
||||
if(context == null) {
|
||||
return;
|
||||
} else {
|
||||
ctx = context;
|
||||
}
|
||||
}
|
||||
this.context = ctx;
|
||||
if(!alive) {
|
||||
super.channelRead(ctx, msg);
|
||||
return;
|
||||
}
|
||||
if(msg instanceof Packet) {
|
||||
try {
|
||||
Packet packet = (Packet)msg;
|
||||
|
||||
int timestamp = (int)(System.currentTimeMillis() - startTime);
|
||||
|
||||
//Converts the packet back to a ByteBuffer for correct saving
|
||||
|
||||
PacketSerializer ps = new PacketSerializer(EnumPacketDirection.CLIENTBOUND);
|
||||
|
||||
ByteBuf bb = Unpooled.buffer();
|
||||
if(packet instanceof S0FPacketSpawnMob) {
|
||||
Field field_149043_l = S0FPacketSpawnMob.class.getDeclaredField(MCPNames.field("field_149043_l"));
|
||||
field_149043_l.setAccessible(true);
|
||||
DataWatcher l = (DataWatcher)field_149043_l.get(packet);
|
||||
DataWatcher dw = new DataWatcher(null);
|
||||
if(l == null) {
|
||||
field_149043_l.set(packet, dw);
|
||||
}
|
||||
}
|
||||
|
||||
if(packet instanceof S0CPacketSpawnPlayer) {
|
||||
Field field_149043_l = S0CPacketSpawnPlayer.class.getDeclaredField(MCPNames.field("field_148960_i"));
|
||||
field_149043_l.setAccessible(true);
|
||||
DataWatcher l = (DataWatcher)field_149043_l.get(packet);
|
||||
DataWatcher dw = new DataWatcher(null);
|
||||
if(l == null) {
|
||||
field_149043_l.set(packet, dw);
|
||||
}
|
||||
}
|
||||
|
||||
ps.encode(ctx, packet, bb);
|
||||
|
||||
bb.readerIndex(0);
|
||||
byte[] array = new byte[bb.readableBytes()];
|
||||
bb.readBytes(array);
|
||||
|
||||
|
||||
totalBytes += (array.length + (2*4)); //two Integer values and the Packet size
|
||||
if(totalBytes >= maxSize && maxSize > 0) {
|
||||
ChatMessageRequests.addChatMessage("Maximum file size exceeded", ChatMessageType.WARNING);
|
||||
ChatMessageRequests.addChatMessage("The Recording has been stopped", ChatMessageType.WARNING);
|
||||
alive = false;
|
||||
}
|
||||
|
||||
bb.readerIndex(0);
|
||||
|
||||
out.writeInt(timestamp); //Timestamp
|
||||
out.writeInt(array.length); //Lenght
|
||||
out.write(array);
|
||||
out.flush();
|
||||
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
super.channelRead(ctx, msg);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package eu.crushedpixel.replaymod.recording;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
|
||||
import net.minecraft.network.EnumConnectionState;
|
||||
import net.minecraft.network.EnumPacketDirection;
|
||||
import net.minecraft.network.NetworkManager;
|
||||
import net.minecraft.network.Packet;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
import net.minecraft.network.play.server.S0CPacketSpawnPlayer;
|
||||
import net.minecraft.util.MessageSerializer;
|
||||
|
||||
public class PacketSerializer extends MessageSerializer {
|
||||
|
||||
public PacketSerializer(EnumPacketDirection direction) {
|
||||
super(direction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(ChannelHandlerContext p_encode_1_, Packet p_encode_2_, ByteBuf p_encode_3_) throws IOException {
|
||||
//super.encode(p_encode_1_, p_encode_2_, p_encode_3_);
|
||||
|
||||
Integer integer = ((EnumConnectionState)p_encode_1_.channel().attr(NetworkManager.attrKeyConnectionState).get()).getPacketId(EnumPacketDirection.CLIENTBOUND, p_encode_2_);
|
||||
|
||||
if (integer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
PacketBuffer packetbuffer = new PacketBuffer(p_encode_3_);
|
||||
packetbuffer.writeVarIntToBuffer(integer.intValue());
|
||||
|
||||
try
|
||||
{
|
||||
if (p_encode_2_ instanceof S0CPacketSpawnPlayer)
|
||||
{
|
||||
//p_encode_2_ = p_encode_2_; FML: Kill warning
|
||||
}
|
||||
|
||||
p_encode_2_.writePacketData(packetbuffer);
|
||||
}
|
||||
catch (Throwable throwable)
|
||||
{
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static ByteBuf toByteBuf(byte[] bytes) throws IOException, ClassNotFoundException {
|
||||
ByteBuf bb;
|
||||
bb = Unpooled.buffer(bytes.length);
|
||||
bb.writeBytes(bytes);
|
||||
|
||||
return bb;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package eu.crushedpixel.replaymod.recording;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
public class ReplayMetaData {
|
||||
|
||||
private boolean singleplayer;
|
||||
private String serverName;
|
||||
private int duration;
|
||||
private long date;
|
||||
|
||||
|
||||
public ReplayMetaData(boolean singleplayer, String serverName,
|
||||
int duration, long date) {
|
||||
this.singleplayer = singleplayer;
|
||||
this.serverName = serverName;
|
||||
this.duration = duration;
|
||||
this.date = date;
|
||||
}
|
||||
public boolean isSingleplayer() {
|
||||
return singleplayer;
|
||||
}
|
||||
public void setSingleplayer(boolean singleplayer) {
|
||||
this.singleplayer = singleplayer;
|
||||
}
|
||||
public String getServerName() {
|
||||
return serverName;
|
||||
}
|
||||
public void setServer_name(String serverName) {
|
||||
this.serverName = serverName;
|
||||
}
|
||||
public int getDuration() {
|
||||
return duration;
|
||||
}
|
||||
public void setDuration(int duration) {
|
||||
this.duration = duration;
|
||||
}
|
||||
public long getDate() {
|
||||
return date;
|
||||
}
|
||||
public void setDate(long date) {
|
||||
this.date = date;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package eu.crushedpixel.replaymod.reflection;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import net.minecraft.client.gui.GuiMainMenu;
|
||||
|
||||
public class MCPEnvironment {
|
||||
|
||||
boolean eclipse = true;
|
||||
|
||||
public MCPEnvironment() {
|
||||
eclipse = true;
|
||||
Class<? extends GuiMainMenu> clazz = GuiMainMenu.class;
|
||||
try {
|
||||
Field viewportTexture = clazz.getDeclaredField("viewportTexture");
|
||||
} catch(Exception e) {
|
||||
eclipse = false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isMCPEnvironment() {
|
||||
return eclipse;
|
||||
}
|
||||
}
|
||||
269
src/main/java/eu/crushedpixel/replaymod/reflection/MCPNames.java
Normal file
269
src/main/java/eu/crushedpixel/replaymod/reflection/MCPNames.java
Normal file
@@ -0,0 +1,269 @@
|
||||
package eu.crushedpixel.replaymod.reflection;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.io.Files;
|
||||
import com.google.common.io.LineProcessor;
|
||||
|
||||
/**
|
||||
* <p>A helper class for working with obfuscated field names.</p>
|
||||
* <p>In the development environment the mappings file will automatically loaded. You can provide the location of a custom mappings file by
|
||||
* providing the system property {@code sevencommons.mappingsFile}.</p>
|
||||
* @author diesieben07
|
||||
* @author CrushedPixel
|
||||
*/
|
||||
public final class MCPNames {
|
||||
|
||||
private static final Map<String, String> fields;
|
||||
private static final Map<String, String> methods;
|
||||
private static final MCPEnvironment env = new MCPEnvironment();
|
||||
|
||||
static {
|
||||
if (use()) {
|
||||
String mappingsDir = "./../build/unpacked/mappings/";
|
||||
ResourceLocation fieldsLocation = new ResourceLocation("assets/replaymod/fields.csv");
|
||||
ResourceLocation methodsLocation = new ResourceLocation("assets/replaymod/methods.csv");
|
||||
|
||||
InputStream fieldsIs = MCPNames.class.getClassLoader().getResourceAsStream("fields.csv");
|
||||
InputStream methodsIs = MCPNames.class.getClassLoader().getResourceAsStream("methods.csv");
|
||||
|
||||
fields = readMappings(fieldsIs);
|
||||
methods = readMappings(methodsIs);
|
||||
|
||||
} else {
|
||||
methods = fields = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Whether the code is running in a development environment or not.</p>
|
||||
* @return true if the code is running in development mode (use MCP instead of SRG names)
|
||||
*/
|
||||
public static boolean use() {
|
||||
return env.isMCPEnvironment();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Get the correct name for the given SRG field based on the context.</p>
|
||||
* @param srg the SRG name for a field
|
||||
* @return the input if the code is running outside of development mode or the matching MCP name otherwise
|
||||
*/
|
||||
public static String field(String srg) {
|
||||
if (use()) {
|
||||
String mcp = fields.get(srg);
|
||||
if (mcp == null) {
|
||||
// no mapping
|
||||
return srg;
|
||||
}
|
||||
return mcp;
|
||||
} else {
|
||||
return srg;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Get the correct name for the given SRG method based on the context.</p>
|
||||
* @param srg the SRG name for a method
|
||||
* @return the input if the code is running outside of development mode or the matching MCP name otherwise
|
||||
*/
|
||||
public static String method(String srg) {
|
||||
if (use()) {
|
||||
String mcp = methods.get(srg);
|
||||
if (mcp == null) {
|
||||
// no mapping
|
||||
return srg;
|
||||
}
|
||||
return mcp;
|
||||
} else {
|
||||
return srg;
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<String, String> readMappings(InputStream is) {
|
||||
try {
|
||||
InputStreamReader isr = new InputStreamReader(is);
|
||||
BufferedReader br = new BufferedReader(isr);
|
||||
|
||||
MCPFileParser fileParser = new MCPFileParser();
|
||||
|
||||
while(br.ready()) {
|
||||
fileParser.processLine(br.readLine());
|
||||
}
|
||||
|
||||
return fileParser.getResult();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Couldn't read SRG->MCP mappings", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static class MCPFileParser implements LineProcessor<Map<String, String>> {
|
||||
|
||||
private static final Splitter splitter = Splitter.on(',').trimResults();
|
||||
private final Map<String, String> map = Maps.newHashMap();
|
||||
private boolean foundFirst;
|
||||
|
||||
@Override
|
||||
public boolean processLine(String line) throws IOException {
|
||||
if (!foundFirst) {
|
||||
foundFirst = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
Iterator<String> splitted = splitter.split(line).iterator();
|
||||
try {
|
||||
String srg = splitted.next();
|
||||
String mcp = splitted.next();
|
||||
if (!map.containsKey(srg)) {
|
||||
map.put(srg, mcp);
|
||||
}
|
||||
} catch (NoSuchElementException e) {
|
||||
throw new IOException("Invalid Mappings file!", e);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getResult() {
|
||||
return ImmutableMap.copyOf(map);
|
||||
}
|
||||
}
|
||||
|
||||
public static final String M_SPAWN_BABY = "func_75388_i";
|
||||
|
||||
public static final String F_TARGET_MATE = "field_75391_e";
|
||||
|
||||
public static final String F_THE_ANIMAL = "field_75390_d";
|
||||
|
||||
public static final String M_CLONE_PLAYER = "func_71049_a";
|
||||
|
||||
public static final String M_CONVERT_TO_VILLAGER = "func_82232_p";
|
||||
|
||||
public static final String M_SET_WORLD_AND_RESOLUTION = "func_73872_a";
|
||||
|
||||
public static final String F_BUTTON_LIST = "field_73887_h";
|
||||
|
||||
public static final String F_TAG_LIST = "field_74747_a";
|
||||
|
||||
public static final String F_TAG_MAP = "field_74784_a";
|
||||
|
||||
public static final String F_FOV_MODIFIER_HAND_PREV = "field_78506_S";
|
||||
|
||||
public static final String F_FOV_MODIFIER_HAND = "field_78507_R";
|
||||
|
||||
public static final String F_TRACKED_ENTITY_IDS = "field_72794_c";
|
||||
|
||||
public static final String F_MAP_TEXTURE_OBJECTS = "field_110585_a";
|
||||
|
||||
public static final String F_MY_ENTITY = "field_73132_a";
|
||||
|
||||
public static final String M_TRY_START_WATCHING_THIS = "func_73117_b";
|
||||
|
||||
public static final String M_ON_UPDATE = "func_70071_h_";
|
||||
|
||||
public static final String M_UPDATE_ENTITY = "func_70316_g";
|
||||
|
||||
public static final String M_DETECT_AND_SEND_CHANGES = "func_75142_b";
|
||||
|
||||
public static final String F_IS_REMOTE = "field_72995_K";
|
||||
|
||||
public static final String F_WORLD_OBJ_TILEENTITY = "field_70331_k";
|
||||
|
||||
public static final String F_WORLD_OBJ_ENTITY = "field_70170_p";
|
||||
|
||||
public static final String F_TIMER = "field_71428_T";
|
||||
|
||||
public static final String F_PACKET_CLASS_TO_ID_MAP = "field_73291_a";
|
||||
|
||||
public static final String M_SEND_PACKET_TO_PLAYER = "func_72567_b";
|
||||
|
||||
public static final String M_REMOVE_ENTITY = "func_72900_e";
|
||||
|
||||
public static final String M_WRITE_ENTITY_TO_NBT = "func_70014_b";
|
||||
|
||||
public static final String M_READ_ENTITY_FROM_NBT = "func_70037_a";
|
||||
|
||||
public static final String M_WRITE_TO_NBT_TILEENTITY = "func_70310_b";
|
||||
|
||||
public static final String M_READ_FROM_NBT_TILEENTITY = "func_70307_a";
|
||||
|
||||
public static final String F_ITEM_DAMAGE = "field_77991_e";
|
||||
|
||||
public static final String M_REGISTER_EXT_PROPS = "registerExtendedProperties";
|
||||
|
||||
public static final String M_READ_PACKET_DATA = "func_73267_a";
|
||||
|
||||
public static final String M_WRITE_PACKET_DATA = "func_73273_a";
|
||||
|
||||
public static final String M_GET_PACKET_SIZE = "func_73284_a";
|
||||
|
||||
public static final String F_UNLOCALIZED_NAME_BLOCK = "field_71968_b";
|
||||
|
||||
public static final String M_SET_HAS_SUBTYPES = "func_77627_a";
|
||||
|
||||
public static final String F_ICON_STRING = "field_111218_cA";
|
||||
|
||||
public static final String F_UNLOCALIZED_NAME_ITEM = "field_77774_bZ";
|
||||
|
||||
public static final String F_TEXTURE_NAME_BLOCK = "field_111026_f";
|
||||
|
||||
public static final String M_ACTION_PERFORMED = "func_73875_a";
|
||||
|
||||
public static final String F_Z_LEVEL = "field_73735_i";
|
||||
|
||||
public static final String M_ADD_SLOT_TO_CONTAINER = "func_75146_a";
|
||||
|
||||
public static final String M_MERGE_ITEM_STACK = "func_75135_a";
|
||||
|
||||
public static final String F_CRAFTERS = "field_75149_d";
|
||||
|
||||
public static final String M_GET_ICON_STRING = "func_111208_A";
|
||||
|
||||
public static final String M_GET_TEXTURE_NAME = "func_111023_E";
|
||||
|
||||
public static final String M_NBT_WRITE = "func_74734_a";
|
||||
|
||||
public static final String M_NBT_LOAD = "func_74735_a";
|
||||
|
||||
public static final String F_NBT_STRING_DATA = "field_74751_a";
|
||||
public static final String F_NBT_BYTE_DATA = "field_74756_a";
|
||||
public static final String F_NBT_SHORT_DATA = "field_74752_a";
|
||||
public static final String F_NBT_INT_DATA = "field_74748_a";
|
||||
public static final String F_NBT_LONG_DATA = "field_74753_a";
|
||||
public static final String F_NBT_FLOAT_DATA = "field_74750_a";
|
||||
public static final String F_NBT_DOUBLE_DATA = "field_74755_a";
|
||||
|
||||
public static final String M_SET_TAG = "func_74782_a";
|
||||
|
||||
public static final String M_NBT_GET_ID = "func_74732_a";
|
||||
|
||||
public static final String M_ITEMSTACK_WRITE_NBT = "func_77955_b";
|
||||
public static final String M_LOAD_ITEMSTACK_FROM_NBT = "func_77949_a";
|
||||
|
||||
public static final String M_ADD_CRAFTING_TO_CRAFTERS = "func_75132_a";
|
||||
|
||||
public static final String M_CHECK_HOTBAR_KEYS = "func_82319_a";
|
||||
|
||||
public static final String M_HANDLE_MOUSE_CLICK = "func_74191_a";
|
||||
|
||||
public static final String F_GUICONTAINER_THE_SLOT = "field_82320_o";
|
||||
|
||||
private MCPNames() { }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package eu.crushedpixel.replaymod.replay;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import scala.actors.threadpool.Arrays;
|
||||
import net.minecraft.entity.DataWatcher;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
import net.minecraft.util.Rotations;
|
||||
|
||||
public class LesserDataWatcher extends DataWatcher {
|
||||
|
||||
public LesserDataWatcher(Entity owner) {
|
||||
super(owner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addObject(int id, Object object) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addObjectByDataType(int id, int type) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getWatchableObjectByte(int id) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getWatchableObjectShort(int id) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWatchableObjectInt(int id) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getWatchableObjectFloat(int id) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWatchableObjectString(int id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getWatchableObjectItemStack(int id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rotations getWatchableObjectRotations(int id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateObject(int id, Object newData) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setObjectWatched(int id) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasObjectChanged() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List getChanged() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(PacketBuffer buffer) throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List getAllWatched() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateWatchedObjectsFromList(List p_75687_1_) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getIsBlank() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void func_111144_e() {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package eu.crushedpixel.replaymod.replay;
|
||||
|
||||
import net.minecraft.network.NetworkManager;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.embedded.EmbeddedChannel;
|
||||
|
||||
public class OpenEmbeddedChannel extends EmbeddedChannel {
|
||||
|
||||
private boolean ignoreClose = false;
|
||||
|
||||
public OpenEmbeddedChannel(NetworkManager networkManager) {
|
||||
super(networkManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean finish() {
|
||||
System.out.println("wanted to finish");
|
||||
ignoreClose = true;
|
||||
return super.finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFuture close() {
|
||||
if(ignoreClose) {
|
||||
ignoreClose = false;
|
||||
return null;
|
||||
}
|
||||
return pipeline().close();
|
||||
}
|
||||
|
||||
public ChannelFuture manualClose() {
|
||||
return pipeline().close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package eu.crushedpixel.replaymod.replay;
|
||||
|
||||
import gnu.trove.iterator.TIntObjectIterator;
|
||||
import gnu.trove.map.TIntObjectMap;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.minecraft.network.EnumConnectionState;
|
||||
import net.minecraft.network.EnumPacketDirection;
|
||||
import net.minecraft.network.NetworkManager;
|
||||
import net.minecraft.network.Packet;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
import net.minecraft.util.MessageDeserializer;
|
||||
|
||||
import com.google.common.collect.BiMap;
|
||||
|
||||
import eu.crushedpixel.replaymod.reflection.MCPNames;
|
||||
|
||||
public class PacketDeserializer extends MessageDeserializer {
|
||||
|
||||
private final EnumPacketDirection direction;
|
||||
private Field directionMaps;
|
||||
private EnumConnectionState state;
|
||||
|
||||
public PacketDeserializer(EnumPacketDirection direction) {
|
||||
super(direction);
|
||||
this.direction = direction;
|
||||
try {
|
||||
directionMaps = EnumConnectionState.class.getDeclaredField(MCPNames.field("field_179247_h"));
|
||||
directionMaps.setAccessible(true);
|
||||
} catch (NoSuchFieldException e) {
|
||||
e.printStackTrace();
|
||||
} catch (SecurityException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public Packet getPacket(EnumPacketDirection direction, int packetId) throws InstantiationException, IllegalAccessException
|
||||
{
|
||||
Map map = ((Map)directionMaps.get(state));
|
||||
BiMap biMap = ((BiMap)map.get(direction));
|
||||
if(biMap == null) {
|
||||
System.out.println("BiMap is null!");
|
||||
}
|
||||
Class oclass = (Class)biMap.get(Integer.valueOf(packetId));
|
||||
return oclass == null ? null : (Packet)oclass.newInstance();
|
||||
}
|
||||
|
||||
public void setEnumConnectionState(EnumConnectionState state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decode(ChannelHandlerContext p_decode_1_,
|
||||
ByteBuf p_decode_2_, List p_decode_3_) throws IOException,
|
||||
InstantiationException, IllegalAccessException {
|
||||
|
||||
if (p_decode_2_.readableBytes() != 0)
|
||||
{
|
||||
PacketBuffer packetbuffer = new PacketBuffer(p_decode_2_);
|
||||
int i = packetbuffer.readVarIntFromBuffer();
|
||||
|
||||
Field state_by_id = null;
|
||||
try {
|
||||
state_by_id = EnumConnectionState.class.getDeclaredField("STATES_BY_ID"); //TODO: Localize
|
||||
state_by_id.setAccessible(true);
|
||||
state = (EnumConnectionState)((TIntObjectMap)state_by_id.get(null)).get(i);
|
||||
TIntObjectMap map = (TIntObjectMap)state_by_id.get(null);
|
||||
TIntObjectIterator it = map.iterator();
|
||||
while(it.hasNext()) {
|
||||
it.advance();
|
||||
System.out.println(it.key() +" | "+it.value().getClass());
|
||||
}
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if(state == null) {
|
||||
System.out.println("state is null");
|
||||
}
|
||||
//Packet packet = getPacket(this.direction, i);
|
||||
Packet packet = state.getPacket(EnumPacketDirection.CLIENTBOUND, i);
|
||||
|
||||
if (packet == null)
|
||||
{
|
||||
throw new IOException("Bad packet id " + i);
|
||||
}
|
||||
else
|
||||
{
|
||||
packet.readPacketData(packetbuffer);
|
||||
|
||||
if (packetbuffer.readableBytes() > 0)
|
||||
{
|
||||
throw new IOException("Packet " + ((EnumConnectionState)p_decode_1_.channel().attr(NetworkManager.attrKeyConnectionState).get()).getId() + "/" + i + " (" + packet.getClass().getSimpleName() + ") was larger than I expected, found " + packetbuffer.readableBytes() + " bytes extra whilst reading packet " + i);
|
||||
}
|
||||
else
|
||||
{
|
||||
p_decode_3_.add(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package eu.crushedpixel.replaymod.replay;
|
||||
|
||||
import net.minecraft.network.EnumConnectionState;
|
||||
|
||||
public class PacketInfo {
|
||||
|
||||
private byte[] bytes;
|
||||
private EnumConnectionState connectionState;
|
||||
private int packetID;
|
||||
|
||||
|
||||
|
||||
public PacketInfo(byte[] bytes, EnumConnectionState connectionState,
|
||||
int packetID) {
|
||||
super();
|
||||
this.bytes = bytes;
|
||||
this.connectionState = connectionState;
|
||||
this.packetID = packetID;
|
||||
}
|
||||
public byte[] getBytes() {
|
||||
return bytes;
|
||||
}
|
||||
public void setBytes(byte[] bytes) {
|
||||
this.bytes = bytes;
|
||||
}
|
||||
public EnumConnectionState getConnectionState() {
|
||||
return connectionState;
|
||||
}
|
||||
public void setConnectionState(EnumConnectionState connectionState) {
|
||||
this.connectionState = connectionState;
|
||||
}
|
||||
public int getPacketID() {
|
||||
return packetID;
|
||||
}
|
||||
public void setPacketID(int packetID) {
|
||||
this.packetID = packetID;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,361 @@
|
||||
package eu.crushedpixel.replaymod.replay;
|
||||
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.embedded.EmbeddedChannel;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.network.NetHandlerPlayClient;
|
||||
import net.minecraft.network.EnumPacketDirection;
|
||||
import net.minecraft.network.NetworkManager;
|
||||
import net.minecraft.network.play.INetHandlerPlayClient;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
|
||||
import eu.crushedpixel.replaymod.ReplayMod;
|
||||
import eu.crushedpixel.replaymod.chat.ChatMessageRequests;
|
||||
import eu.crushedpixel.replaymod.entities.CameraEntity;
|
||||
import eu.crushedpixel.replaymod.holders.Keyframe;
|
||||
import eu.crushedpixel.replaymod.holders.KeyframeComparator;
|
||||
import eu.crushedpixel.replaymod.holders.PositionKeyframe;
|
||||
import eu.crushedpixel.replaymod.holders.TimeKeyframe;
|
||||
|
||||
public class ReplayHandler {
|
||||
|
||||
private static NetworkManager networkManager;
|
||||
private static Minecraft mc = Minecraft.getMinecraft();
|
||||
private static ReplaySender replaySender;
|
||||
private static OpenEmbeddedChannel channel;
|
||||
|
||||
private static Keyframe selectedKeyframe;
|
||||
|
||||
private static boolean isReplaying = false;
|
||||
|
||||
private static CameraEntity cameraEntity;
|
||||
|
||||
private static List<Keyframe> keyframes = new ArrayList<Keyframe>();
|
||||
|
||||
private static boolean replayActive = false;
|
||||
|
||||
public static long lastExit = 0;
|
||||
|
||||
public static void setReplaying(boolean replaying) {
|
||||
isReplaying = replaying;
|
||||
}
|
||||
|
||||
public static void startPath() {
|
||||
ReplayProcess.startReplayProcess();
|
||||
}
|
||||
|
||||
public static void interruptReplay() {
|
||||
ReplayProcess.stopReplayProcess(false);
|
||||
}
|
||||
|
||||
public static boolean isReplaying() {
|
||||
return isReplaying;
|
||||
}
|
||||
|
||||
public static void setCameraEntity(CameraEntity entity) {
|
||||
if(entity == null) return;
|
||||
cameraEntity = entity;
|
||||
mc.setRenderViewEntity(cameraEntity);
|
||||
}
|
||||
|
||||
public static CameraEntity getCameraEntity() {
|
||||
return cameraEntity;
|
||||
}
|
||||
|
||||
public static int getReplayTime() {
|
||||
if(replaySender != null) {
|
||||
return (int)replaySender.currentTimeStamp();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static void sortKeyframes() {
|
||||
Collections.sort(keyframes, new KeyframeComparator());
|
||||
}
|
||||
|
||||
public static void addKeyframe(Keyframe keyframe) {
|
||||
keyframes.add(keyframe);
|
||||
selectKeyframe(keyframe);
|
||||
}
|
||||
|
||||
public static void removeKeyframe(Keyframe keyframe) {
|
||||
keyframes.remove(keyframe);
|
||||
if(keyframe == selectedKeyframe) {
|
||||
selectKeyframe(null);
|
||||
} else {
|
||||
sortKeyframes();
|
||||
}
|
||||
}
|
||||
|
||||
public static int getKeyframeIndex(TimeKeyframe timeKeyframe) {
|
||||
int index = 0;
|
||||
for(Keyframe kf : keyframes) {
|
||||
if(kf == timeKeyframe) return index;
|
||||
else if(kf instanceof TimeKeyframe) index++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int getKeyframeIndex(PositionKeyframe posKeyframe) {
|
||||
int index = 0;
|
||||
for(Keyframe kf : keyframes) {
|
||||
if(kf == posKeyframe) return index;
|
||||
else if(kf instanceof PositionKeyframe) index++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int getPosKeyframeCount() {
|
||||
int size = 0;
|
||||
for(Keyframe kf : keyframes) {
|
||||
if(kf instanceof PositionKeyframe) size++;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
public static int getTimeKeyframeCount() {
|
||||
int size = 0;
|
||||
for(Keyframe kf : keyframes) {
|
||||
if(kf instanceof TimeKeyframe) size++;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
public static TimeKeyframe getClosestTimeKeyframeForRealTime(int realTime, int tolerance) {
|
||||
List<TimeKeyframe> found = new ArrayList<TimeKeyframe>();
|
||||
for(Keyframe kf : keyframes) {
|
||||
if(!(kf instanceof TimeKeyframe)) continue;
|
||||
if(Math.abs(kf.getRealTimestamp()-realTime) <= tolerance) {
|
||||
found.add((TimeKeyframe)kf);
|
||||
}
|
||||
}
|
||||
|
||||
TimeKeyframe closest = null;
|
||||
|
||||
for(TimeKeyframe kf : found) {
|
||||
if(closest == null || Math.abs(closest.getTimestamp()-realTime) > Math.abs(kf.getRealTimestamp()-realTime)) {
|
||||
closest = kf;
|
||||
}
|
||||
}
|
||||
return closest;
|
||||
}
|
||||
|
||||
public static PositionKeyframe getClosestPlaceKeyframeForRealTime(int realTime, int tolerance) {
|
||||
List<PositionKeyframe> found = new ArrayList<PositionKeyframe>();
|
||||
for(Keyframe kf : keyframes) {
|
||||
if(!(kf instanceof PositionKeyframe)) continue;
|
||||
if(Math.abs(kf.getRealTimestamp()-realTime) <= tolerance) {
|
||||
found.add((PositionKeyframe)kf);
|
||||
}
|
||||
}
|
||||
|
||||
PositionKeyframe closest = null;
|
||||
|
||||
for(PositionKeyframe kf : found) {
|
||||
if(closest == null || Math.abs(closest.getRealTimestamp()-realTime) > Math.abs(kf.getRealTimestamp()-realTime)) {
|
||||
closest = kf;
|
||||
}
|
||||
}
|
||||
return closest;
|
||||
}
|
||||
|
||||
public static PositionKeyframe getPreviousPositionKeyframe(int realTime) {
|
||||
if(keyframes.isEmpty()) return null;
|
||||
List<PositionKeyframe> found = new ArrayList<PositionKeyframe>();
|
||||
for(Keyframe kf : keyframes) {
|
||||
if(!(kf instanceof PositionKeyframe)) continue;
|
||||
if(kf.getRealTimestamp() < realTime) {
|
||||
found.add((PositionKeyframe)kf);
|
||||
}
|
||||
}
|
||||
|
||||
if(found.size() > 0)
|
||||
return found.get(found.size()-1); //last element is nearest
|
||||
else return null;
|
||||
}
|
||||
|
||||
public static PositionKeyframe getNextPositionKeyframe(int realTime) {
|
||||
if(keyframes.isEmpty()) return null;
|
||||
for(Keyframe kf : keyframes) {
|
||||
if(!(kf instanceof PositionKeyframe)) continue;
|
||||
if(kf.getRealTimestamp() >= realTime) {
|
||||
return (PositionKeyframe)kf; //first found element is next
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static TimeKeyframe getPreviousTimeKeyframe(int realTime) {
|
||||
if(keyframes.isEmpty()) return null;
|
||||
List<TimeKeyframe> found = new ArrayList<TimeKeyframe>();
|
||||
for(Keyframe kf : keyframes) {
|
||||
if(!(kf instanceof TimeKeyframe)) continue;
|
||||
if(kf.getRealTimestamp() < realTime) {
|
||||
found.add((TimeKeyframe)kf);
|
||||
}
|
||||
}
|
||||
|
||||
if(found.size() > 0)
|
||||
return found.get(found.size()-1); //last element is nearest
|
||||
else return null;
|
||||
}
|
||||
|
||||
public static TimeKeyframe getNextTimeKeyframe(int realTime) {
|
||||
if(keyframes.isEmpty()) return null;
|
||||
for(Keyframe kf : keyframes) {
|
||||
if(!(kf instanceof TimeKeyframe)) continue;
|
||||
if(kf.getRealTimestamp() >= realTime) {
|
||||
return (TimeKeyframe)kf; //first found element is next
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static List<Keyframe> getKeyframes() {
|
||||
return new ArrayList<Keyframe>(keyframes);
|
||||
}
|
||||
|
||||
public static void resetKeyframes() {
|
||||
keyframes = new ArrayList<Keyframe>();
|
||||
selectKeyframe(null);
|
||||
}
|
||||
|
||||
public static void setReplayPos(int pos, boolean force) {
|
||||
if(replaySender != null) {
|
||||
replaySender.jumpToTime(pos, force);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isHurrying() {
|
||||
if(replaySender != null) {
|
||||
return replaySender.isHurrying();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static int getReplayLength() {
|
||||
if(replaySender != null) {
|
||||
return replaySender.replayLength();
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
public static boolean isSelected(Keyframe kf) {
|
||||
return kf == selectedKeyframe;
|
||||
}
|
||||
|
||||
public static void selectKeyframe(Keyframe kf) {
|
||||
selectedKeyframe = kf;
|
||||
sortKeyframes();
|
||||
}
|
||||
|
||||
public static boolean replayActive() {
|
||||
return replayActive;
|
||||
}
|
||||
|
||||
public static boolean isPaused() {
|
||||
if(replaySender != null) {
|
||||
return replaySender.paused();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void setSpeed(double d) {
|
||||
if(replaySender != null) {
|
||||
replaySender.setReplaySpeed(d);
|
||||
}
|
||||
}
|
||||
|
||||
public static double getSpeed() {
|
||||
if(replaySender != null) {
|
||||
return replaySender.getReplaySpeed();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static void startReplay(File file) throws NoSuchMethodException, SecurityException, NoSuchFieldException {
|
||||
|
||||
ChatMessageRequests.initialize();
|
||||
mc.ingameGUI.getChatGUI().clearChatMessages();
|
||||
resetKeyframes();
|
||||
|
||||
if(replaySender != null) {
|
||||
replaySender.terminateReplay();
|
||||
}
|
||||
|
||||
if(channel != null) {
|
||||
channel.close();
|
||||
}
|
||||
|
||||
networkManager = new NetworkManager(EnumPacketDirection.CLIENTBOUND);
|
||||
INetHandlerPlayClient pc = new NetHandlerPlayClient(mc, (GuiScreen)null, networkManager, new GameProfile(UUID.randomUUID(), "Player"));
|
||||
networkManager.setNetHandler(pc);
|
||||
|
||||
channel = new OpenEmbeddedChannel(networkManager);
|
||||
|
||||
replaySender = new ReplaySender(file, networkManager);
|
||||
channel.pipeline().addFirst(replaySender);
|
||||
channel.pipeline().fireChannelActive();
|
||||
|
||||
try {
|
||||
ReplayMod.overlay.resetUI();
|
||||
} catch(Exception e) {}
|
||||
|
||||
replayActive = true;
|
||||
}
|
||||
|
||||
public static void restartReplay() {
|
||||
//mc.setRenderViewEntity(mc.thePlayer);
|
||||
mc.ingameGUI.getChatGUI().clearChatMessages();
|
||||
|
||||
if(channel != null) {
|
||||
channel.close();
|
||||
}
|
||||
|
||||
networkManager = new NetworkManager(EnumPacketDirection.CLIENTBOUND);
|
||||
INetHandlerPlayClient pc = new NetHandlerPlayClient(mc, (GuiScreen)null, networkManager, new GameProfile(UUID.randomUUID(), "Player"));
|
||||
networkManager.setNetHandler(pc);
|
||||
|
||||
EmbeddedChannel channel = new OpenEmbeddedChannel(networkManager);
|
||||
|
||||
channel.pipeline().addFirst(replaySender);
|
||||
channel.pipeline().fireChannelActive();
|
||||
|
||||
ChannelPipeline pipeline = networkManager.channel().pipeline();
|
||||
|
||||
try {
|
||||
ReplayMod.overlay.resetUI();
|
||||
} catch(Exception e) {}
|
||||
|
||||
replayActive = true;
|
||||
}
|
||||
|
||||
public static void endReplay() {
|
||||
if(replaySender != null) {
|
||||
replaySender.terminateReplay();
|
||||
}
|
||||
|
||||
resetKeyframes();
|
||||
|
||||
if(channel != null) {
|
||||
channel.close();
|
||||
}
|
||||
|
||||
replayActive = false;
|
||||
}
|
||||
|
||||
public static Keyframe getSelected() {
|
||||
return selectedKeyframe;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
package eu.crushedpixel.replaymod.replay;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import eu.crushedpixel.replaymod.ReplayMod;
|
||||
import eu.crushedpixel.replaymod.chat.ChatMessageRequests;
|
||||
import eu.crushedpixel.replaymod.chat.ChatMessageRequests.ChatMessageType;
|
||||
import eu.crushedpixel.replaymod.holders.Keyframe;
|
||||
import eu.crushedpixel.replaymod.holders.Position;
|
||||
import eu.crushedpixel.replaymod.holders.PositionKeyframe;
|
||||
import eu.crushedpixel.replaymod.holders.TimeKeyframe;
|
||||
import eu.crushedpixel.replaymod.interpolation.LinearPoint;
|
||||
import eu.crushedpixel.replaymod.interpolation.LinearTimestamp;
|
||||
import eu.crushedpixel.replaymod.interpolation.SplinePoint;
|
||||
|
||||
public class ReplayProcess {
|
||||
|
||||
private static Minecraft mc = Minecraft.getMinecraft();
|
||||
|
||||
private static long startRealTime;
|
||||
private static int lastRealReplayTime;
|
||||
private static long lastRealTime = 0;
|
||||
|
||||
private static boolean linear = false;
|
||||
|
||||
private static Position lastPosition = null;
|
||||
private static int lastTimestamp = -1;
|
||||
|
||||
private static SplinePoint motionSpline = null;
|
||||
private static LinearPoint motionLinear = null;
|
||||
private static LinearTimestamp timeLinear = null;
|
||||
|
||||
private static double previousReplaySpeed = 0;
|
||||
|
||||
public static void startReplayProcess() {
|
||||
ChatMessageRequests.initialize();
|
||||
if(ReplayHandler.getKeyframes().isEmpty()) {
|
||||
ChatMessageRequests.addChatMessage("No keyframes set!", ChatMessageType.WARNING);
|
||||
return;
|
||||
}
|
||||
startRealTime = System.currentTimeMillis();
|
||||
lastRealTime = startRealTime;
|
||||
lastRealReplayTime = 0;
|
||||
lastTimestamp = -1;
|
||||
lastPosition = null;
|
||||
motionSpline = null;
|
||||
timeLinear = null;
|
||||
linear = ReplayMod.replaySettings.isLinearMovement();
|
||||
ReplayHandler.sortKeyframes();
|
||||
ReplayHandler.setReplaying(true);
|
||||
previousReplaySpeed = ReplayHandler.getSpeed();
|
||||
TimeKeyframe tf = ReplayHandler.getNextTimeKeyframe(-1);
|
||||
if(tf != null) {
|
||||
int ts = tf.getTimestamp();
|
||||
ReplayHandler.setReplayPos(ts, true);
|
||||
}
|
||||
ChatMessageRequests.addChatMessage("Replay started!", ChatMessageType.INFORMATION);
|
||||
}
|
||||
|
||||
public static void stopReplayProcess(boolean finished) {
|
||||
if(finished) ChatMessageRequests.addChatMessage("Replay finished!", ChatMessageType.INFORMATION);
|
||||
else ChatMessageRequests.addChatMessage("Replay stopped!", ChatMessageType.INFORMATION);
|
||||
ReplayHandler.setReplaying(false);
|
||||
ReplayHandler.setSpeed(previousReplaySpeed);
|
||||
ReplayHandler.setSpeed(0);
|
||||
}
|
||||
|
||||
public static void tickReplay() {
|
||||
if(!ReplayHandler.isReplaying()) return;
|
||||
if(ReplayHandler.isHurrying()) {
|
||||
lastRealTime = System.currentTimeMillis();
|
||||
System.out.println("rethurrn");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!linear && motionSpline == null) {
|
||||
//set up spline path
|
||||
motionSpline = new SplinePoint();
|
||||
for(Keyframe kf : ReplayHandler.getKeyframes()) {
|
||||
if(kf instanceof PositionKeyframe) {
|
||||
PositionKeyframe pkf = (PositionKeyframe)kf;
|
||||
Position pos = pkf.getPosition();
|
||||
motionSpline.addPoint(pos);
|
||||
}
|
||||
}
|
||||
if(motionSpline.getPoints().size() < 3) linear = true;
|
||||
else motionSpline.calcSpline();
|
||||
|
||||
}
|
||||
if(linear && motionLinear == null) {
|
||||
//set up linear path
|
||||
motionLinear = new LinearPoint();
|
||||
for(Keyframe kf : ReplayHandler.getKeyframes()) {
|
||||
if(kf instanceof PositionKeyframe) {
|
||||
PositionKeyframe pkf = (PositionKeyframe)kf;
|
||||
Position pos = pkf.getPosition();
|
||||
motionLinear.addPoint(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(timeLinear == null) {
|
||||
timeLinear = new LinearTimestamp();
|
||||
for(Keyframe kf : ReplayHandler.getKeyframes()) {
|
||||
if(kf instanceof TimeKeyframe) {
|
||||
timeLinear.addPoint(((TimeKeyframe)kf).getTimestamp());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
for(float x = 0; x <= 1f; x+=0.1) {
|
||||
//timeLinear.getPoint(x);
|
||||
System.out.println(x+" | "+timeLinear.getPoint(x));
|
||||
}
|
||||
*/
|
||||
//System.out.println(timeLinear.getPoint(0));
|
||||
}
|
||||
|
||||
long curTime = System.currentTimeMillis();
|
||||
long timeStep = curTime - lastRealTime;
|
||||
|
||||
int curRealReplayTime = (int)(lastRealReplayTime + timeStep);
|
||||
|
||||
PositionKeyframe lastPos = ReplayHandler.getPreviousPositionKeyframe(curRealReplayTime);
|
||||
PositionKeyframe nextPos = ReplayHandler.getNextPositionKeyframe(curRealReplayTime);
|
||||
|
||||
int lastPosStamp = 0;
|
||||
int nextPosStamp = 0;
|
||||
|
||||
if(nextPos != null || lastPos != null) {
|
||||
if(nextPos != null) {
|
||||
nextPosStamp = nextPos.getRealTimestamp();
|
||||
} else {
|
||||
nextPosStamp = lastPos.getRealTimestamp();
|
||||
}
|
||||
|
||||
if(lastPos != null) {
|
||||
lastPosStamp = lastPos.getRealTimestamp();
|
||||
} else {
|
||||
lastPosStamp = nextPos.getRealTimestamp();
|
||||
}
|
||||
}
|
||||
|
||||
TimeKeyframe lastTime = ReplayHandler.getPreviousTimeKeyframe(curRealReplayTime);
|
||||
TimeKeyframe nextTime = ReplayHandler.getNextTimeKeyframe(curRealReplayTime);
|
||||
|
||||
int lastTimeStamp = 0;
|
||||
int nextTimeStamp = 0;
|
||||
|
||||
double curSpeed = 0f;
|
||||
|
||||
if(nextTime != null || lastTime != null) {
|
||||
if(nextTime != null) {
|
||||
nextTimeStamp = nextTime.getRealTimestamp();
|
||||
} else {
|
||||
nextTimeStamp = lastTime.getRealTimestamp();
|
||||
}
|
||||
|
||||
if(lastTime != null) {
|
||||
lastTimeStamp = lastTime.getRealTimestamp();
|
||||
} else {
|
||||
lastTimeStamp = nextTime.getRealTimestamp();
|
||||
}
|
||||
|
||||
if(!(nextTime == null || lastTime == null)) {
|
||||
curSpeed = ((double)((nextTime.getTimestamp()-lastTime.getTimestamp())))/((double)((nextTimeStamp-lastTimeStamp)));
|
||||
}
|
||||
}
|
||||
|
||||
int currentDiff = nextPosStamp - lastPosStamp;
|
||||
int current = curRealReplayTime - lastPosStamp;
|
||||
|
||||
float currentStepPerc = (float)current/(float)currentDiff; //The percentage of the travelled path between the current positions
|
||||
if(Float.isInfinite(currentStepPerc)) currentStepPerc = 0;
|
||||
|
||||
float splinePos = ((float)ReplayHandler.getKeyframeIndex(lastPos) + currentStepPerc)/(float)(ReplayHandler.getPosKeyframeCount()-1);
|
||||
float timePos = ((float)ReplayHandler.getKeyframeIndex(lastTime) + currentStepPerc)/(float)(ReplayHandler.getTimeKeyframeCount()-1);
|
||||
|
||||
Position pos = null;
|
||||
if(!linear) {
|
||||
pos = motionSpline.getPoint(Math.max(0, Math.min(1, splinePos)));
|
||||
} else {
|
||||
pos = motionLinear.getPoint(Math.max(0, Math.min(1, splinePos)));
|
||||
}
|
||||
|
||||
//int curPos = timeLinear.getPoint(Math.max(0, Math.min(1, timePos)));
|
||||
|
||||
//set replay speed
|
||||
if(pos != null) ReplayHandler.getCameraEntity().movePath(pos);
|
||||
//System.out.println(curSpeed+" | "+curPos);
|
||||
//ReplayHandler.setSpeed(curSpeed);
|
||||
//System.out.println("sent "+curPos);
|
||||
//ReplayHandler.setReplayPos(curPos, timePos == 0);
|
||||
|
||||
|
||||
//calculate replay speed
|
||||
|
||||
/*
|
||||
int ts = timeLinear.getPoint(Math.max(0, Math.min(1, splinePos)));
|
||||
if(ts != lastTimestamp) {
|
||||
lastTimestamp = ts;
|
||||
}
|
||||
*/
|
||||
|
||||
//splinePos = (index of last entry + add) / total entries
|
||||
|
||||
|
||||
lastRealReplayTime = curRealReplayTime;
|
||||
lastRealTime = curTime;
|
||||
|
||||
if(splinePos >= 1) stopReplayProcess(true);
|
||||
}
|
||||
}
|
||||
528
src/main/java/eu/crushedpixel/replaymod/replay/ReplaySender.java
Normal file
528
src/main/java/eu/crushedpixel/replaymod/replay/ReplaySender.java
Normal file
@@ -0,0 +1,528 @@
|
||||
package eu.crushedpixel.replaymod.replay;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandler.Sharable;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiDownloadTerrain;
|
||||
import net.minecraft.client.particle.EffectRenderer;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.network.EnumConnectionState;
|
||||
import net.minecraft.network.EnumPacketDirection;
|
||||
import net.minecraft.network.NetworkManager;
|
||||
import net.minecraft.network.Packet;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
import net.minecraft.network.play.server.S01PacketJoinGame;
|
||||
import net.minecraft.network.play.server.S02PacketChat;
|
||||
import net.minecraft.network.play.server.S06PacketUpdateHealth;
|
||||
import net.minecraft.network.play.server.S08PacketPlayerPosLook;
|
||||
import net.minecraft.network.play.server.S0BPacketAnimation;
|
||||
import net.minecraft.network.play.server.S0CPacketSpawnPlayer;
|
||||
import net.minecraft.network.play.server.S14PacketEntity;
|
||||
import net.minecraft.network.play.server.S18PacketEntityTeleport;
|
||||
import net.minecraft.network.play.server.S1CPacketEntityMetadata;
|
||||
import net.minecraft.network.play.server.S1DPacketEntityEffect;
|
||||
import net.minecraft.network.play.server.S28PacketEffect;
|
||||
import net.minecraft.network.play.server.S29PacketSoundEffect;
|
||||
import net.minecraft.network.play.server.S2BPacketChangeGameState;
|
||||
import net.minecraft.network.play.server.S2DPacketOpenWindow;
|
||||
import net.minecraft.network.play.server.S2EPacketCloseWindow;
|
||||
import net.minecraft.network.play.server.S2FPacketSetSlot;
|
||||
import net.minecraft.network.play.server.S30PacketWindowItems;
|
||||
import net.minecraft.network.play.server.S36PacketSignEditorOpen;
|
||||
import net.minecraft.network.play.server.S37PacketStatistics;
|
||||
import net.minecraft.network.play.server.S39PacketPlayerAbilities;
|
||||
import net.minecraft.network.play.server.S43PacketCamera;
|
||||
import net.minecraft.network.play.server.S45PacketTitle;
|
||||
import net.minecraft.network.play.server.S14PacketEntity.S17PacketEntityLookMove;
|
||||
import net.minecraft.util.Timer;
|
||||
import net.minecraft.world.EnumDifficulty;
|
||||
import net.minecraft.world.WorldSettings.GameType;
|
||||
import net.minecraft.world.WorldType;
|
||||
import net.minecraftforge.fml.client.FMLClientHandler;
|
||||
import net.minecraftforge.fml.common.FMLCommonHandler;
|
||||
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import eu.crushedpixel.replaymod.entities.CameraEntity;
|
||||
import eu.crushedpixel.replaymod.recording.ConnectionEventHandler;
|
||||
import eu.crushedpixel.replaymod.recording.ReplayMetaData;
|
||||
import eu.crushedpixel.replaymod.reflection.MCPNames;
|
||||
|
||||
@Sharable
|
||||
public class ReplaySender extends ChannelInboundHandlerAdapter {
|
||||
|
||||
private long currentTimeStamp;
|
||||
private boolean hurryToTimestamp;
|
||||
private long desiredTimeStamp;
|
||||
private long lastTimeStamp, lastPacketSent;
|
||||
|
||||
private long toleratedTimeStamp;
|
||||
|
||||
private File replayFile;
|
||||
private PacketDeserializer ds = new PacketDeserializer(EnumPacketDirection.SERVERBOUND);
|
||||
private boolean active = true;
|
||||
private ZipFile archive;
|
||||
private DataInputStream dis;
|
||||
private ChannelHandlerContext ctx = null;
|
||||
|
||||
private boolean startFromBeginning = true;
|
||||
|
||||
private NetworkManager networkManager;
|
||||
private boolean terminate = false;
|
||||
|
||||
private float defaultReplaySpeed = 0.5f;
|
||||
|
||||
private int packetAt = 0;
|
||||
private double replaySpeed = 1f;
|
||||
private long lastTime = 0, lastTimestamp = 0;
|
||||
|
||||
private Field joinPacketEntityId, joinPacketWorldType,
|
||||
joinPacketDimension, joinPacketDifficulty, joinPacketMaxPlayers;
|
||||
|
||||
private Field effectPacketEntityId;
|
||||
private Field metadataPacketEntityId, metadataPacketList;
|
||||
|
||||
private Field animationPacketEntityId;
|
||||
private Field entityDataWatcher;
|
||||
|
||||
private Field chatPacketPosition;
|
||||
|
||||
private Minecraft mc = Minecraft.getMinecraft();
|
||||
private Field mcTimer;
|
||||
|
||||
private long now = System.currentTimeMillis();
|
||||
|
||||
private int replayLength = 0;
|
||||
|
||||
private EffectRenderer old = mc.effectRenderer;
|
||||
|
||||
private ZipArchiveEntry replayEntry;
|
||||
|
||||
public boolean isHurrying() {
|
||||
return hurryToTimestamp;
|
||||
}
|
||||
|
||||
public long currentTimeStamp() {
|
||||
return currentTimeStamp;
|
||||
}
|
||||
|
||||
public int replayLength() {
|
||||
return replayLength;
|
||||
}
|
||||
|
||||
public void terminateReplay() {
|
||||
terminate = true;
|
||||
try {
|
||||
channelInactive(ctx);
|
||||
ctx.channel().pipeline().close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void jumpToTime(int millis, boolean forceReload) {
|
||||
setReplaySpeed(replaySpeed);
|
||||
|
||||
if((millis < currentTimeStamp && !isHurrying()) || forceReload) {
|
||||
if(forceReload) System.out.println("forced");
|
||||
if(ReplayHandler.isReplaying()) {
|
||||
if(forceReload) {
|
||||
startFromBeginning = true;
|
||||
}
|
||||
else {
|
||||
startFromBeginning = false;
|
||||
desiredTimeStamp = millis;
|
||||
hurryToTimestamp = false;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
startFromBeginning = true;
|
||||
}
|
||||
}
|
||||
|
||||
desiredTimeStamp = millis;
|
||||
hurryToTimestamp = true;
|
||||
}
|
||||
|
||||
public void setReplaySpeed(final double d) {
|
||||
if(d != 0) this.replaySpeed = d;
|
||||
|
||||
try {
|
||||
Timer timer = (Timer)mcTimer.get(mc);
|
||||
timer.timerSpeed = (float)d;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public ReplaySender(final File replayFile, NetworkManager nm) {
|
||||
try {
|
||||
joinPacketEntityId = S01PacketJoinGame.class.getDeclaredField(MCPNames.field("field_149206_a"));
|
||||
joinPacketEntityId.setAccessible(true);
|
||||
|
||||
joinPacketDifficulty = S01PacketJoinGame.class.getDeclaredField(MCPNames.field("field_149203_e"));
|
||||
joinPacketDifficulty.setAccessible(true);
|
||||
|
||||
joinPacketDimension = S01PacketJoinGame.class.getDeclaredField(MCPNames.field("field_149202_d"));
|
||||
joinPacketDimension.setAccessible(true);
|
||||
|
||||
joinPacketMaxPlayers = S01PacketJoinGame.class.getDeclaredField(MCPNames.field("field_149200_f"));
|
||||
joinPacketMaxPlayers.setAccessible(true);
|
||||
|
||||
joinPacketWorldType = S01PacketJoinGame.class.getDeclaredField(MCPNames.field("field_149201_g"));
|
||||
joinPacketWorldType.setAccessible(true);
|
||||
|
||||
mcTimer = Minecraft.class.getDeclaredField(MCPNames.field("field_71428_T"));
|
||||
mcTimer.setAccessible(true);
|
||||
|
||||
effectPacketEntityId = S1DPacketEntityEffect.class.getDeclaredField(MCPNames.field("field_149434_a"));
|
||||
effectPacketEntityId.setAccessible(true);
|
||||
|
||||
metadataPacketEntityId = S1CPacketEntityMetadata.class.getDeclaredField(MCPNames.field("field_149379_a"));
|
||||
metadataPacketEntityId.setAccessible(true);
|
||||
|
||||
metadataPacketList = S1CPacketEntityMetadata.class.getDeclaredField(MCPNames.field("field_149378_b"));
|
||||
metadataPacketList.setAccessible(true);
|
||||
|
||||
animationPacketEntityId = S0BPacketAnimation.class.getDeclaredField(MCPNames.field("field_148981_a"));
|
||||
animationPacketEntityId.setAccessible(true);
|
||||
|
||||
entityDataWatcher = Entity.class.getDeclaredField(MCPNames.field("field_70180_af"));
|
||||
entityDataWatcher.setAccessible(true);
|
||||
|
||||
chatPacketPosition = S02PacketChat.class.getDeclaredField(MCPNames.field("field_179842_b"));
|
||||
chatPacketPosition.setAccessible(true);
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
this.replayFile = replayFile;
|
||||
this.networkManager = nm;
|
||||
if(("."+FilenameUtils.getExtension(replayFile.getAbsolutePath())).equals(ConnectionEventHandler.ZIP_FILE_EXTENSION)) {
|
||||
try {
|
||||
archive = new ZipFile(replayFile);
|
||||
replayEntry = archive.getEntry("recording"+ConnectionEventHandler.TEMP_FILE_EXTENSION);
|
||||
|
||||
ZipArchiveEntry metadata = archive.getEntry("metaData"+ConnectionEventHandler.JSON_FILE_EXTENSION);
|
||||
InputStream is = archive.getInputStream(metadata);
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(is));
|
||||
|
||||
String json = br.readLine();
|
||||
|
||||
ReplayMetaData metaData = new Gson().fromJson(json, ReplayMetaData.class);
|
||||
|
||||
this.replayLength = metaData.getDuration();
|
||||
|
||||
sender.start();
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Thread sender = new Thread(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
try {
|
||||
dis = new DataInputStream(archive.getInputStream(replayEntry));
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
try {
|
||||
while(ctx == null && !terminate) {
|
||||
Thread.sleep(10);
|
||||
}
|
||||
while(!terminate) {
|
||||
if(startFromBeginning) {
|
||||
currentTimeStamp = 0;
|
||||
dis = new DataInputStream(archive.getInputStream(replayEntry));
|
||||
startFromBeginning = false;
|
||||
ReplayHandler.restartReplay();
|
||||
}
|
||||
while(!startFromBeginning && (!paused() || FMLClientHandler.instance().isGUIOpen(GuiDownloadTerrain.class))) {
|
||||
try {
|
||||
|
||||
/*
|
||||
* LOGIC:
|
||||
* While behind desired timestamp, only send packets
|
||||
* until desired timestamp is reached,
|
||||
* then increase desired timestamp by 1/20th of a second
|
||||
*
|
||||
* Desired timestamp is divided through stretch factor.
|
||||
*
|
||||
* If hurrying, don't wait for correct timing.
|
||||
*/
|
||||
|
||||
if(!hurryToTimestamp && ReplayHandler.isReplaying()) {
|
||||
continue;
|
||||
} else if(ReplayHandler.isReplaying()) {
|
||||
System.out.println("nocont "+currentTimeStamp+" | "+((Timer)mcTimer.get(mc)).timerSpeed);
|
||||
}
|
||||
|
||||
int timestamp = dis.readInt();
|
||||
|
||||
currentTimeStamp = timestamp;
|
||||
|
||||
if(!ReplayHandler.isReplaying() && !hurryToTimestamp && !FMLClientHandler.instance().isGUIOpen(GuiDownloadTerrain.class)) {
|
||||
int timeWait = (int)Math.round((currentTimeStamp - lastTimeStamp)/replaySpeed);
|
||||
long timeDiff = System.currentTimeMillis() - lastPacketSent;
|
||||
lastPacketSent = System.currentTimeMillis();
|
||||
Thread.sleep(Math.max(0, timeWait-timeDiff));
|
||||
}
|
||||
|
||||
int bytes = dis.readInt();
|
||||
byte[] bb = new byte[bytes];
|
||||
dis.readFully(bb);
|
||||
|
||||
ReplaySender.this.channelRead(ctx, bb);
|
||||
|
||||
lastTimeStamp = currentTimeStamp;
|
||||
|
||||
if(hurryToTimestamp && currentTimeStamp >= desiredTimeStamp && !startFromBeginning) {
|
||||
toleratedTimeStamp = currentTimeStamp;
|
||||
hurryToTimestamp = false;
|
||||
System.out.println("stop hurrying");
|
||||
}
|
||||
|
||||
} catch(EOFException eof) {
|
||||
setReplaySpeed(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
private List<Class> packetClasses = new ArrayList<Class>();
|
||||
|
||||
private static Field field_149074_a; //TODO: REMOVE
|
||||
static {
|
||||
try {
|
||||
field_149074_a = S14PacketEntity.class.getDeclaredField(MCPNames.field("field_149074_a"));
|
||||
field_149074_a.setAccessible(true);
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg)
|
||||
throws Exception {
|
||||
if(terminate) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(msg instanceof Packet) {
|
||||
super.channelRead(ctx, msg);
|
||||
return;
|
||||
}
|
||||
byte[] ba = (byte[])msg;
|
||||
|
||||
try {
|
||||
ByteBuf bb = Unpooled.buffer(ba.length);
|
||||
PacketBuffer pb = new PacketBuffer(bb);
|
||||
|
||||
pb.writeBytes(ba);
|
||||
|
||||
int i = pb.readVarIntFromBuffer();
|
||||
|
||||
Packet p = EnumConnectionState.PLAY.getPacket(EnumPacketDirection.CLIENTBOUND, i);
|
||||
|
||||
if(hurryToTimestamp) { //If hurrying, ignore some packets
|
||||
if(p instanceof S45PacketTitle ||
|
||||
p instanceof S29PacketSoundEffect) return;
|
||||
}
|
||||
|
||||
if(p instanceof S28PacketEffect) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(p instanceof S2BPacketChangeGameState) { //TODO: Test with rain
|
||||
return;
|
||||
}
|
||||
|
||||
if(p instanceof S06PacketUpdateHealth) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(p instanceof S2DPacketOpenWindow) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(p instanceof S2EPacketCloseWindow) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(p instanceof S2FPacketSetSlot) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(p instanceof S30PacketWindowItems) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(p instanceof S36PacketSignEditorOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(p instanceof S02PacketChat) {
|
||||
byte pos = (Byte)chatPacketPosition.get(p);
|
||||
if(pos == 1) { //Ignores command block output sent
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(p instanceof S1CPacketEntityMetadata) {
|
||||
int entityId = (Integer)metadataPacketEntityId.get(p);
|
||||
}
|
||||
|
||||
if(p instanceof S0BPacketAnimation) {
|
||||
int entityId = (Integer)animationPacketEntityId.get(p);
|
||||
if(entityId == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(p instanceof S1DPacketEntityEffect) {
|
||||
int entityId = (Integer)effectPacketEntityId.get(p);
|
||||
if(entityId == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(p instanceof S37PacketStatistics) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
p.readPacketData(pb);
|
||||
|
||||
if(p instanceof S0CPacketSpawnPlayer) {
|
||||
S0CPacketSpawnPlayer sp = (S0CPacketSpawnPlayer)p;
|
||||
System.out.println("PACKET SPAWN PLAYER ----------");
|
||||
System.out.println("ENTITY ID: "+sp.func_148943_d());
|
||||
System.out.println("UUID: "+sp.func_179819_c());
|
||||
System.out.println("X: "+sp.func_148942_f());
|
||||
System.out.println("Y: "+sp.func_148949_g());
|
||||
System.out.println("Z: "+sp.func_148946_h());
|
||||
System.out.println("YAW: "+sp.func_148941_i());
|
||||
System.out.println("PITCH: "+sp.func_148945_j());
|
||||
System.out.println("ITEM: "+sp.func_148947_k());
|
||||
System.out.println("PACKET END -------------------");
|
||||
}
|
||||
|
||||
if(p instanceof S01PacketJoinGame) {
|
||||
|
||||
int entId = (Integer)joinPacketEntityId.get(p);
|
||||
entId = -1;
|
||||
int dimension = (Integer)joinPacketDimension.get(p);
|
||||
EnumDifficulty difficulty = (EnumDifficulty)joinPacketDifficulty.get(p);
|
||||
int maxPlayers = (Integer)joinPacketMaxPlayers.get(p);
|
||||
WorldType worldType = (WorldType)joinPacketWorldType.get(p);
|
||||
|
||||
p = new S01PacketJoinGame(entId, GameType.SPECTATOR, false, dimension,
|
||||
difficulty, maxPlayers, worldType, false);
|
||||
}
|
||||
|
||||
if(p instanceof S39PacketPlayerAbilities) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(p instanceof S08PacketPlayerPosLook) {
|
||||
final S08PacketPlayerPosLook ppl = (S08PacketPlayerPosLook)p;
|
||||
|
||||
if(ReplayHandler.isReplaying()) return;
|
||||
Thread t = new Thread(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while(mc.theWorld == null) {
|
||||
try {
|
||||
Thread.sleep(10);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Entity ent = ReplayHandler.getCameraEntity();
|
||||
if(ent == null || !(ent instanceof CameraEntity)) ent = new CameraEntity(mc.theWorld);
|
||||
CameraEntity cent = (CameraEntity)ent;
|
||||
cent.moveAbsolute(ppl.func_148932_c(), ppl.func_148928_d(), ppl.func_148933_e());
|
||||
|
||||
ReplayHandler.setCameraEntity(cent);
|
||||
}
|
||||
});
|
||||
|
||||
t.start();
|
||||
}
|
||||
|
||||
if(p instanceof S43PacketCamera) {
|
||||
return;
|
||||
}
|
||||
if(ReplayHandler.isReplaying())
|
||||
System.out.println("packet arrived");
|
||||
super.channelRead(ctx, p);
|
||||
} catch(Exception e) {
|
||||
System.out.println(p.getClass());
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
} catch(Exception e) {
|
||||
//e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
||||
this.ctx = ctx;
|
||||
networkManager.channel().attr(networkManager.attrKeyConnectionState).set(EnumConnectionState.PLAY);
|
||||
super.channelActive(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
archive.close();
|
||||
super.channelInactive(ctx);
|
||||
}
|
||||
|
||||
public boolean paused() {
|
||||
try {
|
||||
return ((Timer)mcTimer.get(mc)).timerSpeed == 0;
|
||||
} catch(Exception e) {}
|
||||
return true;
|
||||
}
|
||||
|
||||
public double getReplaySpeed() {
|
||||
if(!paused()) return replaySpeed;
|
||||
else return 0;
|
||||
//return timeInfo.get().getSpeed();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package eu.crushedpixel.replaymod.settings;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import net.minecraftforge.common.config.Property;
|
||||
import eu.crushedpixel.replaymod.ReplayMod;
|
||||
|
||||
public class ReplaySettings {
|
||||
|
||||
private int maximumFileSize = 0;
|
||||
private boolean enableRecordingServer = true;
|
||||
private boolean enableRecordingSingleplayer = true;
|
||||
private boolean showNotifications = true;
|
||||
private boolean forceLinearPath = false;
|
||||
|
||||
public ReplaySettings(int maximumFileSize, boolean enableRecordingServer,
|
||||
boolean enableRecordingSingleplayer, boolean showNotifications, boolean forceLinearPath) {
|
||||
this.maximumFileSize = maximumFileSize;
|
||||
this.enableRecordingServer = enableRecordingServer;
|
||||
this.enableRecordingSingleplayer = enableRecordingSingleplayer;
|
||||
this.showNotifications = showNotifications;
|
||||
this.forceLinearPath = forceLinearPath;
|
||||
}
|
||||
|
||||
public int getMaximumFileSize() {
|
||||
return maximumFileSize;
|
||||
}
|
||||
public void setMaximumFileSize(int maximumFileSize) {
|
||||
this.maximumFileSize = maximumFileSize;
|
||||
rewriteSettings();
|
||||
}
|
||||
public boolean isEnableRecordingServer() {
|
||||
return enableRecordingServer;
|
||||
}
|
||||
public void setEnableRecordingServer(boolean enableRecordingServer) {
|
||||
this.enableRecordingServer = enableRecordingServer;
|
||||
rewriteSettings();
|
||||
}
|
||||
public boolean isEnableRecordingSingleplayer() {
|
||||
return enableRecordingSingleplayer;
|
||||
}
|
||||
public void setEnableRecordingSingleplayer(boolean enableRecordingSingleplayer) {
|
||||
this.enableRecordingSingleplayer = enableRecordingSingleplayer;
|
||||
rewriteSettings();
|
||||
}
|
||||
public boolean isShowNotifications() {
|
||||
return showNotifications;
|
||||
}
|
||||
public void setShowNotifications(boolean showNotifications) {
|
||||
this.showNotifications = showNotifications;
|
||||
rewriteSettings();
|
||||
}
|
||||
public boolean isLinearMovement() {
|
||||
return forceLinearPath;
|
||||
}
|
||||
public void setLinearMovement(boolean linear) {
|
||||
this.forceLinearPath = linear;
|
||||
rewriteSettings();
|
||||
}
|
||||
|
||||
public Map<String, Object> getOptions() {
|
||||
Map<String, Object> map = new LinkedHashMap<String, Object>();
|
||||
|
||||
map.put("Enable Notifications", showNotifications);
|
||||
map.put("Maximum File Size", maximumFileSize);
|
||||
map.put("Record Server", enableRecordingServer);
|
||||
map.put("Record Singleplayer", enableRecordingSingleplayer);
|
||||
map.put("Force Linear Movement", forceLinearPath);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
public void setOptions(Map<String, Object> map) {
|
||||
try {
|
||||
|
||||
maximumFileSize = (Integer)map.get("Maximum File Size");
|
||||
showNotifications = (Boolean)map.get("Enable Notifications");
|
||||
enableRecordingServer = (Boolean)map.get("Record Server");
|
||||
enableRecordingSingleplayer = (Boolean)map.get("Record Singleplayer");
|
||||
forceLinearPath = (Boolean)map.get("Force Linear Movement");
|
||||
|
||||
rewriteSettings();
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void rewriteSettings() {
|
||||
ReplayMod.instance.config.load();
|
||||
|
||||
ReplayMod.instance.config.removeCategory(ReplayMod.instance.config.getCategory("settings"));
|
||||
Property recServer = ReplayMod.instance.config.get("settings", "enableRecordingServer", enableRecordingServer, "Defines whether a recording should be started upon joining a server.");
|
||||
Property recSP = ReplayMod.instance.config.get("settings", "enableRecordingSingleplayer", enableRecordingSingleplayer, "Defines whether a recording should be started upon joining a singleplayer world.");
|
||||
Property maxFileSize = ReplayMod.instance.config.get("settings", "maximumFileSize", maximumFileSize, "The maximum File size (in MB) of a recording. 0 means unlimited.");
|
||||
Property showNot = ReplayMod.instance.config.get("settings", "showNotifications", showNotifications, "Defines whether notifications should be sent to the player.");
|
||||
Property linear = ReplayMod.instance.config.get("settings", "forceLinearPath", forceLinearPath, "Defines whether travelling paths should be linear instead of interpolated.");
|
||||
|
||||
ReplayMod.instance.config.save();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package eu.crushedpixel.replaymod.unused;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.network.Packet;
|
||||
|
||||
public class DataReciever {
|
||||
|
||||
private byte[] array;
|
||||
private int timestamp;
|
||||
|
||||
public DataReciever(byte[] array, int timestamp) {
|
||||
this.array = array;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public byte[] getByteArray() {
|
||||
return array;
|
||||
}
|
||||
public void setByteArray(byte[] array) {
|
||||
this.array = array;
|
||||
}
|
||||
public int getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
public void setTimestamp(int timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package eu.crushedpixel.replaymod.unused;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
|
||||
public class DataWriter {
|
||||
|
||||
private boolean active = true;
|
||||
|
||||
private ConcurrentLinkedQueue<DataReciever> queue = new ConcurrentLinkedQueue<DataReciever>();
|
||||
|
||||
Thread outputThread = new Thread(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
HashMap<Class, Integer> counts = new HashMap<Class, Integer>();
|
||||
|
||||
while(active) {
|
||||
DataReciever dataReciever = queue.poll();
|
||||
if(dataReciever != null) {
|
||||
//write the ByteBuf to the given OutputStream
|
||||
|
||||
byte[] array = dataReciever.getByteArray();
|
||||
|
||||
if(array != null) {
|
||||
try {
|
||||
|
||||
stream.writeInt(dataReciever.getTimestamp()); //Timestamp
|
||||
stream.writeInt(array.length); //Lenght
|
||||
|
||||
stream.write(array); //Content
|
||||
stream.flush();
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
try {
|
||||
//let the Thread sleep for 1/2 second and queue up new Packets
|
||||
Thread.sleep(500L);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
stream.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
for(Entry<Class, Integer> entries : counts.entrySet()) {
|
||||
System.out.println(entries.getKey()+ "| "+entries.getValue());
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
public ConcurrentLinkedQueue<DataReciever> getQueue() {
|
||||
return queue;
|
||||
}
|
||||
|
||||
public void setQueue(ConcurrentLinkedQueue<DataReciever> queue) {
|
||||
this.queue = queue;
|
||||
}
|
||||
|
||||
private DataOutputStream stream;
|
||||
|
||||
public DataWriter(DataOutputStream stream) {
|
||||
this.stream = stream;
|
||||
outputThread.start();
|
||||
}
|
||||
|
||||
public void requestFinish() {
|
||||
active = false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package eu.crushedpixel.replaymod.unused;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import net.minecraft.network.EnumConnectionState;
|
||||
import net.minecraft.network.EnumPacketDirection;
|
||||
import net.minecraft.network.NetworkManager;
|
||||
import net.minecraft.network.Packet;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
|
||||
public class PacketListener extends ChannelInboundHandlerAdapter {
|
||||
|
||||
private PacketWriter packetWriter;
|
||||
private long startTime;
|
||||
|
||||
public PacketListener(PacketWriter packetWriter, long startTime) {
|
||||
this.packetWriter = packetWriter;
|
||||
this.startTime = startTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
super.channelRead(ctx, msg);
|
||||
|
||||
if(msg instanceof Packet) {
|
||||
Packet packet = (Packet)msg;
|
||||
int packetID = ((EnumConnectionState)ctx.channel()
|
||||
.attr(NetworkManager.attrKeyConnectionState).get()).getPacketId(EnumPacketDirection.CLIENTBOUND, packet);
|
||||
int timestamp = (int) (System.currentTimeMillis() - startTime);
|
||||
PacketReciever reciever = new PacketReciever(packet, (short)packetID, timestamp);
|
||||
packetWriter.getQueue().add(reciever);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package eu.crushedpixel.replaymod.unused;
|
||||
|
||||
import net.minecraft.network.Packet;
|
||||
|
||||
public class PacketReciever {
|
||||
|
||||
private Packet packet;
|
||||
private short packetID;
|
||||
private int timestamp;
|
||||
|
||||
public PacketReciever(Packet packet, short packetID, int timestamp) {
|
||||
this.packet = packet;
|
||||
this.packetID = packetID;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public Packet getPacket() {
|
||||
return packet;
|
||||
}
|
||||
public void setPacket(Packet packet) {
|
||||
this.packet = packet;
|
||||
}
|
||||
public short getPacketID() {
|
||||
return packetID;
|
||||
}
|
||||
public void setPacketID(short packetID) {
|
||||
this.packetID = packetID;
|
||||
}
|
||||
public int getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
public void setTimestamp(int timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
101
src/main/java/eu/crushedpixel/replaymod/unused/PacketWriter.java
Normal file
101
src/main/java/eu/crushedpixel/replaymod/unused/PacketWriter.java
Normal file
@@ -0,0 +1,101 @@
|
||||
package eu.crushedpixel.replaymod.unused;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
|
||||
public class PacketWriter {
|
||||
|
||||
private boolean active = true;
|
||||
|
||||
private ConcurrentLinkedQueue<PacketReciever> queue = new ConcurrentLinkedQueue<PacketReciever>();
|
||||
|
||||
Thread outputThread = new Thread(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
HashMap<Class, Integer> counts = new HashMap<Class, Integer>();
|
||||
|
||||
while(active) {
|
||||
PacketReciever packetReciever = queue.poll();
|
||||
if(packetReciever != null) {
|
||||
ByteBuf bb = Unpooled.buffer();
|
||||
PacketBuffer packetBuffer = new PacketBuffer(bb);
|
||||
//write the Packet to the given OutputStream
|
||||
|
||||
if(packetReciever.getPacket() != null) {
|
||||
try {
|
||||
packetReciever.getPacket().writePacketData(packetBuffer);
|
||||
|
||||
bb.readerIndex(0);
|
||||
byte[] array = new byte[bb.readableBytes()];
|
||||
bb.readBytes(array);
|
||||
|
||||
stream.writeInt(packetReciever.getTimestamp()); //Timestamp
|
||||
stream.writeInt(array.length); //Lenght
|
||||
|
||||
if(counts.containsKey(packetReciever.getPacket().getClass())) {
|
||||
counts.put(packetReciever.getPacket().getClass(), counts.get(packetReciever.getPacket().getClass())+array.length);
|
||||
} else {
|
||||
counts.put(packetReciever.getPacket().getClass(), array.length);
|
||||
}
|
||||
|
||||
stream.writeShort(packetReciever.getPacketID()); //Packet ID
|
||||
stream.write(array); //Content
|
||||
stream.flush();
|
||||
} catch(Exception e) {
|
||||
System.out.println(packetReciever.getPacket().getClass());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
try {
|
||||
//let the Thread sleep for 1/2 second and queue up new Packets
|
||||
Thread.sleep(500L);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
stream.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
for(Entry<Class, Integer> entries : counts.entrySet()) {
|
||||
System.out.println(entries.getKey()+ "| "+entries.getValue());
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
public ConcurrentLinkedQueue<PacketReciever> getQueue() {
|
||||
return queue;
|
||||
}
|
||||
|
||||
public void setQueue(ConcurrentLinkedQueue<PacketReciever> queue) {
|
||||
this.queue = queue;
|
||||
}
|
||||
|
||||
private DataOutputStream stream;
|
||||
|
||||
public PacketWriter(DataOutputStream stream) {
|
||||
this.stream = stream;
|
||||
outputThread.start();
|
||||
}
|
||||
|
||||
public void requestFinish() {
|
||||
active = false;
|
||||
}
|
||||
|
||||
}
|
||||
BIN
src/main/resources/assets/replaymod/extended_gui.png
Normal file
BIN
src/main/resources/assets/replaymod/extended_gui.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
BIN
src/main/resources/assets/replaymod/replay_gui.png
Normal file
BIN
src/main/resources/assets/replaymod/replay_gui.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
BIN
src/main/resources/assets/replaymod/steve.png
Normal file
BIN
src/main/resources/assets/replaymod/steve.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
BIN
src/main/resources/assets/replaymod/timeline_icons.png
Normal file
BIN
src/main/resources/assets/replaymod/timeline_icons.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
5870
src/main/resources/fields.csv
Normal file
5870
src/main/resources/fields.csv
Normal file
File diff suppressed because it is too large
Load Diff
16
src/main/resources/mcmod.info
Normal file
16
src/main/resources/mcmod.info
Normal file
@@ -0,0 +1,16 @@
|
||||
[
|
||||
{
|
||||
"modid": "replaymod",
|
||||
"name": "Replay Mod",
|
||||
"description": "Example placeholder mod.",
|
||||
"version": "0.0.1",
|
||||
"mcversion": "${mcversion}",
|
||||
"url": "",
|
||||
"updateUrl": "",
|
||||
"authorList": ["CrushedPixel"],
|
||||
"credits": "Johni0702 for helping me out!",
|
||||
"logoFile": "",
|
||||
"screenshots": [],
|
||||
"dependencies": []
|
||||
}
|
||||
]
|
||||
6038
src/main/resources/methods.csv
Normal file
6038
src/main/resources/methods.csv
Normal file
File diff suppressed because it is too large
Load Diff
5124
src/main/resources/params.csv
Normal file
5124
src/main/resources/params.csv
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user