Split mod into core, recording, replay, render[todo], paths[todo] and extras[wip] modules
Move everything to com.replaymod package Add KeyBindingRegistry and SettingsRegistry Recreate settings GUI with new GUI API and dynamically from SettingsRegistry Use ReplayFile from ReplayStudio ReplayHandler is now object oriented Add GuiOverlay, GuiSlider and GuiTexturedButton to GUI API Rewrite both overlays to use new GUI API Fix size capping in vertical and horizontal layout Allow CustomLayouts to have parents Fix tooltip rendering when close to screen border Allow changing of columns in GridLayout
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
package com.replaymod.recording;
|
||||
|
||||
import com.replaymod.recording.handler.ConnectionEventHandler;
|
||||
import com.replaymod.recording.packet.PacketListener;
|
||||
import com.replaymod.core.ReplayMod;
|
||||
import net.minecraftforge.fml.common.FMLCommonHandler;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
|
||||
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
|
||||
import net.minecraftforge.fml.common.eventhandler.EventBus;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
|
||||
@Mod(modid = ReplayModRecording.MOD_ID, useMetadata = true)
|
||||
public class ReplayModRecording {
|
||||
public static final String MOD_ID = "replaymod-recording";
|
||||
|
||||
@Mod.Instance(ReplayMod.MOD_ID)
|
||||
private static ReplayMod core;
|
||||
|
||||
private Logger logger;
|
||||
|
||||
private ConnectionEventHandler connectionEventHandler;
|
||||
|
||||
@Mod.EventHandler
|
||||
public void preInit(FMLPreInitializationEvent event) {
|
||||
logger = event.getModLog();
|
||||
|
||||
core.getSettingsRegistry().register(Setting.class);
|
||||
|
||||
core.getKeyBindingRegistry().registerKeyBinding("replaymod.input.marker", Keyboard.KEY_M, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
PacketListener packetListener = connectionEventHandler.getPacketListener();
|
||||
if (packetListener != null) {
|
||||
packetListener.addMarker();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Mod.EventHandler
|
||||
public void init(FMLInitializationEvent event) {
|
||||
EventBus bus = FMLCommonHandler.instance().bus();
|
||||
bus.register(connectionEventHandler = new ConnectionEventHandler(logger, core));
|
||||
}
|
||||
}
|
||||
17
src/main/java/com/replaymod/recording/Setting.java
Normal file
17
src/main/java/com/replaymod/recording/Setting.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package com.replaymod.recording;
|
||||
|
||||
import com.replaymod.core.SettingsRegistry;
|
||||
|
||||
public final class Setting<T> extends SettingsRegistry.SettingKeys<T> {
|
||||
public static final Setting<Boolean> RECORD_SINGLEPLAYER = make("recordSingleplayer", "recordsingleplayer", true);
|
||||
public static final Setting<Boolean> RECORD_SERVER = make("recordServer", "recordserver", true);
|
||||
public static final Setting<Boolean> INDICATOR = make("indicator", "indicator", true);
|
||||
|
||||
private static <T> Setting<T> make(String key, String displayName, T defaultValue) {
|
||||
return new Setting<>(key, displayName, defaultValue);
|
||||
}
|
||||
|
||||
public Setting(String key, String displayString, T defaultValue) {
|
||||
super("recording", key, "replaymod.gui.settings." + displayString, defaultValue);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.replaymod.recording.gui;
|
||||
|
||||
import com.replaymod.core.ReplayMod;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.gui.Gui;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraftforge.client.event.RenderGameOverlayEvent;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
import static com.replaymod.core.ReplayMod.TEXTURE;
|
||||
import static com.replaymod.core.ReplayMod.TEXTURE_SIZE;
|
||||
|
||||
/**
|
||||
* Renders overlay during recording.
|
||||
*/
|
||||
public class GuiRecordingOverlay {
|
||||
private final Minecraft mc;
|
||||
|
||||
public GuiRecordingOverlay(Minecraft mc) {
|
||||
this.mc = mc;
|
||||
}
|
||||
|
||||
public void register() {
|
||||
MinecraftForge.EVENT_BUS.register(this);
|
||||
}
|
||||
|
||||
public void unregister() {
|
||||
MinecraftForge.EVENT_BUS.unregister(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the recording icon and text in the top left corner of the screen.
|
||||
* @param event Rendered post game overlay
|
||||
*/
|
||||
@SubscribeEvent
|
||||
public void renderRecordingIndicator(RenderGameOverlayEvent.Post event) {
|
||||
if (event.type != RenderGameOverlayEvent.ElementType.ALL) return;
|
||||
if(ReplayMod.replaySettings.showRecordingIndicator()) {
|
||||
FontRenderer fontRenderer = mc.fontRendererObj;
|
||||
fontRenderer.drawString(I18n.format("replaymod.gui.recording").toUpperCase(), 30, 18 - (fontRenderer.FONT_HEIGHT / 2), 0xffffffff);
|
||||
mc.renderEngine.bindTexture(TEXTURE);
|
||||
GlStateManager.resetColor();
|
||||
GlStateManager.enableAlpha();
|
||||
Gui.drawModalRectWithCustomSizedTexture(10, 10, 58, 20, 16, 16, TEXTURE_SIZE, TEXTURE_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
117
src/main/java/com/replaymod/recording/handler/ConnectionEventHandler.java
Executable file
117
src/main/java/com/replaymod/recording/handler/ConnectionEventHandler.java
Executable file
@@ -0,0 +1,117 @@
|
||||
package com.replaymod.recording.handler;
|
||||
|
||||
import com.replaymod.recording.Setting;
|
||||
import com.replaymod.recording.gui.GuiRecordingOverlay;
|
||||
import com.replaymod.recording.packet.PacketListener;
|
||||
import de.johni0702.replaystudio.replay.ReplayFile;
|
||||
import de.johni0702.replaystudio.replay.ZipReplayFile;
|
||||
import de.johni0702.replaystudio.studio.ReplayStudio;
|
||||
import com.replaymod.core.ReplayMod;
|
||||
import eu.crushedpixel.replaymod.chat.ChatMessageHandler.ChatMessageType;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.network.NetworkManager;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.world.WorldType;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.network.FMLNetworkEvent.ClientConnectedToServerEvent;
|
||||
import net.minecraftforge.fml.common.network.FMLNetworkEvent.ClientDisconnectionFromServerEvent;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
|
||||
/**
|
||||
* Handles connection events and initiates recording if enabled.
|
||||
*/
|
||||
public class ConnectionEventHandler {
|
||||
|
||||
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);
|
||||
private static final Minecraft mc = Minecraft.getMinecraft();
|
||||
|
||||
private final Logger logger;
|
||||
private final ReplayMod core;
|
||||
|
||||
private RecordingEventHandler recordingEventHandler;
|
||||
private PacketListener packetListener;
|
||||
private GuiRecordingOverlay guiOverlay;
|
||||
|
||||
public ConnectionEventHandler(Logger logger, ReplayMod core) {
|
||||
this.logger = logger;
|
||||
this.core = core;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onConnectedToServerEvent(ClientConnectedToServerEvent event) {
|
||||
ReplayMod.chatMessageHandler.initialize();
|
||||
|
||||
try {
|
||||
if(event.isLocal) {
|
||||
if (MinecraftServer.getServer().getEntityWorld().getWorldType() == WorldType.DEBUG_WORLD) {
|
||||
logger.info("Debug World recording is not supported.");
|
||||
return;
|
||||
}
|
||||
if(!core.getSettingsRegistry().get(Setting.RECORD_SINGLEPLAYER)) {
|
||||
logger.info("Singleplayer Recording is disabled");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if(!core.getSettingsRegistry().get(Setting.RECORD_SERVER)) {
|
||||
logger.info("Multiplayer Recording is disabled");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
NetworkManager nm = event.manager;
|
||||
String worldName;
|
||||
if(event.isLocal) {
|
||||
worldName = MinecraftServer.getServer().getWorldName();
|
||||
} else {
|
||||
worldName = Minecraft.getMinecraft().getCurrentServerData().serverIP;
|
||||
}
|
||||
Channel channel = nm.channel();
|
||||
ChannelPipeline pipeline = channel.pipeline();
|
||||
|
||||
File folder = core.getReplayFolder();
|
||||
|
||||
String name = sdf.format(Calendar.getInstance().getTime());
|
||||
File currentFile = new File(folder, name + ".mcpr");
|
||||
ReplayFile replayFile = new ZipReplayFile(new ReplayStudio(), currentFile);
|
||||
|
||||
packetListener = new PacketListener(replayFile, name, worldName, System.currentTimeMillis(), event.isLocal);
|
||||
pipeline.addBefore(packetHandlerKey, "replay_recorder", packetListener);
|
||||
|
||||
recordingEventHandler = new RecordingEventHandler(packetListener);
|
||||
recordingEventHandler.register();
|
||||
|
||||
guiOverlay = new GuiRecordingOverlay(mc);
|
||||
guiOverlay.register();
|
||||
|
||||
ReplayMod.chatMessageHandler.addLocalizedChatMessage("replaymod.chat.recordingstarted", ChatMessageType.INFORMATION);
|
||||
} catch(Exception e) {
|
||||
ReplayMod.chatMessageHandler.addLocalizedChatMessage("replaymod.chat.recordingfailed", ChatMessageType.WARNING);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onDisconnectedFromServerEvent(ClientDisconnectionFromServerEvent event) {
|
||||
if (packetListener != null) {
|
||||
guiOverlay.unregister();
|
||||
guiOverlay = null;
|
||||
recordingEventHandler.unregister();
|
||||
recordingEventHandler = null;
|
||||
packetListener = null;
|
||||
|
||||
ReplayMod.chatMessageHandler.stop();
|
||||
}
|
||||
}
|
||||
|
||||
public PacketListener getPacketListener() {
|
||||
return packetListener;
|
||||
}
|
||||
}
|
||||
388
src/main/java/com/replaymod/recording/handler/RecordingEventHandler.java
Executable file
388
src/main/java/com/replaymod/recording/handler/RecordingEventHandler.java
Executable file
@@ -0,0 +1,388 @@
|
||||
package com.replaymod.recording.handler;
|
||||
|
||||
import com.replaymod.recording.packet.PacketListener;
|
||||
import eu.crushedpixel.replaymod.utils.Objects;
|
||||
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.*;
|
||||
import net.minecraft.network.play.server.S14PacketEntity.S17PacketEntityLookMove;
|
||||
import net.minecraft.server.integrated.IntegratedServer;
|
||||
import net.minecraft.util.BlockPos;
|
||||
import net.minecraft.util.MathHelper;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.event.entity.minecart.MinecartInteractEvent;
|
||||
import net.minecraftforge.event.entity.player.PlayerSleepInBedEvent;
|
||||
import net.minecraftforge.event.entity.player.PlayerUseItemEvent;
|
||||
import net.minecraftforge.fml.common.FMLCommonHandler;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.gameevent.PlayerEvent.ItemPickupEvent;
|
||||
import net.minecraftforge.fml.common.gameevent.TickEvent;
|
||||
import net.minecraftforge.fml.common.gameevent.TickEvent.PlayerTickEvent;
|
||||
|
||||
public class RecordingEventHandler {
|
||||
|
||||
private final Minecraft mc = Minecraft.getMinecraft();
|
||||
private final PacketListener packetListener;
|
||||
|
||||
private Double lastX, lastY, lastZ;
|
||||
private ItemStack[] playerItems = new ItemStack[5];
|
||||
private int ticksSinceLastCorrection;
|
||||
private boolean wasSleeping;
|
||||
private int lastRiding = -1;
|
||||
private Integer rotationYawHeadBefore;
|
||||
|
||||
public RecordingEventHandler(PacketListener packetListener) {
|
||||
this.packetListener = packetListener;
|
||||
}
|
||||
|
||||
public void register() {
|
||||
FMLCommonHandler.instance().bus().register(this);
|
||||
MinecraftForge.EVENT_BUS.register(this);
|
||||
((RecordingEventSender) mc.renderGlobal).setRecordingEventHandler(this);
|
||||
}
|
||||
|
||||
public void unregister() {
|
||||
FMLCommonHandler.instance().bus().unregister(this);
|
||||
MinecraftForge.EVENT_BUS.unregister(this);
|
||||
RecordingEventSender recordingEventSender = ((RecordingEventSender) mc.renderGlobal);
|
||||
if (recordingEventSender.getRecordingEventHandler() == this) {
|
||||
recordingEventSender.setRecordingEventHandler(null);
|
||||
}
|
||||
}
|
||||
|
||||
public void onPlayerJoin() {
|
||||
try {
|
||||
packetListener.save(spawnPlayer(mc.thePlayer));
|
||||
} catch(Exception e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void onPlayerRespawn() {
|
||||
try {
|
||||
packetListener.save(spawnPlayer(mc.thePlayer));
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private S0CPacketSpawnPlayer spawnPlayer(EntityPlayer player) {
|
||||
try {
|
||||
S0CPacketSpawnPlayer packet = new S0CPacketSpawnPlayer();
|
||||
|
||||
ByteBuf bb = Unpooled.buffer();
|
||||
PacketBuffer pb = new PacketBuffer(bb);
|
||||
|
||||
pb.writeVarIntToBuffer(player.getEntityId());
|
||||
pb.writeUuid(EntityPlayer.getUUID(player.getGameProfile()));
|
||||
|
||||
pb.writeInt(MathHelper.floor_double(player.posX * 32.0D));
|
||||
pb.writeInt(MathHelper.floor_double(player.posY * 32.0D));
|
||||
pb.writeInt(MathHelper.floor_double(player.posZ * 32.0D));
|
||||
pb.writeByte((byte) ((int) (player.rotationYaw * 256.0F / 360.0F)));
|
||||
pb.writeByte((byte) ((int) (player.rotationPitch * 256.0F / 360.0F)));
|
||||
|
||||
ItemStack itemstack = player.inventory.getCurrentItem();
|
||||
pb.writeShort(itemstack == null ? 0 : Item.getIdFromItem(itemstack.getItem()));
|
||||
|
||||
player.getDataWatcher().writeTo(pb);
|
||||
|
||||
packet.readPacketData(pb);
|
||||
|
||||
return packet;
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onPlayerTick(PlayerTickEvent e) {
|
||||
try {
|
||||
if(e.player != mc.thePlayer) return;
|
||||
|
||||
boolean force = false;
|
||||
if(lastX == null || lastY == null || lastZ == null) {
|
||||
force = true;
|
||||
lastX = e.player.posX;
|
||||
lastY = e.player.posY;
|
||||
lastZ = e.player.posZ;
|
||||
}
|
||||
|
||||
ticksSinceLastCorrection++;
|
||||
if(ticksSinceLastCorrection >= 100) {
|
||||
ticksSinceLastCorrection = 0;
|
||||
force = true;
|
||||
}
|
||||
|
||||
double dx = e.player.posX - lastX;
|
||||
double dy = e.player.posY - lastY;
|
||||
double dz = e.player.posZ - lastZ;
|
||||
|
||||
lastX = e.player.posX;
|
||||
lastY = e.player.posY;
|
||||
lastZ = e.player.posZ;
|
||||
|
||||
Packet packet;
|
||||
if(force || Math.abs(dx) > 4.0 || Math.abs(dy) > 4.0 || Math.abs(dz) > 4.0) {
|
||||
int x = MathHelper.floor_double(e.player.posX * 32.0D);
|
||||
int y = MathHelper.floor_double(e.player.posY * 32.0D);
|
||||
int z = MathHelper.floor_double(e.player.posZ * 32.0D);
|
||||
byte yaw = (byte) ((int) (e.player.rotationYaw * 256.0F / 360.0F));
|
||||
byte pitch = (byte) ((int) (e.player.rotationPitch * 256.0F / 360.0F));
|
||||
packet = new S18PacketEntityTeleport(e.player.getEntityId(), x, y, z, yaw, pitch, e.player.onGround);
|
||||
} else {
|
||||
byte newYaw = (byte) ((int) (e.player.rotationYaw * 256.0F / 360.0F));
|
||||
byte newPitch = (byte) ((int) (e.player.rotationPitch * 256.0F / 360.0F));
|
||||
|
||||
packet = new S17PacketEntityLookMove(e.player.getEntityId(),
|
||||
(byte) Math.round(dx * 32), (byte) Math.round(dy * 32), (byte) Math.round(dz * 32),
|
||||
newYaw, newPitch, e.player.onGround);
|
||||
}
|
||||
|
||||
packetListener.save(packet);
|
||||
|
||||
//HEAD POS
|
||||
int rotationYawHead = ((int)(e.player.rotationYawHead * 256.0F / 360.0F));
|
||||
|
||||
if(!Objects.equals(rotationYawHead, rotationYawHeadBefore)) {
|
||||
S19PacketEntityHeadLook head = new S19PacketEntityHeadLook();
|
||||
ByteBuf bb1 = Unpooled.buffer();
|
||||
PacketBuffer pb1 = new PacketBuffer(bb1);
|
||||
|
||||
pb1.writeVarIntToBuffer(e.player.getEntityId());
|
||||
pb1.writeByte(rotationYawHead);
|
||||
|
||||
head.readPacketData(pb1);
|
||||
|
||||
packetListener.save(head);
|
||||
|
||||
rotationYawHeadBefore = rotationYawHead;
|
||||
}
|
||||
|
||||
S12PacketEntityVelocity vel = new S12PacketEntityVelocity(e.player.getEntityId(), e.player.motionX, e.player.motionY, e.player.motionZ);
|
||||
packetListener.save(vel);
|
||||
|
||||
//Animation Packets
|
||||
//Swing Animation
|
||||
if(e.player.swingProgressInt == 1) {
|
||||
S0BPacketAnimation pac = new S0BPacketAnimation();
|
||||
|
||||
ByteBuf bb = Unpooled.buffer();
|
||||
PacketBuffer pb = new PacketBuffer(bb);
|
||||
|
||||
pb.writeVarIntToBuffer(e.player.getEntityId());
|
||||
pb.writeByte(0);
|
||||
|
||||
pac.readPacketData(pb);
|
||||
|
||||
packetListener.save(pac);
|
||||
}
|
||||
|
||||
/*
|
||||
//Potion Effect Handling
|
||||
List<Integer> found = new ArrayList<Integer>();
|
||||
for(PotionEffect pe : (Collection<PotionEffect>)e.player.getActivePotionEffects()) {
|
||||
found.add(pe.getPotionID());
|
||||
if(lastEffects.contains(found)) continue;
|
||||
S1DPacketEntityEffect pee = new S1DPacketEntityEffect(entityID, pe);
|
||||
packetListener.save(pee);
|
||||
}
|
||||
|
||||
for(int id : lastEffects) {
|
||||
if(!found.contains(id)) {
|
||||
S1EPacketRemoveEntityEffect pre = new S1EPacketRemoveEntityEffect(entityID, new PotionEffect(id, 0));
|
||||
packetListener.save(pre);
|
||||
}
|
||||
}
|
||||
|
||||
lastEffects = found;
|
||||
*/
|
||||
|
||||
//Inventory Handling
|
||||
if(playerItems[0] != mc.thePlayer.getHeldItem()) {
|
||||
playerItems[0] = mc.thePlayer.getHeldItem();
|
||||
S04PacketEntityEquipment pee = new S04PacketEntityEquipment(e.player.getEntityId(), 0, playerItems[0]);
|
||||
packetListener.save(pee);
|
||||
}
|
||||
|
||||
if(playerItems[1] != mc.thePlayer.inventory.armorInventory[0]) {
|
||||
playerItems[1] = mc.thePlayer.inventory.armorInventory[0];
|
||||
S04PacketEntityEquipment pee = new S04PacketEntityEquipment(e.player.getEntityId(), 1, playerItems[1]);
|
||||
packetListener.save(pee);
|
||||
}
|
||||
|
||||
if(playerItems[2] != mc.thePlayer.inventory.armorInventory[1]) {
|
||||
playerItems[2] = mc.thePlayer.inventory.armorInventory[1];
|
||||
S04PacketEntityEquipment pee = new S04PacketEntityEquipment(e.player.getEntityId(), 2, playerItems[2]);
|
||||
packetListener.save(pee);
|
||||
}
|
||||
|
||||
if(playerItems[3] != mc.thePlayer.inventory.armorInventory[2]) {
|
||||
playerItems[3] = mc.thePlayer.inventory.armorInventory[2];
|
||||
S04PacketEntityEquipment pee = new S04PacketEntityEquipment(e.player.getEntityId(), 3, playerItems[3]);
|
||||
packetListener.save(pee);
|
||||
}
|
||||
|
||||
if(playerItems[4] != mc.thePlayer.inventory.armorInventory[3]) {
|
||||
playerItems[4] = mc.thePlayer.inventory.armorInventory[3];
|
||||
S04PacketEntityEquipment pee = new S04PacketEntityEquipment(e.player.getEntityId(), 4, playerItems[4]);
|
||||
packetListener.save(pee);
|
||||
}
|
||||
|
||||
//Leaving Ride
|
||||
|
||||
if((!mc.thePlayer.isRiding() && lastRiding != -1) ||
|
||||
(mc.thePlayer.isRiding() && lastRiding != mc.thePlayer.ridingEntity.getEntityId())) {
|
||||
if(!mc.thePlayer.isRiding()) {
|
||||
lastRiding = -1;
|
||||
} else {
|
||||
lastRiding = mc.thePlayer.ridingEntity.getEntityId();
|
||||
}
|
||||
|
||||
S1BPacketEntityAttach pea = new S1BPacketEntityAttach();
|
||||
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
PacketBuffer pbuf = new PacketBuffer(buf);
|
||||
|
||||
pbuf.writeInt(e.player.getEntityId());
|
||||
pbuf.writeInt(lastRiding);
|
||||
pbuf.writeBoolean(false);
|
||||
|
||||
pea.readPacketData(pbuf);
|
||||
|
||||
packetListener.save(pea);
|
||||
}
|
||||
|
||||
//Sleeping
|
||||
if(!mc.thePlayer.isPlayerSleeping() && wasSleeping) {
|
||||
S0BPacketAnimation pac = new S0BPacketAnimation();
|
||||
|
||||
ByteBuf bb = Unpooled.buffer();
|
||||
PacketBuffer pb = new PacketBuffer(bb);
|
||||
|
||||
pb.writeVarIntToBuffer(e.player.getEntityId());
|
||||
pb.writeByte(2);
|
||||
|
||||
pac.readPacketData(pb);
|
||||
|
||||
packetListener.save(pac);
|
||||
|
||||
wasSleeping = false;
|
||||
}
|
||||
|
||||
} catch(Exception e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onPickupItem(ItemPickupEvent event) {
|
||||
try {
|
||||
packetListener.save(new S0DPacketCollectItem(event.pickedUp.getEntityId(), event.player.getEntityId()));
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onStartEating(PlayerUseItemEvent.Start event) {
|
||||
try {
|
||||
if(!event.entityPlayer.isEating()) return;
|
||||
S0BPacketAnimation packet = new S0BPacketAnimation();
|
||||
|
||||
ByteBuf bb = Unpooled.buffer();
|
||||
PacketBuffer pb = new PacketBuffer(bb);
|
||||
|
||||
pb.writeVarIntToBuffer(event.entityPlayer.getEntityId());
|
||||
pb.writeByte(3);
|
||||
|
||||
packet.readPacketData(pb);
|
||||
|
||||
packetListener.save(packet);
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onSleep(PlayerSleepInBedEvent event) {
|
||||
try {
|
||||
if(event.entityPlayer != mc.thePlayer) {
|
||||
return;
|
||||
}
|
||||
|
||||
S0APacketUseBed pub = new S0APacketUseBed();
|
||||
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
PacketBuffer pbuf = new PacketBuffer(buf);
|
||||
|
||||
pbuf.writeVarIntToBuffer(event.entityPlayer.getEntityId());
|
||||
pbuf.writeBlockPos(event.pos);
|
||||
|
||||
pub.readPacketData(pbuf);
|
||||
|
||||
packetListener.save(pub);
|
||||
|
||||
wasSleeping = true;
|
||||
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void enterMinecart(MinecartInteractEvent event) {
|
||||
try {
|
||||
if(event.player != mc.thePlayer) {
|
||||
return;
|
||||
}
|
||||
|
||||
S1BPacketEntityAttach pea = new S1BPacketEntityAttach();
|
||||
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
PacketBuffer pbuf = new PacketBuffer(buf);
|
||||
|
||||
pbuf.writeInt(event.player.getEntityId());
|
||||
pbuf.writeInt(event.minecart.getEntityId());
|
||||
pbuf.writeBoolean(false);
|
||||
|
||||
pea.readPacketData(pbuf);
|
||||
|
||||
packetListener.save(pea);
|
||||
|
||||
lastRiding = event.minecart.getEntityId();
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void onBlockBreakAnim(int breakerId, BlockPos pos, int progress) {
|
||||
EntityPlayer thePlayer = mc.thePlayer;
|
||||
if (thePlayer != null && breakerId == thePlayer.getEntityId()) {
|
||||
packetListener.save(new S25PacketBlockBreakAnim(breakerId, pos, progress));
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void checkForGamePaused(TickEvent.RenderTickEvent event) {
|
||||
if (event.phase != TickEvent.Phase.START) return;
|
||||
if (mc.isIntegratedServerRunning()) {
|
||||
IntegratedServer server = mc.getIntegratedServer();
|
||||
if (server != null && server.isGamePaused) {
|
||||
packetListener.serverWasPaused = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface RecordingEventSender {
|
||||
void setRecordingEventHandler(RecordingEventHandler recordingEventHandler);
|
||||
RecordingEventHandler getRecordingEventHandler();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.replaymod.recording.mixin;
|
||||
|
||||
import com.replaymod.recording.handler.RecordingEventHandler;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.network.NetHandlerPlayClient;
|
||||
import net.minecraft.network.play.server.S07PacketRespawn;
|
||||
import net.minecraft.network.play.server.S38PacketPlayerListItem;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mixin(NetHandlerPlayClient.class)
|
||||
public abstract class MixinNetHandlerPlayClient {
|
||||
|
||||
@Shadow
|
||||
private Minecraft gameController;
|
||||
|
||||
public RecordingEventHandler getRecordingEventHandler() {
|
||||
return ((RecordingEventHandler.RecordingEventSender) gameController.renderGlobal).getRecordingEventHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
* Record the own player entity joining the world.
|
||||
* We cannot use the {@link net.minecraftforge.event.entity.EntityJoinWorldEvent} because the entity id
|
||||
* of the player is set afterwards and the tablist entry might not yet be sent.
|
||||
* @param packet The packet
|
||||
* @param ci Callback info
|
||||
*/
|
||||
@Inject(method = "handlePlayerListItem", at=@At("RETURN"))
|
||||
public void recordOwnJoin(S38PacketPlayerListItem packet, CallbackInfo ci) {
|
||||
RecordingEventHandler handler = getRecordingEventHandler();
|
||||
if (handler != null && packet.func_179768_b() == S38PacketPlayerListItem.Action.ADD_PLAYER) {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<S38PacketPlayerListItem.AddPlayerData> dataList = packet.func_179767_a();
|
||||
for (S38PacketPlayerListItem.AddPlayerData data : dataList) {
|
||||
if (data.func_179962_a().getId().equals(Minecraft.getMinecraft().thePlayer.getGameProfile().getId())) {
|
||||
handler.onPlayerJoin();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Record the own player entity respawning.
|
||||
* We cannot use the {@link net.minecraftforge.event.entity.EntityJoinWorldEvent} because that would also include
|
||||
* the first spawn which is already handled by {@link #recordOwnJoin(S38PacketPlayerListItem, CallbackInfo)}.
|
||||
* @param packet The packet
|
||||
* @param ci Callback info
|
||||
*/
|
||||
@Inject(method = "handleRespawn", at=@At("RETURN"))
|
||||
public void recordOwnRespawn(S07PacketRespawn packet, CallbackInfo ci) {
|
||||
RecordingEventHandler handler = getRecordingEventHandler();
|
||||
if (handler != null) {
|
||||
handler.onPlayerRespawn();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.replaymod.recording.mixin;
|
||||
|
||||
import com.replaymod.recording.handler.RecordingEventHandler;
|
||||
import net.minecraft.client.renderer.RenderGlobal;
|
||||
import net.minecraft.util.BlockPos;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(RenderGlobal.class)
|
||||
public abstract class MixinRenderGlobal implements RecordingEventHandler.RecordingEventSender {
|
||||
|
||||
private RecordingEventHandler recordingEventHandler;
|
||||
|
||||
@Override
|
||||
public void setRecordingEventHandler(RecordingEventHandler recordingEventHandler) {
|
||||
this.recordingEventHandler = recordingEventHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecordingEventHandler getRecordingEventHandler() {
|
||||
return recordingEventHandler;
|
||||
}
|
||||
|
||||
@Inject(method = "sendBlockBreakProgress", at = @At("HEAD"))
|
||||
public void saveBlockBreakProgressPacket(int breakerId, BlockPos pos, int progress, CallbackInfo info) {
|
||||
if (recordingEventHandler != null) {
|
||||
recordingEventHandler.onBlockBreakAnim(breakerId, pos, progress);
|
||||
}
|
||||
}
|
||||
}
|
||||
182
src/main/java/com/replaymod/recording/packet/DataListener.java
Executable file
182
src/main/java/com/replaymod/recording/packet/DataListener.java
Executable file
@@ -0,0 +1,182 @@
|
||||
package com.replaymod.recording.packet;
|
||||
|
||||
import com.google.common.hash.Hashing;
|
||||
import com.google.common.io.Files;
|
||||
import de.johni0702.replaystudio.data.Marker;
|
||||
import de.johni0702.replaystudio.replay.ReplayFile;
|
||||
import de.johni0702.replaystudio.replay.ReplayMetaData;
|
||||
import com.replaymod.core.ReplayMod;
|
||||
import eu.crushedpixel.replaymod.chat.ChatMessageHandler;
|
||||
import eu.crushedpixel.replaymod.holders.AdvancedPosition;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public abstract class DataListener extends ChannelInboundHandlerAdapter {
|
||||
|
||||
private static final Logger logger = LogManager.getLogger();
|
||||
|
||||
private final ReplayFile replayFile;
|
||||
protected final DataWriter dataWriter;
|
||||
|
||||
protected Long startTime = null;
|
||||
protected String name;
|
||||
protected String worldName;
|
||||
public boolean serverWasPaused;
|
||||
protected long lastSentPacket = 0;
|
||||
protected boolean alive = true;
|
||||
protected Set<String> players = new HashSet<>();
|
||||
private boolean singleplayer;
|
||||
|
||||
private final Set<Marker> markers = new HashSet<>();
|
||||
private final Map<Integer, String> requestToHash = new ConcurrentHashMap<>();
|
||||
|
||||
public DataListener(ReplayFile replayFile, String name, String worldName, long startTime, boolean singleplayer) throws IOException {
|
||||
this.replayFile = replayFile;
|
||||
this.startTime = startTime;
|
||||
this.name = name;
|
||||
this.worldName = worldName;
|
||||
this.singleplayer = singleplayer;
|
||||
|
||||
dataWriter = new DataWriter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) {
|
||||
dataWriter.requestFinish();
|
||||
}
|
||||
|
||||
protected void recordResourcePack(File file, int requestId) {
|
||||
try {
|
||||
String hash = Hashing.sha1().hashBytes(Files.toByteArray(file)).toString();
|
||||
synchronized (dataWriter) {
|
||||
if (!requestToHash.containsValue(hash)) {
|
||||
try (OutputStream out = replayFile.writeResourcePack(hash)) {
|
||||
FileUtils.copyFile(file, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
requestToHash.put(requestId, hash);
|
||||
} catch (IOException e) {
|
||||
logger.warn("Failed to save resource pack.", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void addMarker() {
|
||||
AdvancedPosition pos = new AdvancedPosition(Minecraft.getMinecraft().getRenderViewEntity());
|
||||
int timestamp = (int) (System.currentTimeMillis() - startTime);
|
||||
|
||||
Marker marker = new Marker();
|
||||
marker.setTime(timestamp);
|
||||
marker.setX(pos.getX());
|
||||
marker.setY(pos.getY());
|
||||
marker.setZ(pos.getZ());
|
||||
marker.setYaw((float) pos.getYaw());
|
||||
marker.setPitch((float) pos.getPitch());
|
||||
marker.setRoll((float) pos.getRoll());
|
||||
markers.add(marker);
|
||||
|
||||
ReplayMod.chatMessageHandler.addLocalizedChatMessage("replaymod.chat.addedmarker", ChatMessageHandler.ChatMessageType.INFORMATION);
|
||||
}
|
||||
|
||||
public class DataWriter {
|
||||
private long paused;
|
||||
|
||||
private DataOutputStream stream;
|
||||
private ExecutorService saveService = Executors.newSingleThreadExecutor();
|
||||
|
||||
private final Thread shutdownHook = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!saveService.isShutdown()) {
|
||||
requestFinish();
|
||||
}
|
||||
}
|
||||
}, "shutdown-hook-data-listener");
|
||||
|
||||
public DataWriter() throws IOException {
|
||||
stream = new DataOutputStream(replayFile.writePacketData());
|
||||
Runtime.getRuntime().addShutdownHook(shutdownHook);
|
||||
}
|
||||
|
||||
public synchronized void writePacket(final byte[] bytes) {
|
||||
long now = System.currentTimeMillis();
|
||||
if(startTime == null) {
|
||||
startTime = now;
|
||||
}
|
||||
|
||||
if (serverWasPaused) {
|
||||
paused = now - startTime - lastSentPacket;
|
||||
serverWasPaused = false;
|
||||
}
|
||||
final int timestamp = (int) (now - startTime - paused);
|
||||
lastSentPacket = timestamp;
|
||||
saveService.submit(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
stream.writeInt(timestamp);
|
||||
stream.writeInt(bytes.length);
|
||||
stream.write(bytes);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public synchronized void requestFinish() {
|
||||
if (saveService.isShutdown()) return;
|
||||
try {
|
||||
Runtime.getRuntime().removeShutdownHook(shutdownHook);
|
||||
saveService.shutdown();
|
||||
saveService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
|
||||
stream.close();
|
||||
|
||||
// TODO: Get rid of replay file appender
|
||||
ReplayMod.replayFileAppender.startNewReplayFileWriting();
|
||||
|
||||
String mcversion = ReplayMod.getMinecraftVersion();
|
||||
|
||||
String[] pl = players.toArray(new String[players.size()]);
|
||||
|
||||
String generator = "ReplayMod v" + ReplayMod.getContainer().getVersion();
|
||||
|
||||
ReplayMetaData metaData = new ReplayMetaData();
|
||||
metaData.setSingleplayer(singleplayer);
|
||||
metaData.setServerName(worldName);
|
||||
metaData.setGenerator(generator);
|
||||
metaData.setDuration((int) lastSentPacket);
|
||||
metaData.setDate(startTime);
|
||||
metaData.setPlayers(pl);
|
||||
metaData.setMcVersion(mcversion);
|
||||
|
||||
replayFile.writeMetaData(metaData);
|
||||
replayFile.writeMarkers(markers);
|
||||
replayFile.writeResourcePackIndex(requestToHash);
|
||||
replayFile.save();
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
ReplayMod.replayFileAppender.replayFileWritingFinished();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
298
src/main/java/com/replaymod/recording/packet/PacketListener.java
Executable file
298
src/main/java/com/replaymod/recording/packet/PacketListener.java
Executable file
@@ -0,0 +1,298 @@
|
||||
package com.replaymod.recording.packet;
|
||||
|
||||
import com.google.common.hash.Hashing;
|
||||
import com.google.common.io.Files;
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import de.johni0702.replaystudio.replay.ReplayFile;
|
||||
import eu.crushedpixel.replaymod.replay.Restrictions;
|
||||
import eu.crushedpixel.replaymod.utils.ReplayFileIO;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiScreenWorking;
|
||||
import net.minecraft.client.gui.GuiYesNo;
|
||||
import net.minecraft.client.gui.GuiYesNoCallback;
|
||||
import net.minecraft.client.multiplayer.ServerData;
|
||||
import net.minecraft.client.multiplayer.ServerList;
|
||||
import net.minecraft.client.network.NetHandlerPlayClient;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.client.resources.ResourcePackRepository;
|
||||
import net.minecraft.entity.DataWatcher;
|
||||
import net.minecraft.network.NetworkManager;
|
||||
import net.minecraft.network.Packet;
|
||||
import net.minecraft.network.play.client.C19PacketResourcePackStatus;
|
||||
import net.minecraft.network.play.server.*;
|
||||
import net.minecraft.util.ChatComponentText;
|
||||
import net.minecraft.util.HttpUtil;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class PacketListener extends DataListener {
|
||||
|
||||
private static final Minecraft mc = Minecraft.getMinecraft();
|
||||
private static final Logger logger = LogManager.getLogger();
|
||||
|
||||
private ChannelHandlerContext context = null;
|
||||
|
||||
private int nextRequestId;
|
||||
|
||||
public PacketListener(ReplayFile file, String name, String worldName, long startTime, boolean singleplayer) throws IOException {
|
||||
super(file, name, worldName, startTime, singleplayer);
|
||||
}
|
||||
|
||||
public void save(Packet packet) {
|
||||
try {
|
||||
if(packet instanceof S0CPacketSpawnPlayer) {
|
||||
UUID uuid = ((S0CPacketSpawnPlayer) packet).func_179819_c();
|
||||
players.add(uuid.toString());
|
||||
}
|
||||
|
||||
dataWriter.writePacket(getPacketData(packet));
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@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;
|
||||
|
||||
if(packet instanceof S0DPacketCollectItem) {
|
||||
if(mc.thePlayer != null ||
|
||||
((S0DPacketCollectItem) packet).func_149353_d() == mc.thePlayer.getEntityId()) {
|
||||
super.channelRead(ctx, msg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(packet instanceof S0CPacketSpawnPlayer) {
|
||||
UUID uuid = ((S0CPacketSpawnPlayer) packet).func_179819_c();
|
||||
players.add(uuid.toString());
|
||||
}
|
||||
|
||||
if (packet instanceof S48PacketResourcePackSend) {
|
||||
S48PacketResourcePackSend p = (S48PacketResourcePackSend) packet;
|
||||
int requestId = handleResourcePack(p);
|
||||
save(new S48PacketResourcePackSend("replay://" + requestId, ""));
|
||||
return;
|
||||
}
|
||||
|
||||
dataWriter.writePacket(getPacketData(packet));
|
||||
|
||||
if (packet instanceof S3FPacketCustomPayload) {
|
||||
S3FPacketCustomPayload p = (S3FPacketCustomPayload) packet;
|
||||
if (Restrictions.PLUGIN_CHANNEL.equals(p.getChannelName())) {
|
||||
packet = new S40PacketDisconnect(new ChatComponentText("Please update to view this replay."));
|
||||
dataWriter.writePacket(getPacketData(packet));
|
||||
}
|
||||
}
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
super.channelRead(ctx, msg);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private byte[] getPacketData(Packet packet) {
|
||||
if(packet instanceof S0FPacketSpawnMob) {
|
||||
S0FPacketSpawnMob p = (S0FPacketSpawnMob) packet;
|
||||
if (p.field_149043_l == null) {
|
||||
p.field_149043_l = new DataWatcher(null);
|
||||
if(p.func_149027_c() != null) {
|
||||
for(DataWatcher.WatchableObject wo : (List<DataWatcher.WatchableObject>) p.func_149027_c()) {
|
||||
p.field_149043_l.addObject(wo.getDataValueId(), wo.getObject());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(packet instanceof S0CPacketSpawnPlayer) {
|
||||
S0CPacketSpawnPlayer p = (S0CPacketSpawnPlayer) packet;
|
||||
if (p.field_148960_i == null) {
|
||||
p.field_148960_i = new DataWatcher(null);
|
||||
if(p.func_148944_c() != null) {
|
||||
for(DataWatcher.WatchableObject wo : (List<DataWatcher.WatchableObject>) p.func_148944_c()) {
|
||||
p.field_148960_i.addObject(wo.getDataValueId(), wo.getObject());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ReplayFileIO.serializePacket(packet);
|
||||
}
|
||||
|
||||
public synchronized int handleResourcePack(S48PacketResourcePackSend packet) {
|
||||
final int requestId = nextRequestId++;
|
||||
final NetHandlerPlayClient netHandler = mc.getNetHandler();
|
||||
final NetworkManager netManager = netHandler.getNetworkManager();
|
||||
final String url = packet.func_179783_a();
|
||||
final String hash = packet.func_179784_b();
|
||||
|
||||
if (url.startsWith("level://")) {
|
||||
String levelName = url.substring("level://".length());
|
||||
File savesDir = new File(mc.mcDataDir, "saves");
|
||||
final File levelDir = new File(savesDir, levelName);
|
||||
|
||||
if (levelDir.isFile()) {
|
||||
netManager.sendPacket(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.ACCEPTED));
|
||||
Futures.addCallback(mc.getResourcePackRepository().func_177319_a(levelDir), new FutureCallback() {
|
||||
@Override
|
||||
public void onSuccess(Object result) {
|
||||
recordResourcePack(levelDir, requestId);
|
||||
netManager.sendPacket(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.SUCCESSFULLY_LOADED));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@Nonnull Throwable throwable) {
|
||||
netManager.sendPacket(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.FAILED_DOWNLOAD));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
netManager.sendPacket(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.FAILED_DOWNLOAD));
|
||||
}
|
||||
} else {
|
||||
final ServerData serverData = mc.getCurrentServerData();
|
||||
if (serverData != null && serverData.getResourceMode() == ServerData.ServerResourceMode.ENABLED) {
|
||||
netManager.sendPacket(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.ACCEPTED));
|
||||
downloadResourcePackFuture(requestId, url, hash);
|
||||
} else if (serverData != null && serverData.getResourceMode() != ServerData.ServerResourceMode.PROMPT) {
|
||||
netManager.sendPacket(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.DECLINED));
|
||||
} else {
|
||||
mc.addScheduledTask(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mc.displayGuiScreen(new GuiYesNo(new GuiYesNoCallback() {
|
||||
@Override
|
||||
public void confirmClicked(boolean result, int id) {
|
||||
if (serverData != null) {
|
||||
serverData.setResourceMode(result ? ServerData.ServerResourceMode.ENABLED : ServerData.ServerResourceMode.DISABLED);
|
||||
}
|
||||
if (result) {
|
||||
netManager.sendPacket(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.ACCEPTED));
|
||||
downloadResourcePackFuture(requestId, url, hash);
|
||||
} else {
|
||||
netManager.sendPacket(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.DECLINED));
|
||||
}
|
||||
|
||||
ServerList.func_147414_b(serverData);
|
||||
mc.displayGuiScreen(null);
|
||||
}
|
||||
}, I18n.format("multiplayer.texturePrompt.line1"), I18n.format("multiplayer.texturePrompt.line2"), 0));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return requestId;
|
||||
}
|
||||
|
||||
private void downloadResourcePackFuture(int requestId, String url, final String hash) {
|
||||
Futures.addCallback(downloadResourcePack(requestId, url, hash), new FutureCallback() {
|
||||
@Override
|
||||
public void onSuccess(Object result) {
|
||||
mc.getNetHandler().addToSendQueue(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.SUCCESSFULLY_LOADED));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@Nonnull Throwable throwable) {
|
||||
mc.getNetHandler().addToSendQueue(new C19PacketResourcePackStatus(hash, C19PacketResourcePackStatus.Action.FAILED_DOWNLOAD));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private ListenableFuture downloadResourcePack(final int requestId, String url, String hash) {
|
||||
final ResourcePackRepository repo = mc.mcResourcePackRepository;
|
||||
String fileName;
|
||||
if (hash.matches("^[a-f0-9]{40}$")) {
|
||||
fileName = hash;
|
||||
} else {
|
||||
fileName = url.substring(url.lastIndexOf("/") + 1);
|
||||
|
||||
if (fileName.contains("?")) {
|
||||
fileName = fileName.substring(0, fileName.indexOf("?"));
|
||||
}
|
||||
|
||||
if (!fileName.endsWith(".zip")) {
|
||||
return Futures.immediateFailedFuture(new IllegalArgumentException("Invalid filename; must end in .zip"));
|
||||
}
|
||||
|
||||
fileName = "legacy_" + fileName.replaceAll("\\W", "");
|
||||
}
|
||||
|
||||
final File file = new File(repo.dirServerResourcepacks, fileName);
|
||||
repo.field_177321_h.lock();
|
||||
try {
|
||||
repo.func_148529_f();
|
||||
|
||||
if (file.exists() && hash.length() == 40) {
|
||||
try {
|
||||
String fileHash = Hashing.sha1().hashBytes(Files.toByteArray(file)).toString();
|
||||
if (fileHash.equals(hash)) {
|
||||
recordResourcePack(file, requestId);
|
||||
return repo.func_177319_a(file);
|
||||
}
|
||||
|
||||
logger.warn("File " + file + " had wrong hash (expected " + hash + ", found " + fileHash + "). Deleting it.");
|
||||
FileUtils.deleteQuietly(file);
|
||||
} catch (IOException ioexception) {
|
||||
logger.warn("File " + file + " couldn\'t be hashed. Deleting it.", ioexception);
|
||||
FileUtils.deleteQuietly(file);
|
||||
}
|
||||
}
|
||||
|
||||
final GuiScreenWorking guiScreen = new GuiScreenWorking();
|
||||
final Minecraft mc = Minecraft.getMinecraft();
|
||||
|
||||
Futures.getUnchecked(mc.addScheduledTask(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mc.displayGuiScreen(guiScreen);
|
||||
}
|
||||
}));
|
||||
|
||||
Map sessionInfo = Minecraft.getSessionInfo();
|
||||
repo.field_177322_i = HttpUtil.func_180192_a(file, url, sessionInfo, 50 * 1024 * 1024, guiScreen, mc.getProxy());
|
||||
Futures.addCallback(repo.field_177322_i, new FutureCallback() {
|
||||
@Override
|
||||
public void onSuccess(Object value) {
|
||||
recordResourcePack(file, requestId);
|
||||
repo.func_177319_a(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@Nonnull Throwable throwable) {
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
});
|
||||
return repo.field_177322_i;
|
||||
} finally {
|
||||
repo.field_177321_h.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
38
src/main/java/com/replaymod/recording/packet/PacketSerializer.java
Executable file
38
src/main/java/com/replaymod/recording/packet/PacketSerializer.java
Executable file
@@ -0,0 +1,38 @@
|
||||
package com.replaymod.recording.packet;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import net.minecraft.network.*;
|
||||
import net.minecraft.util.MessageSerializer;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class PacketSerializer extends MessageSerializer {
|
||||
|
||||
public PacketSerializer(EnumPacketDirection direction) {
|
||||
super(direction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(ChannelHandlerContext ctx, Packet packet, ByteBuf byteBuf) throws IOException {
|
||||
EnumConnectionState state = ((EnumConnectionState) ctx.channel().attr(NetworkManager.attrKeyConnectionState).get());
|
||||
encode(state, packet, byteBuf);
|
||||
}
|
||||
|
||||
public void encode(EnumConnectionState state, Packet packet, ByteBuf byteBuf) {
|
||||
Integer integer = state.getPacketId(EnumPacketDirection.CLIENTBOUND, packet);
|
||||
|
||||
if (integer != null) {
|
||||
PacketBuffer packetbuffer = new PacketBuffer(byteBuf);
|
||||
packetbuffer.writeVarIntToBuffer(integer);
|
||||
|
||||
try {
|
||||
packet.writePacketData(packetbuffer);
|
||||
} catch(Throwable throwable) {
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user