Release 2.6.6

This commit is contained in:
Jonas Herzig
2022-06-26 14:49:13 +02:00
64 changed files with 893 additions and 512 deletions

View File

@@ -1,4 +1,5 @@
import com.replaymod.gradle.preprocess.PreprocessTask
import static gg.essential.gradle.util.PrebundleKt.prebundle
buildscript {
def mcVersion
@@ -18,23 +19,23 @@ buildscript {
name = "fabric"
url = "https://maven.fabricmc.net/"
}
if (!fabric) {
maven {
name = "forge"
url = "https://maven.minecraftforge.net"
}
maven {
name = "forge"
url = "https://maven.minecraftforge.net"
}
maven {
name = "sonatype"
url = "https://oss.sonatype.org/content/repositories/snapshots/"
}
maven { url 'https://jitpack.io' }
maven { url "https://maven.architectury.dev" }
maven { url "https://repo.essential.gg/repository/maven-public" }
}
dependencies {
classpath 'gradle.plugin.com.github.jengelman.gradle.plugins:shadow:7.0.0'
if (fabric) {
classpath 'fabric-loom:fabric-loom.gradle.plugin:0.10-SNAPSHOT'
classpath 'fabric-loom:fabric-loom.gradle.plugin:0.11-SNAPSHOT'
} else if (mcVersion >= 11400) {
classpath('net.minecraftforge.gradle:ForgeGradle:5.0.5') { // the FG people still haven't learned to not do breaking changes
exclude group: 'trove', module: 'trove' // preprocessor/idea requires more recent one
@@ -48,6 +49,7 @@ buildscript {
} else {
classpath 'com.github.ReplayMod:ForgeGradle:a8a9e0ca:all' // FG 1.2
}
classpath 'gg.essential:essential-gradle-toolkit:0.1.10'
}
}
@@ -246,6 +248,7 @@ dependencies {
11800: '1.18',
11801: '1.18.1',
11802: '1.18.2',
11900: '1.19',
][mcVersion]
mappings 'net.fabricmc:yarn:' + [
11404: '1.14.4+build.16',
@@ -258,8 +261,9 @@ dependencies {
11800: '1.18+build.1:v2',
11801: '1.18.1+build.1:v2',
11802: '1.18.2+build.1:v2',
11900: '1.19+build.2:v2',
][mcVersion]
modImplementation 'net.fabricmc:fabric-loader:0.12.5'
modImplementation 'net.fabricmc:fabric-loader:0.14.6'
def fabricApiVersion = [
11404: '0.4.3+build.247-1.14',
11502: '0.5.1+build.294-1.15',
@@ -271,6 +275,7 @@ dependencies {
11800: '0.43.1+1.18',
11801: '0.43.1+1.18',
11802: '0.47.9+1.18.2',
11900: '0.55.3+1.19',
][mcVersion]
def fabricApiModules = [
"api-base",
@@ -279,9 +284,11 @@ dependencies {
"resource-loader-v0",
]
if (mcVersion >= 11600) {
fabricApiModules.remove("keybindings-v0")
fabricApiModules.add("key-binding-api-v1")
}
if (mcVersion >= 11700) {
fabricApiModules.remove("networking-v0")
fabricApiModules.add("networking-api-v1")
}
fabricApiModules.each { module ->
@@ -318,13 +325,13 @@ dependencies {
shadow 'com.google.api-client:google-api-client-java6:1.20.0', shadeExclusions
shadow 'com.google.oauth-client:google-oauth-client-jetty:1.20.0'
if (mcVersion >= 11400) { // need lwjgl 3
for (suffix in ['', ':natives-linux', ':natives-windows', ':natives-macos']) {
shadow('org.lwjgl:lwjgl-tinyexr:3.2.2' + suffix) {
exclude group: 'org.lwjgl', module: 'lwjgl' // comes with MC
}
}
def lwjgl = configurations.create("lwjgl")
for (suffix in ['', ':natives-linux', ':natives-windows', ':natives-macos', ':natives-macos-arm64']) {
add(lwjgl.name, 'org.lwjgl:lwjgl:3.3.1' + suffix)
add(lwjgl.name, 'org.lwjgl:lwjgl-tinyexr:3.3.1' + suffix)
}
compileOnly('org.lwjgl:lwjgl-tinyexr:3.3.1')
shadow(prebundle(project, lwjgl, "com/replaymod/render/utils/lwjgl.jar", {}))
if (mcVersion < 11200) {
// The version which MC ships is too old, we'll need to ship our own
@@ -341,7 +348,10 @@ dependencies {
shadow 'com.github.ReplayMod.JavaBlend:2.79.0:a0696f8'
shadow "com.github.ReplayMod:ReplayStudio:6fc8e20", shadeExclusions
shadow "com.github.ReplayMod:ReplayStudio:74d8465", shadeExclusions
// FIXME this should be pulled in by ReplayStudio, and IntelliJ sees it, but javac for some reason does not
implementation 'com.github.viaversion:opennbt:0a02214' // 2.0-SNAPSHOT (ViaVersion Edition)
implementation(FABRIC ? dependencies.project(path: jGui.path, configuration: "namedElements") : jGui) {
transitive = false // FG 1.2 puts all MC deps into the compile configuration and we don't want to shade those
@@ -349,7 +359,9 @@ dependencies {
shadow 'com.github.ReplayMod:lwjgl-utils:27dcd66'
if (FABRIC) {
if (mcVersion >= 11802) {
if (mcVersion >= 11900) {
modCompileOnly 'com.terraformersmc:modmenu:3.1.0' // FIXME update
} else if (mcVersion >= 11802) {
modImplementation 'com.terraformersmc:modmenu:3.1.0'
} else if (mcVersion >= 11800) {
modImplementation 'com.terraformersmc:modmenu:3.0.0'

2
jGui

Submodule jGui updated: 5e41452b0e...c1e43fc9f0

View File

@@ -2,8 +2,8 @@ import groovy.json.JsonOutput
import java.io.ByteArrayOutputStream
plugins {
id("fabric-loom") version "0.10-SNAPSHOT" apply false
id("com.replaymod.preprocess") version "7746c47"
id("fabric-loom") version "0.11-SNAPSHOT" apply false
id("com.replaymod.preprocess") version "48e02ad"
id("com.github.hierynomus.license") version "0.15.0"
}
@@ -189,6 +189,7 @@ val doRelease by tasks.registering {
defaultTasks("bundleJar")
preprocess {
val mc11900 = createNode("1.19", 11900, "yarn")
val mc11802 = createNode("1.18.2", 11802, "yarn")
val mc11801 = createNode("1.18.1", 11801, "yarn")
val mc11701 = createNode("1.17.1", 11701, "yarn")
@@ -209,6 +210,7 @@ preprocess {
val mc10800 = createNode("1.8", 10800, "srg")
val mc10710 = createNode("1.7.10", 10710, "srg")
mc11900.link(mc11802, file("versions/mapping-fabric-1.19-1.18.2.txt"))
mc11802.link(mc11801)
mc11801.link(mc11701, file("versions/mapping-fabric-1.18.1-1.17.1.txt"))
mc11701.link(mc11700)

View File

@@ -33,6 +33,7 @@ val jGuiVersions = listOf(
"1.17.1",
"1.18.1",
"1.18.2",
"1.19",
)
val replayModVersions = listOf(
// "1.7.10",
@@ -54,6 +55,7 @@ val replayModVersions = listOf(
"1.17.1",
"1.18.1",
"1.18.2",
"1.19",
)
rootProject.buildFileName = "root.gradle.kts"

View File

@@ -14,7 +14,11 @@ import net.minecraft.util.crash.CrashException;
//#if FABRIC>=1
import com.replaymod.core.versions.LangResourcePack;
import net.fabricmc.fabric.api.client.keybinding.FabricKeyBinding;
//#if MC>=11600
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
//#else
//$$ import net.fabricmc.fabric.api.client.keybinding.FabricKeyBinding;
//#endif
import net.minecraft.client.util.InputUtil;
import net.minecraft.util.Identifier;
import static com.replaymod.core.ReplayMod.MOD_ID;
@@ -35,8 +39,8 @@ import java.util.function.Supplier;
public class KeyBindingRegistry extends EventRegistrations {
private static final String CATEGORY = "replaymod.title";
//#if FABRIC>=1
static { net.fabricmc.fabric.api.client.keybinding.KeyBindingRegistry.INSTANCE.addCategory(CATEGORY); }
//#if FABRIC>=1 && MC<11600
//$$ static { net.fabricmc.fabric.api.client.keybinding.KeyBindingRegistry.INSTANCE.addCategory(CATEGORY); }
//#endif
private final Map<String, Binding> bindings = new HashMap<>();
@@ -63,9 +67,15 @@ public class KeyBindingRegistry extends EventRegistrations {
keyCode = -1;
}
Identifier id = new Identifier(MOD_ID, name.substring(LangResourcePack.LEGACY_KEY_PREFIX.length()));
FabricKeyBinding fabricKeyBinding = FabricKeyBinding.Builder.create(id, InputUtil.Type.KEYSYM, keyCode, CATEGORY).build();
net.fabricmc.fabric.api.client.keybinding.KeyBindingRegistry.INSTANCE.register(fabricKeyBinding);
KeyBinding keyBinding = fabricKeyBinding;
//#if MC>=11600
String key = String.format("key.%s.%s", id.getNamespace(), id.getPath());
KeyBinding keyBinding = new KeyBinding(key, InputUtil.Type.KEYSYM, keyCode, CATEGORY);
KeyBindingHelper.registerKeyBinding(keyBinding);
//#else
//$$ FabricKeyBinding fabricKeyBinding = FabricKeyBinding.Builder.create(id, InputUtil.Type.KEYSYM, keyCode, CATEGORY).build();
//$$ net.fabricmc.fabric.api.client.keybinding.KeyBindingRegistry.INSTANCE.register(fabricKeyBinding);
//$$ KeyBinding keyBinding = fabricKeyBinding;
//#endif
//#else
//$$ KeyBinding keyBinding = new KeyBinding(name, keyCode, CATEGORY);
//$$ ClientRegistry.registerKeyBinding(keyBinding);

View File

@@ -18,7 +18,6 @@ import com.replaymod.replaystudio.studio.ReplayStudio;
import com.replaymod.replaystudio.util.I18n;
import com.replaymod.simplepathing.ReplayModSimplePathing;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.options.Option;
import net.minecraft.resource.DirectoryResourcePack;
import net.minecraft.text.LiteralText;
import net.minecraft.text.Style;
@@ -37,6 +36,11 @@ import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
//#if MC>=11900
//#else
import net.minecraft.client.options.Option;
//#endif
public class ReplayMod implements Module, Scheduler {
public static final String MOD_ID = "replaymod";
@@ -162,7 +166,8 @@ public class ReplayMod implements Module, Scheduler {
keyBindingRegistry.register();
// 1.7.10 crashes when render distance > 16
//#if MC>=10800
// Post 1.19 this has become non-trivial to do, install Sodium+Bobby or OptiFine if you need it
//#if MC>=10800 && MC<11900
if (!MCVer.hasOptifine()) {
Option.RENDER_DISTANCE.setMax(64f);
}

View File

@@ -0,0 +1 @@
// 1.19+

View File

@@ -158,8 +158,12 @@ public class LangResourcePack extends AbstractFileResourcePack {
String namespace,
//#endif
String path,
//#if MC>=11900
//$$ Predicate<Identifier> filter
//#else
int maxDepth,
Predicate<String> filter
//#endif
) {
if (resourcePackType == ResourceType.CLIENT_RESOURCES && "lang".equals(path)) {
Path base = baseLangPath();
@@ -174,8 +178,13 @@ public class LangResourcePack extends AbstractFileResourcePack {
.map(LANG_FILE_NAME_PATTERN::matcher)
.filter(Matcher::matches)
.map(matcher -> String.format("%s_%s.json", matcher.group(1), matcher.group(1)))
.filter(filter::test)
//#if MC<11900
.filter(filter)
//#endif
.map(name -> new Identifier(ReplayMod.MOD_ID, "lang/" + name))
//#if MC>=11900
//$$ .filter(filter)
//#endif
.collect(Collectors.toList());
} catch (IOException e) {
e.printStackTrace();

View File

@@ -34,6 +34,7 @@ import net.minecraft.client.util.Window;
import java.util.concurrent.CompletableFuture;
//#if MC>=11600
import net.minecraft.text.Text;
import net.minecraft.text.TranslatableText;
//#else
//$$ import net.minecraft.client.resource.language.I18n;
@@ -220,7 +221,7 @@ public class MCVer {
//#if MC>=11400
public static Optional<AbstractButtonWidget> findButton(Iterable<AbstractButtonWidget> buttonList, @SuppressWarnings("unused") String text, @SuppressWarnings("unused") int id) {
//#if MC>=11600
final TranslatableText message = new TranslatableText(text);
final Text message = new TranslatableText(text);
//#else
//$$ final String message = I18n.translate(text);
//#endif

View File

@@ -6,6 +6,7 @@ import com.google.common.util.concurrent.ListenableFuture;
import com.replaymod.core.mixin.MinecraftAccessor;
import com.replaymod.gradle.remap.Pattern;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.options.GameOptions;
import net.minecraft.client.options.KeyBinding;
import net.minecraft.client.render.VertexFormat;
import net.minecraft.client.texture.TextureManager;
@@ -14,6 +15,11 @@ import net.minecraft.client.render.Tessellator;
import net.minecraft.client.render.entity.EntityRenderDispatcher;
import net.minecraft.client.sound.PositionedSoundInstance;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.resource.Resource;
import net.minecraft.resource.ResourceManager;
import net.minecraft.text.LiteralText;
import net.minecraft.text.Text;
import net.minecraft.text.TranslatableText;
import net.minecraft.util.crash.CrashException;
import net.minecraft.util.crash.CrashReport;
import net.minecraft.util.crash.CrashReportSection;
@@ -61,6 +67,7 @@ import net.minecraft.client.render.BufferBuilder;
//$$ import net.minecraft.entity.EntityLivingBase;
//#endif
import java.io.IOException;
import java.util.Collection;
import java.util.List;
@@ -549,7 +556,9 @@ class Patterns {
@Pattern
private static void setCrashReport(MinecraftClient mc, CrashReport report) {
//#if MC>=11800
//#if MC>=11900
//$$ mc.setCrashReportSupplier(report);
//#elseif MC>=11800
//$$ mc.setCrashReportSupplier(() -> report);
//#else
mc.setCrashReport(report);
@@ -573,4 +582,91 @@ class Patterns {
//$$ return com.replaymod.core.versions.MCVer.getTrackedPosition(entity);
//#endif
}
@Pattern
private static Text newTextLiteral(String str) {
//#if MC>=11900
//$$ return net.minecraft.text.Text.literal(str);
//#else
return new LiteralText(str);
//#endif
}
@Pattern
private static Text newTextTranslatable(String key, Object...args) {
//#if MC>=11900
//$$ return net.minecraft.text.Text.translatable(key, args);
//#else
return new TranslatableText(key, args);
//#endif
}
//#if MC>=11500
@Pattern
private static Vec3d getTrackedPos(Entity entity) {
//#if MC>=11900
//$$ return entity.getTrackedPosition().withDelta(0, 0, 0);
//#else
return entity.getTrackedPosition();
//#endif
}
//#else
//$$ @Pattern private static void getTrackedPos() {}
//#endif
@Pattern
private static void setGamma(GameOptions options, double value) {
//#if MC>=11900
//$$ ((com.replaymod.core.mixin.SimpleOptionAccessor<Double>) (Object) options.getGamma()).setRawValue(value);
//#elseif MC>=11400
options.gamma = value;
//#else
//$$ options.gammaSetting = (float) value;
//#endif
}
@Pattern
private static double getGamma(GameOptions options) {
//#if MC>=11900
//$$ return options.getGamma().getValue();
//#else
return options.gamma;
//#endif
}
@Pattern
private static int getViewDistance(GameOptions options) {
//#if MC>=11900
//$$ return options.getViewDistance().getValue();
//#else
return options.viewDistance;
//#endif
}
@Pattern
private static double getFov(GameOptions options) {
//#if MC>=11900
//$$ return options.getFov().getValue();
//#else
return options.fov;
//#endif
}
@Pattern
private static int getGuiScale(GameOptions options) {
//#if MC>=11900
//$$ return options.getGuiScale().getValue();
//#else
return options.guiScale;
//#endif
}
@Pattern
private static Resource getResource(ResourceManager manager, Identifier id) throws IOException {
//#if MC>=11900
//$$ return manager.getResourceOrThrow(id);
//#else
return manager.getResource(id);
//#endif
}
}

View File

@@ -124,7 +124,7 @@ public class MarkerProcessor {
PacketTypeRegistry registry = MCVer.getPacketTypeRegistry(true);
DimensionTracker dimensionTracker = new DimensionTracker();
SquashFilter squashFilter = new SquashFilter(null, null);
SquashFilter squashFilter = new SquashFilter(null, null, null);
List<Pair<Path, ReplayMetaData>> outputPaths = new ArrayList<>();

View File

@@ -72,7 +72,7 @@ public class FullBrightness extends EventRegistrations implements Extra {
Type type = getType();
if (type == Type.Gamma || type == Type.Both) {
originalGamma = mc.options.gamma;
mc.options.gamma = 1000;
mc.options.gamma = 1000.0;
}
if (type == Type.NightVision || type == Type.Both) {
if (mc.player != null) {

View File

@@ -63,4 +63,9 @@ public class ScreenshotWriter implements FrameConsumer<BitmapFrame> {
public void close() throws IOException {
}
@Override
public boolean isParallelCapable() {
return false;
}
}

View File

@@ -116,7 +116,7 @@ public class PlayerOverviewGui extends GuiScreen implements Closeable {
}.setSize(16, 16),
new GuiLabel().setText(
//#if MC>=11400
p.getName().asString()
p.getName().getString()
//#else
//#if MC>=10800
//$$ p.getName()
@@ -181,7 +181,7 @@ public class PlayerOverviewGui extends GuiScreen implements Closeable {
if (isSpectator(o1) && !isSpectator(o2)) return 1;
if (isSpectator(o2) && !isSpectator(o1)) return -1;
//#if MC>=11400
return o1.getName().asString().compareToIgnoreCase(o2.getName().asString());
return o1.getName().getString().compareToIgnoreCase(o2.getName().getString());
//#else
//#if MC>=10800
//$$ return o1.getName().compareToIgnoreCase(o2.getName());

View File

@@ -16,7 +16,11 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
//#if FABRIC>=1
//#if MC>=11700
//$$ import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
//#else
import net.fabricmc.fabric.api.network.ClientSidePacketRegistry;
//#endif
//#else
//$$ import net.minecraftforge.fml.network.NetworkRegistry;
//#endif
@@ -68,7 +72,11 @@ public class ReplayModRecording implements Module {
new GuiHandler(core).register();
//#if FABRIC>=1
//#if MC>=11700
//$$ ClientPlayNetworking.registerGlobalReceiver(Restrictions.PLUGIN_CHANNEL, (client, handler, buf, resp) -> {});
//#else
ClientSidePacketRegistry.INSTANCE.register(Restrictions.PLUGIN_CHANNEL, (packetContext, packetByteBuf) -> {});
//#endif
//#else
//#if MC>=11400
//$$ NetworkRegistry.newEventChannel(Restrictions.PLUGIN_CHANNEL, () -> "0", any -> true, any -> true);

View File

@@ -3,7 +3,6 @@ package com.replaymod.recording.handler;
import com.replaymod.core.ReplayMod;
import com.replaymod.core.utils.ModCompat;
import com.replaymod.core.utils.Utils;
import com.replaymod.core.versions.MCVer;
import com.replaymod.editor.gui.MarkerProcessor;
import com.replaymod.recording.ServerInfoExt;
import com.replaymod.recording.Setting;
@@ -43,7 +42,6 @@ import static com.replaymod.core.versions.MCVer.getMinecraft;
*/
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 MinecraftClient mc = getMinecraft();
@@ -141,7 +139,13 @@ public class ConnectionEventHandler {
metaData.setMcVersion(ReplayMod.instance.getMinecraftVersion());
packetListener = new PacketListener(core, outputPath, replayFile, metaData);
Channel channel = ((NetworkManagerAccessor) networkManager).getChannel();
channel.pipeline().addBefore(packetHandlerKey, "replay_recorder", packetListener);
if (channel.pipeline().get(PacketListener.DECODER_KEY) != null) {
// Regular channel, we'll inject our recorder directly before the decoder
channel.pipeline().addBefore(PacketListener.DECODER_KEY, PacketListener.RAW_RECORDER_KEY, packetListener);
} else {
// Integrated server passes packets directly, there's no splitting, decompression or decoding
channel.pipeline().addFirst(PacketListener.RAW_RECORDER_KEY, packetListener);
}
recordingEventHandler = new RecordingEventHandler(packetListener);
recordingEventHandler.register();

View File

@@ -42,12 +42,9 @@ import java.util.Collections;
//#if MC>=10904
import net.minecraft.network.packet.s2c.play.EntityTrackerUpdateS2CPacket;
import net.minecraft.network.packet.s2c.play.PlaySoundS2CPacket;
import net.minecraft.network.packet.s2c.play.WorldEventS2CPacket;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.util.Hand;
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvent;
//#endif
//#if MC>=10800
@@ -91,7 +88,7 @@ public class RecordingEventHandler extends EventRegistrations {
}
}
//#if MC>=11400
//#if MC>=10904
public void onPacket(Packet<?> packet) {
packetListener.save(packet);
}
@@ -112,16 +109,6 @@ public class RecordingEventHandler extends EventRegistrations {
}
//#if MC>=10904
public void onClientSound(SoundEvent sound, SoundCategory category,
double x, double y, double z, float volume, float pitch) {
try {
// Send to all other players in ServerWorldEventHandler#playSoundToAllNearExcept
packetListener.save(new PlaySoundS2CPacket(sound, category, x, y, z, volume, pitch));
} catch(Exception e) {
e.printStackTrace();
}
}
public void onClientEffect(int type, BlockPos pos, int data) {
try {
// Send to all other players in ServerWorldEventHandler#playEvent
@@ -334,99 +321,6 @@ public class RecordingEventHandler extends EventRegistrations {
}
}
//#if FABRIC>=1
// FIXME fabric
//#else
//$$ @SubscribeEvent
//$$ public void onPickupItem(ItemPickupEvent event) {
//$$ try {
//#if MC>=11100
//#if MC>=11200
//#if MC>=11400
//$$ ItemStack stack = event.getStack();
//$$ packetListener.save(new SCollectItemPacket(
//$$ event.getOriginalEntity().getEntityId(),
//$$ event.getPlayer().getEntityId(),
//$$ event.getStack().getCount()
//$$ ));
//#else
//$$ packetListener.save(new SPacketCollectItem(event.pickedUp.getEntityId(), event.player.getEntityId(),
//$$ event.pickedUp.getItem().getMaxStackSize()));
//#endif
//#else
//$$ packetListener.save(new SPacketCollectItem(event.pickedUp.getEntityId(), event.player.getEntityId(),
//$$ event.pickedUp.getEntityItem().getMaxStackSize()));
//#endif
//#else
//$$ packetListener.save(new SPacketCollectItem(event.pickedUp.getEntityId(), event.player.getEntityId()));
//#endif
//$$ } catch(Exception e) {
//$$ e.printStackTrace();
//$$ }
//$$ }
//#endif
//#if MC>=11400
// FIXME fabric
//#else
//$$ @SubscribeEvent
//$$ public void onSleep(PlayerSleepInBedEvent event) {
//$$ try {
//#if MC>=10904
//$$ if (event.getEntityPlayer() != mc.player) {
//$$ return;
//$$ }
//$$
//$$ packetListener.save(new SPacketUseBed(event.getEntityPlayer(), event.getPos()));
//#else
//$$ if (event.entityPlayer != mc.thePlayer) {
//$$ return;
//$$ }
//$$
//$$ packetListener.save(new S0APacketUseBed(event.entityPlayer,
//#if MC>=10800
//$$ event.pos
//#else
//$$ event.x, event.y, event.z
//#endif
//$$ ));
//#endif
//$$
//$$ wasSleeping = true;
//$$
//$$ } catch(Exception e) {
//$$ e.printStackTrace();
//$$ }
//$$ }
//#endif
/* FIXME event not (yet?) on 1.13
@SubscribeEvent
public void enterMinecart(MinecartInteractEvent event) {
try {
//#if MC>=10904
if(event.getEntity() != mc.player) {
return;
}
packetListener.save(new SPacketEntityAttach(event.getPlayer(), event.getMinecart()));
lastRiding = event.getMinecart().getEntityId();
//#else
//$$ if(event.entity != mc.thePlayer) {
//$$ return;
//$$ }
//$$
//$$ packetListener.save(new S1BPacketEntityAttach(0, event.player, event.minecart));
//$$
//$$ lastRiding = event.minecart.getEntityId();
//#endif
} catch(Exception e) {
e.printStackTrace();
}
}
*/
//#if MC>=10800
public void onBlockBreakAnim(int breakerId, BlockPos pos, int progress) {
//#else

View File

@@ -0,0 +1,39 @@
package com.replaymod.recording.mixin;
import com.replaymod.recording.packet.PacketListener;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import net.minecraft.network.ClientConnection;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Map;
@Mixin(ClientConnection.class)
public abstract class MixinClientConnection {
@Shadow
private Channel channel;
@Inject(method = "setCompressionThreshold", at = @At("RETURN"))
private void ensureReplayModRecorderIsAfterDecompress(CallbackInfo ci) {
ChannelHandler recorder = null;
for (Map.Entry<String, ChannelHandler> entry : channel.pipeline()) {
String key = entry.getKey();
if (PacketListener.RAW_RECORDER_KEY.equals(key)) {
recorder = entry.getValue();
}
if (PacketListener.DECOMPRESS_KEY.equals(key)) {
if (recorder != null) {
// If we've already found the recorder, then that means decompress is after recorder. That's no good
// because it means the recorder is getting compressed packets, we need to move the recorder.
channel.pipeline().remove(recorder);
channel.pipeline().addBefore(PacketListener.DECODER_KEY, PacketListener.RAW_RECORDER_KEY, recorder);
return;
}
}
}
}
}

View File

@@ -33,7 +33,11 @@ public abstract class MixinDownloadingPackFinder implements ResourcePackRecorder
}
//#if MC>=10800
//#if MC>=11900
//$$ @Inject(method = "loadServerPack(Ljava/io/File;Lnet/minecraft/resource/ResourcePackSource;)Ljava/util/concurrent/CompletableFuture;", at = @At("HEAD"))
//#else
@Inject(method = "loadServerPack", at = @At("HEAD"))
//#endif
private void recordDownloadedPack(
File file,
//#if MC>=11600

View File

@@ -5,6 +5,7 @@ import com.replaymod.recording.handler.RecordingEventHandler;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.network.packet.s2c.play.PlaySoundS2CPacket;
import net.minecraft.util.profiler.Profiler;
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvent;
@@ -61,12 +62,20 @@ public abstract class MixinWorldClient extends World implements RecordingEventHa
//#else
DimensionType dimensionType,
//#endif
Supplier<Profiler> profiler, boolean bl, boolean bl2, long l) {
Supplier<Profiler> profiler, boolean bl, boolean bl2, long l
//#if MC>=11900
//$$ , int maxChainedNeighborUpdates
//#endif
) {
super(mutableWorldProperties, registryKey,
//#if MC<11602
//$$ registryKey2,
//#endif
dimensionType, profiler, bl, bl2, l);
dimensionType, profiler, bl, bl2, l
//#if MC>=11900
//$$ , maxChainedNeighborUpdates
//#endif
);
}
//#else
//#if MC>=11400
@@ -102,7 +111,10 @@ public abstract class MixinWorldClient extends World implements RecordingEventHa
// but are instead played directly by the client. The server only sends these sounds to
// other clients so we have to record them manually.
// E.g. Block place sounds
//#if MC>=11400
//#if MC>=11900
//$$ @Inject(method = "playSound(Lnet/minecraft/entity/player/PlayerEntity;DDDLnet/minecraft/sound/SoundEvent;Lnet/minecraft/sound/SoundCategory;FFJ)V",
//$$ at = @At("HEAD"))
//#elseif MC>=11400
//#if FABRIC>=1
@Inject(method = "playSound(Lnet/minecraft/entity/player/PlayerEntity;DDDLnet/minecraft/sound/SoundEvent;Lnet/minecraft/sound/SoundCategory;FF)V",
at = @At("HEAD"))
@@ -114,12 +126,23 @@ public abstract class MixinWorldClient extends World implements RecordingEventHa
//$$ @Inject(method = "playSound(Lnet/minecraft/entity/player/EntityPlayer;DDDLnet/minecraft/util/SoundEvent;Lnet/minecraft/util/SoundCategory;FF)V",
//$$ at = @At("HEAD"))
//#endif
public void replayModRecording_recordClientSound(PlayerEntity player, double x, double y, double z, SoundEvent sound, SoundCategory category,
float volume, float pitch, CallbackInfo ci) {
public void replayModRecording_recordClientSound(
PlayerEntity player, double x, double y, double z, SoundEvent sound, SoundCategory category,
float volume, float pitch,
//#if MC>=11900
//$$ long seed,
//#endif
CallbackInfo ci) {
if (player == this.client.player) {
RecordingEventHandler handler = replayModRecording_getRecordingEventHandler();
if (handler != null) {
handler.onClientSound(sound, category, x, y, z, volume, pitch);
// Sent to all other players in ServerWorldEventHandler#playSoundToAllNearExcept
handler.onPacket(new PlaySoundS2CPacket(
sound, category, x, y, z, volume, pitch
//#if MC>=11900
//$$ , seed
//#endif
));
}
}
}

View File

@@ -1,16 +1,16 @@
package com.replaymod.recording.mixin;
import net.minecraft.entity.data.DataTracker;
import net.minecraft.network.packet.s2c.play.MobSpawnS2CPacket;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(MobSpawnS2CPacket.class)
public interface SPacketSpawnMobAccessor {
//#if MC<11500
//$$ @Accessor("dataTracker")
//$$ DataTracker getDataManager();
//$$ @Accessor("dataTracker")
//$$ void setDataManager(DataTracker value);
//#endif
}
//#if MC<11500
//$$ package com.replaymod.recording.mixin;
//$$
//$$ import net.minecraft.entity.data.DataTracker;
//$$ import net.minecraft.network.packet.s2c.play.MobSpawnS2CPacket;
//$$ import org.spongepowered.asm.mixin.Mixin;
//$$ import org.spongepowered.asm.mixin.gen.Accessor;
//$$
//$$ @Mixin(MobSpawnS2CPacket.class)
//$$ public interface SPacketSpawnMobAccessor {
//$$ @Accessor("dataTracker")
//$$ DataTracker getDataManager();
//$$ @Accessor("dataTracker")
//$$ void setDataManager(DataTracker value);
//$$ }
//#endif

View File

@@ -1,16 +1,16 @@
package com.replaymod.recording.mixin;
import net.minecraft.entity.data.DataTracker;
import net.minecraft.network.packet.s2c.play.PlayerSpawnS2CPacket;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(PlayerSpawnS2CPacket.class)
public interface SPacketSpawnPlayerAccessor {
//#if MC<11500
//$$ @Accessor("dataTracker")
//$$ DataTracker getDataManager();
//$$ @Accessor("dataTracker")
//$$ void setDataManager(DataTracker value);
//#endif
}
//#if MC<11500
//$$ package com.replaymod.recording.mixin;
//$$
//$$ import net.minecraft.entity.data.DataTracker;
//$$ import net.minecraft.network.packet.s2c.play.PlayerSpawnS2CPacket;
//$$ import org.spongepowered.asm.mixin.Mixin;
//$$ import org.spongepowered.asm.mixin.gen.Accessor;
//$$
//$$ @Mixin(PlayerSpawnS2CPacket.class)
//$$ public interface SPacketSpawnPlayerAccessor {
//$$ @Accessor("dataTracker")
//$$ DataTracker getDataManager();
//$$ @Accessor("dataTracker")
//$$ void setDataManager(DataTracker value);
//$$ }
//#endif

View File

@@ -2,7 +2,6 @@ package com.replaymod.recording.packet;
import com.github.steveice10.netty.buffer.PooledByteBufAllocator;
import com.github.steveice10.packetlib.tcp.io.ByteBufNetOutput;
import com.google.common.collect.Lists;
import com.google.gson.Gson;
import com.replaymod.core.ReplayMod;
import com.replaymod.core.utils.Restrictions;
@@ -13,29 +12,27 @@ import com.replaymod.recording.ReplayModRecording;
import com.replaymod.recording.Setting;
import com.replaymod.recording.gui.GuiSavingReplay;
import com.replaymod.recording.handler.ConnectionEventHandler;
import com.replaymod.recording.mixin.SPacketSpawnMobAccessor;
import com.replaymod.recording.mixin.SPacketSpawnPlayerAccessor;
import com.replaymod.replaystudio.PacketData;
import com.replaymod.replaystudio.data.Marker;
import com.replaymod.replaystudio.io.ReplayOutputStream;
import com.replaymod.replaystudio.lib.viaversion.api.protocol.packet.State;
import com.replaymod.replaystudio.protocol.Packet;
import com.replaymod.replaystudio.replay.ReplayFile;
import com.replaymod.replaystudio.replay.ReplayMetaData;
import de.johni0702.minecraft.gui.container.VanillaGuiScreen;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.AttributeKey;
import net.minecraft.client.MinecraftClient;
import net.minecraft.network.ClientConnection;
import net.minecraft.network.packet.s2c.play.CustomPayloadS2CPacket;
import net.minecraft.network.packet.s2c.play.DisconnectS2CPacket;
import net.minecraft.network.packet.s2c.play.ItemPickupAnimationS2CPacket;
import net.minecraft.network.packet.s2c.play.MobSpawnS2CPacket;
import net.minecraft.network.packet.s2c.play.PlayerSpawnS2CPacket;
import net.minecraft.entity.Entity;
import net.minecraft.entity.data.DataTracker;
import net.minecraft.network.NetworkState;
import net.minecraft.network.Packet;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.text.LiteralText;
import net.minecraft.util.crash.CrashReport;
@@ -44,17 +41,6 @@ import org.apache.commons.lang3.tuple.Pair;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
//#if MC>=11400
import net.minecraft.network.packet.s2c.login.LoginSuccessS2CPacket;
//#else
//$$ import net.minecraftforge.fml.common.network.internal.FMLProxyPacket;
//#endif
//#if MC>=10904
//#else
//$$ import java.util.List;
//#endif
//#if MC>=10800
//#if MC<10904
//$$ import net.minecraft.network.play.server.S46PacketSetCompressionLevel;
@@ -64,7 +50,6 @@ import net.minecraft.network.packet.s2c.play.ResourcePackSendS2CPacket;
import net.minecraft.network.NetworkSide;
//#endif
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
@@ -82,12 +67,31 @@ import java.util.concurrent.atomic.AtomicInteger;
import static com.replaymod.core.versions.MCVer.*;
import static com.replaymod.replaystudio.util.Utils.writeInt;
import static java.util.Objects.requireNonNull;
@ChannelHandler.Sharable // so we can re-order it
public class PacketListener extends ChannelInboundHandlerAdapter {
public static final String RAW_RECORDER_KEY = "replay_recorder_raw";
public static final String DECODED_RECORDER_KEY = "replay_recorder_decoded";
public static final String DECOMPRESS_KEY = "decompress";
public static final String DECODER_KEY = "decoder";
private static final MinecraftClient mc = getMinecraft();
private static final Logger logger = LogManager.getLogger();
//#if MC>=11700
//$$ private static final int PACKET_ID_RESOURCE_PACK_SEND = getPacketId(NetworkState.PLAY, new ResourcePackSendS2CPacket("", "", false, null));
//$$ private static final int PACKET_ID_LOGIN_COMPRESSION = getPacketId(NetworkState.LOGIN, new LoginCompressionS2CPacket(0));
//#else
private static final int PACKET_ID_RESOURCE_PACK_SEND = getPacketId(NetworkState.PLAY, new ResourcePackSendS2CPacket());
private static final int PACKET_ID_LOGIN_COMPRESSION = getPacketId(NetworkState.LOGIN, new LoginCompressionS2CPacket());
//#endif
//#if MC<10904
//$$ private static final int PACKET_ID_PLAY_COMPRESSION = getPacketId(EnumConnectionState.PLAY, new S46PacketSetCompressionLevel());
//#endif
private final ReplayMod core;
private final Path outputPath;
private final ReplayFile replayFile;
@@ -105,13 +109,6 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
private long lastSentPacket;
private long timePassedWhilePaused;
private volatile boolean serverWasPaused;
//#if MC>=11400
private NetworkState connectionState = NetworkState.LOGIN;
private boolean loginPhase = true;
//#else
//$$ private EnumConnectionState connectionState = EnumConnectionState.PLAY;
//$$ private boolean loginPhase = false;
//#endif
/**
* Used to keep track of the last metadata save job submitted to the save service and
@@ -159,6 +156,17 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
});
}
public void save(net.minecraft.network.Packet packet) {
Packet encoded;
try {
encoded = encodeMcPacket(getConnectionState(), packet);
} catch (Exception e) {
logger.error("Encoding packet:", e);
return;
}
save(encoded);
}
public void save(Packet packet) {
// If we're not on the main thread (i.e. we're on the netty thread), then we need to schedule the saving
// to happen on the main thread so we can guarantee correct ordering of inbound and inject packets.
@@ -169,24 +177,12 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
return;
}
try {
if(packet instanceof PlayerSpawnS2CPacket) {
//#if MC>=10800
UUID uuid = ((PlayerSpawnS2CPacket) packet).getPlayerUuid();
//#else
//$$ UUID uuid = ((S0CPacketSpawnPlayer) packet).func_148948_e().getId();
//#endif
Set<String> uuids = new HashSet<>(Arrays.asList(metaData.getPlayers()));
uuids.add(uuid.toString());
metaData.setPlayers(uuids.toArray(new String[uuids.size()]));
saveMetaData();
}
//#if MC>=10800
if (packet instanceof LoginCompressionS2CPacket) {
//#if MC>=11800
if (packet.getRegistry().getState() == State.LOGIN && packet.getId() == PACKET_ID_LOGIN_COMPRESSION) {
return; // Replay data is never compressed on the packet level
}
//#if MC<10904
//$$ if (packet instanceof S46PacketSetCompressionLevel) {
//$$ if (packet.getRegistry().getState() == State.PLAY && packet.getId() == PACKET_ID_PLAY_COMPRESSION) {
//$$ return; // Replay data is never compressed on the packet level
//$$ }
//#endif
@@ -199,7 +195,7 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
}
int timestamp = (int) (now - startTime - timePassedWhilePaused);
lastSentPacket = timestamp;
PacketData packetData = getPacketData(timestamp, packet);
PacketData packetData = new PacketData(timestamp, packet);
saveService.submit(() -> {
try {
if (ReplayMod.isMinimalMode()) {
@@ -226,18 +222,27 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
throw new RuntimeException(e);
}
});
//#if MC>=11400
if (packet instanceof LoginSuccessS2CPacket) {
connectionState = NetworkState.PLAY;
loginPhase = false;
}
//#endif
} catch(Exception e) {
logger.error("Writing packet:", e);
}
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
super.handlerAdded(ctx);
if (ctx.pipeline().get(DECODED_RECORDER_KEY) == null) {
if (ctx.pipeline().get(PacketListener.DECODER_KEY) != null) {
// Regular channel, we'll inject our decoded recorder directly after the decoder
ctx.pipeline().addAfter(DECODER_KEY, DECODED_RECORDER_KEY, new DecodedPacketListener());
} else {
// Integrated server passes packets directly, there's no splitting, decompression or decoding
// The decoded packet handler can just go directly behind this hand
ctx.pipeline().addAfter(RAW_RECORDER_KEY, DECODED_RECORDER_KEY, new DecodedPacketListener());
}
}
}
@Override
public void channelInactive(ChannelHandlerContext ctx) {
metaData.setDuration((int) lastSentPacket);
@@ -324,139 +329,44 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
}
this.context = ctx;
if (msg instanceof Packet) {
try {
Packet packet = (Packet) msg;
NetworkState connectionState = getConnectionState();
//#if MC>=10904
if(packet instanceof ItemPickupAnimationS2CPacket) {
if(mc.player != null ||
((ItemPickupAnimationS2CPacket) packet).getEntityId() == mc.player.getEntityId()) {
//#else
//$$ if(packet instanceof S0DPacketCollectItem) {
//$$ if(mc.thePlayer != null || ((S0DPacketCollectItem) packet).getEntityID() == mc.thePlayer.getEntityId()) {
//#endif
super.channelRead(ctx, msg);
return;
}
}
Packet packet = null;
if (msg instanceof ByteBuf) {
// for regular connections, we're expecting to observe `ByteBuf`s here
ByteBuf buf = (ByteBuf) msg;
if (buf.readableBytes() > 0) {
packet = decodePacket(connectionState, buf);
}
} else if (msg instanceof net.minecraft.network.Packet) {
// for integrated server connections MC is passing the packet objects directly, so we need to encode them
// ourselves to be able to store them
packet = encodeMcPacket(connectionState, (net.minecraft.network.Packet) msg);
}
//#if MC>=10800
if (packet instanceof ResourcePackSendS2CPacket) {
ClientConnection connection = ctx.pipeline().get(ClientConnection.class);
save(resourcePackRecorder.handleResourcePack(connection, (ResourcePackSendS2CPacket) packet));
return;
}
//#else
//$$ if (packet instanceof S3FPacketCustomPayload) {
//$$ S3FPacketCustomPayload p = (S3FPacketCustomPayload) packet;
//$$ if ("MC|RPack".equals(p.func_149169_c())) {
//$$ save(resourcePackRecorder.handleResourcePack(p));
//$$ return;
//$$ }
//$$ }
//#endif
//#if MC<11400
//$$ if (packet instanceof FMLProxyPacket) {
//$$ // This packet requires special handling
//#if MC>=10800
//$$ ((FMLProxyPacket) packet).toS3FPackets().forEach(this::save);
//#else
//$$ save(((FMLProxyPacket) packet).toS3FPacket());
//#endif
//$$ super.channelRead(ctx, msg);
//$$ return;
//$$ }
//#endif
//#if MC>=10800
if (packet instanceof CustomPayloadS2CPacket) {
// Forge may read from this ByteBuf and/or release it during handling
// We want to save the full thing however, so we create a copy and save that one instead of the
// original one
// Note: This isn't an issue with vanilla MC because our saving code runs on the main thread
// shortly before the vanilla handling code does. Forge however does some stuff on the netty
// threads which leads to this race condition
packet = new CustomPayloadS2CPacket(
((CustomPayloadS2CPacket) packet).getChannel(),
new PacketByteBuf(((CustomPayloadS2CPacket) packet).getData().slice().retain())
);
}
//#endif
save(packet);
if (packet instanceof CustomPayloadS2CPacket) {
CustomPayloadS2CPacket p = (CustomPayloadS2CPacket) packet;
if (Restrictions.PLUGIN_CHANNEL.equals(p.getChannel())) {
packet = new DisconnectS2CPacket(new LiteralText("Please update to view this replay."));
save(packet);
}
}
} catch(Exception e) {
logger.error("Handling packet for recording:", e);
if (packet != null) {
if (connectionState == NetworkState.PLAY && packet.getId() == PACKET_ID_RESOURCE_PACK_SEND) {
ClientConnection connection = ctx.pipeline().get(ClientConnection.class);
save(resourcePackRecorder.handleResourcePack(connection, (ResourcePackSendS2CPacket) decodeMcPacket(packet)));
return;
}
save(packet);
}
super.channelRead(ctx, msg);
}
//#if MC>=10904
private <T> void DataManager_set(DataTracker dataManager, DataTracker.Entry<T> entry) {
dataManager.startTracking(entry.getData(), entry.get());
private NetworkState getConnectionState() {
ChannelHandlerContext ctx = context;
if (ctx == null) {
return NetworkState.LOGIN;
}
AttributeKey<NetworkState> key = ClientConnection.ATTR_KEY_PROTOCOL;
return ctx.channel().attr(key).get();
}
//#endif
@SuppressWarnings("unchecked")
private PacketData getPacketData(int timestamp, Packet packet) throws Exception {
//#if MC<11500
//$$ if (packet instanceof MobSpawnS2CPacket) {
//$$ MobSpawnS2CPacket p = (MobSpawnS2CPacket) packet;
//$$ SPacketSpawnMobAccessor pa = (SPacketSpawnMobAccessor) p;
//$$ if (pa.getDataManager() == null) {
//$$ pa.setDataManager(new DataTracker(null));
//$$ if (p.getTrackedValues() != null) {
//$$ Set<Integer> seen = new HashSet<>();
//#if MC>=10904
//$$ for (DataTracker.Entry<?> entry : Lists.reverse(p.getTrackedValues())) {
//$$ if (!seen.add(entry.getData().getId())) continue;
//$$ DataManager_set(pa.getDataManager(), entry);
//$$ }
//#else
//$$ for(DataWatcher.WatchableObject wo : Lists.reverse((List<DataWatcher.WatchableObject>) p.func_149027_c())) {
//$$ if (!seen.add(wo.getDataValueId())) continue;
//$$ pa.getDataManager().addObject(wo.getDataValueId(), wo.getObject());
//$$ }
//#endif
//$$ }
//$$ }
//$$ }
//$$
//$$ if (packet instanceof PlayerSpawnS2CPacket) {
//$$ PlayerSpawnS2CPacket p = (PlayerSpawnS2CPacket) packet;
//$$ SPacketSpawnPlayerAccessor pa = (SPacketSpawnPlayerAccessor) p;
//$$ if (pa.getDataManager() == null) {
//$$ pa.setDataManager(new DataTracker(null));
//$$ if (p.getTrackedValues() != null) {
//$$ Set<Integer> seen = new HashSet<>();
//#if MC>=10904
//$$ for (DataTracker.Entry<?> entry : Lists.reverse(p.getTrackedValues())) {
//$$ if (!seen.add(entry.getData().getId())) continue;
//$$ DataManager_set(pa.getDataManager(), entry);
//$$ }
//#else
//$$ for(DataWatcher.WatchableObject wo : Lists.reverse((List<DataWatcher.WatchableObject>) p.func_148944_c())) {
//$$ if (!seen.add(wo.getDataValueId())) continue;
//$$ pa.getDataManager().addObject(wo.getDataValueId(), wo.getObject());
//$$ }
//#endif
//$$ }
//$$ }
//$$ }
//#endif
private static Packet encodeMcPacket(NetworkState connectionState, net.minecraft.network.Packet packet) throws Exception {
//#if MC>=10800
Integer packetId = connectionState.getPacketId(NetworkSide.CLIENTBOUND, packet);
//#else
@@ -468,23 +378,55 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
ByteBuf byteBuf = Unpooled.buffer();
try {
packet.write(new PacketByteBuf(byteBuf));
return new PacketData(timestamp, new com.replaymod.replaystudio.protocol.Packet(
MCVer.getPacketTypeRegistry(loginPhase),
return new Packet(
MCVer.getPacketTypeRegistry(connectionState == NetworkState.LOGIN),
packetId,
com.github.steveice10.netty.buffer.Unpooled.wrappedBuffer(
byteBuf.array(),
byteBuf.arrayOffset(),
byteBuf.readableBytes()
)
));
);
} finally {
byteBuf.release();
}
}
//#if MC>=10800
if (packet instanceof CustomPayloadS2CPacket) {
((CustomPayloadS2CPacket) packet).getData().release();
}
//#endif
private static net.minecraft.network.Packet decodeMcPacket(Packet packet) throws IOException, IllegalAccessException, InstantiationException {
NetworkState connectionState = packet.getRegistry().getState() == State.LOGIN ? NetworkState.LOGIN : NetworkState.PLAY;
int packetId = packet.getId();
PacketByteBuf packetBuf = new PacketByteBuf(Unpooled.wrappedBuffer(packet.getBuf().nioBuffer()));
//#if MC>=11700
//$$ return connectionState.getPacketHandler(NetworkSide.CLIENTBOUND, packetId, packetBuf);
//#else
//#if MC>=10800
net.minecraft.network.Packet p = connectionState.getPacketHandler(NetworkSide.CLIENTBOUND, packetId);
//#else
//$$ net.minecraft.network.Packet p = net.minecraft.network.Packet.generatePacket(connectionState.func_150755_b(), packetId);
//#endif
p.read(packetBuf);
return p;
//#endif
}
private static Packet decodePacket(NetworkState connectionState, ByteBuf buf) {
PacketByteBuf packetBuf = new PacketByteBuf(buf.slice());
int packetId = packetBuf.readVarInt();
byte[] bytes = new byte[packetBuf.readableBytes()];
packetBuf.readBytes(bytes);
return new Packet(
MCVer.getPacketTypeRegistry(connectionState == NetworkState.LOGIN),
packetId,
com.github.steveice10.netty.buffer.Unpooled.wrappedBuffer(bytes)
);
}
private static int getPacketId(NetworkState networkState, net.minecraft.network.Packet packet) {
try {
return requireNonNull(networkState.getPacketId(NetworkSide.CLIENTBOUND, packet));
} catch (Exception e) {
throw new RuntimeException("Failed to determine packet id for " + packet.getClass(), e);
}
}
@@ -526,4 +468,31 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
public void setServerWasPaused() {
this.serverWasPaused = true;
}
private class DecodedPacketListener extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof CustomPayloadS2CPacket) {
CustomPayloadS2CPacket packet = (CustomPayloadS2CPacket) msg;
if (Restrictions.PLUGIN_CHANNEL.equals(packet.getChannel())) {
save(new DisconnectS2CPacket(new LiteralText("Please update to view this replay.")));
}
}
if (msg instanceof PlayerSpawnS2CPacket) {
//#if MC>=10800
UUID uuid = ((PlayerSpawnS2CPacket) msg).getPlayerUuid();
//#else
//$$ UUID uuid = ((S0CPacketSpawnPlayer) msg).func_148948_e().getId();
//#endif
Set<String> uuids = new HashSet<>(Arrays.asList(metaData.getPlayers()));
uuids.add(uuid.toString());
metaData.setPlayers(uuids.toArray(new String[uuids.size()]));
saveMetaData();
}
super.channelRead(ctx, msg);
}
}
}

View File

@@ -11,6 +11,11 @@ import net.minecraft.client.resource.ClientBuiltinResourcePackProvider;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
//#if MC>=11900
//$$ import java.net.MalformedURLException;
//$$ import java.net.URL;
//#endif
//#if MC>=11400
import net.minecraft.text.TranslatableText;
//#else
@@ -192,7 +197,18 @@ public class ResourcePackRecorder {
downloadResourcePack(final int requestId, String url, String hash) {
ClientBuiltinResourcePackProvider packFinder = mc.getResourcePackDownloader();
((IDownloadingPackFinder) packFinder).setRequestCallback(file -> recordResourcePack(file, requestId));
//#if MC>=11700
//#if MC>=11900
//$$ try {
//$$ URL theUrl = new URL(url);
//$$ String protocol = theUrl.getProtocol();
//$$ if (!"http".equals(protocol) && !"https".equals(protocol)) {
//$$ throw new MalformedURLException("Unsupported protocol.");
//$$ }
//$$ return packFinder.download(theUrl, hash, true);
//$$ } catch (MalformedURLException e) {
//$$ return CompletableFuture.failedFuture(e);
//$$ }
//#elseif MC>=11700
//$$ return packFinder.download(url, hash, true);
//#else
return packFinder.download(url, hash);

View File

@@ -1,4 +1,3 @@
//#if MC>=11400
package com.replaymod.render;
import com.replaymod.core.versions.MCVer;
@@ -6,6 +5,7 @@ import com.replaymod.render.frame.BitmapFrame;
import com.replaymod.render.rendering.Channel;
import com.replaymod.render.rendering.FrameConsumer;
import com.replaymod.render.utils.ByteBufferPool;
import com.replaymod.render.utils.Lwjgl3Loader;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import net.minecraft.util.crash.CrashReport;
import org.lwjgl.PointerBuffer;
@@ -27,6 +27,19 @@ import static org.lwjgl.util.tinyexr.TinyEXR.*;
public class EXRWriter implements FrameConsumer<BitmapFrame> {
public static FrameConsumer<BitmapFrame> create(Path outputFolder, boolean keepAlpha) {
return Lwjgl3Loader.createFrameConsumer(
EXRWriter.class,
new Class[]{ Path.class, boolean.class },
new Object[]{ outputFolder, keepAlpha }
);
}
// Compression is pretty slow, so we'll only use it when we've got enough cpu cores to make up for that
private static final int COMPRESSION = Runtime.getRuntime().availableProcessors() >= 8
? TINYEXR_COMPRESSIONTYPE_ZIPS
: TINYEXR_COMPRESSIONTYPE_NONE;
private final Path outputFolder;
private final boolean keepAlpha;
@@ -63,6 +76,7 @@ public class EXRWriter implements FrameConsumer<BitmapFrame> {
header.channels(channelInfos);
header.pixel_types(pixelTypes);
header.requested_pixel_types(requestedPixelTypes);
header.compression_type(COMPRESSION);
// Some readers ignore this, so we use the most expected order
memASCII("A", true, channelInfos.get(0).name());
@@ -127,5 +141,9 @@ public class EXRWriter implements FrameConsumer<BitmapFrame> {
@Override
public void close() {
}
@Override
public boolean isParallelCapable() {
return true;
}
}
//#endif

View File

@@ -134,6 +134,11 @@ public class FFmpegWriter implements FrameConsumer<BitmapFrame> {
}
}
@Override
public boolean isParallelCapable() {
return false;
}
private void checkSize(ReadableDimension size) {
checkSize(size.getWidth(), size.getHeight());
}

View File

@@ -71,4 +71,9 @@ public class PNGWriter implements FrameConsumer<BitmapFrame> {
@Override
public void close() {
}
@Override
public boolean isParallelCapable() {
return true;
}
}

View File

@@ -105,13 +105,6 @@ public class RenderSettings {
public boolean isSupported() {
if (this == BLEND) {
return RenderMethod.BLEND.isSupported();
} else if (this == EXR) {
// Need LJWGL 3
//#if MC>=11400
return true;
//#else
//$$ return false;
//#endif
} else {
return true;
}

View File

@@ -1,4 +1,4 @@
//#if MC>=10800
//#if MC>=10800 && MC<11900
package com.replaymod.render.blend;
import com.replaymod.render.blend.data.DMaterial;

View File

@@ -2,7 +2,7 @@ package com.replaymod.render.blend;
import com.replaymod.render.blend.data.DScene;
import com.replaymod.render.blend.data.Serializer;
//#if MC>=10800
//#if MC>=10800 && MC<11900
// FIXME 1.15
//#if MC<11500
//$$ import com.replaymod.render.blend.exporters.ChunkExporter;
@@ -53,7 +53,7 @@ public class BlendState implements Exporter {
this.blenderFile = BlenderFactory.newBlenderFile(file);
this.factory = new BlenderFactory(blenderFile);
//#if MC>=10800
//#if MC>=10800 && MC<11900
RenderState renderState = new RenderState(this);
register(renderState);
// FIXME 1.15

View File

@@ -1,4 +1,4 @@
//#if MC>=10800
//#if MC>=10800 && MC<11900
package com.replaymod.render.blend.exporters;
import com.replaymod.core.versions.MCVer;

View File

@@ -1,4 +1,4 @@
//#if MC>=10800
//#if MC>=10800 && MC<11900
package com.replaymod.render.blend.exporters;
import com.replaymod.render.blend.BlendMeshBuilder;

View File

@@ -1,4 +1,4 @@
//#if MC>=10800
//#if MC>=10800 && MC<11900
package com.replaymod.render.blend.exporters;
import com.replaymod.render.blend.BlendMeshBuilder;

View File

@@ -1,4 +1,4 @@
//#if MC>=10800
//#if MC>=10800 && MC<11900
package com.replaymod.render.blend.exporters;
import com.replaymod.core.versions.MCVer;

View File

@@ -1,4 +1,4 @@
//#if MC>=10800
//#if MC>=10800 && MC<11900
package com.replaymod.render.blend.exporters;
import com.replaymod.render.blend.BlendState;

View File

@@ -1,4 +1,4 @@
//#if MC>=10800
//#if MC>=10800 && MC<11900
package com.replaymod.render.blend.exporters;
import com.replaymod.core.versions.MCVer;

View File

@@ -1,4 +1,4 @@
//#if MC>=10800
//#if MC>=10800 && MC<11900
package com.replaymod.render.blend.mixin;
import com.replaymod.render.blend.BlendState;

View File

@@ -1,4 +1,4 @@
//#if MC>=10800
//#if MC>=10800 && MC<11900
package com.replaymod.render.blend.mixin;
import com.replaymod.render.blend.BlendState;

View File

@@ -1,4 +1,4 @@
//#if MC>=10800
//#if MC>=10800 && MC<11900
package com.replaymod.render.blend.mixin;
import com.replaymod.render.blend.BlendState;

View File

@@ -1,4 +1,4 @@
//#if MC>=10800
//#if MC>=10800 && MC<11900
package com.replaymod.render.blend.mixin;
import com.replaymod.render.blend.BlendState;

View File

@@ -3,7 +3,6 @@ package com.replaymod.render.mixin;
//#if MC>=10904
import com.replaymod.core.versions.MCVer;
import com.replaymod.render.blend.BlendState;
import com.replaymod.render.blend.exporters.ParticlesExporter;
import com.replaymod.render.hooks.EntityRendererHandler;
import net.minecraft.client.particle.Particle;
import net.minecraft.client.particle.ParticleManager;
@@ -12,6 +11,11 @@ import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
//#if MC>=11900
//#else
import com.replaymod.render.blend.exporters.ParticlesExporter;
//#endif
//#if MC>=11500
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.util.math.Quaternion;
@@ -52,10 +56,12 @@ public abstract class MixinParticleManager {
}
private void buildGeometry(Particle particle, VertexConsumer vertexConsumer, Camera camera, float partialTicks) {
//#if MC<11900
BlendState blendState = BlendState.getState();
if (blendState != null) {
blendState.get(ParticlesExporter.class).onRender(particle, partialTicks);
}
//#endif
particle.buildGeometry(vertexConsumer, camera, partialTicks);
}
//#else

View File

@@ -1,81 +0,0 @@
//#if MC>=11400
package com.replaymod.render.mixin;
import org.lwjgl.system.Library;
import org.lwjgl.system.Platform;
import org.lwjgl.util.tinyexr.TinyEXR;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.function.Consumer;
import java.util.regex.Pattern;
/**
* It appears like natives on Windows cannot be loaded if one of their dependencies has already been loaded by a
* different class loader. In our case we cannot load tinyexr (on the knot class loader) because lwjgl has already
* been loaded on the system class loader.
*
* If we force the tinyexr native to load on the system class loader (by calling `Library.loadSystem(absPath)`),
* it'll load but we'll get an error when we call any of the native methods.
*
* We can't really load TinyEXR itself via the system class loader because Java does not provide any methods for
* modifying the system class path at runtime and we'd have to use JVM-specific hacks.
*
* Strangely, if we use System.loadLibrary instead of System.load, then it all just works. This mixin implements
* that workaround by finding MC's natives folder, extracting the dll from our jar into that folder and then replacing
* the context class passed to Library.loadSystem (which it uses to find dlls in jars) with Library (which is on the
* system class loader) so it cannot find the dll in our jar and falls back to using System.loadLibrary.
*/
@Mixin(value = TinyEXR.class, remap = false)
public class Mixin_WindowsWorkaroundForTinyEXRNatives {
private static final String LOAD_SYSTEM_CONSUMERS = "Lorg/lwjgl/system/Library;loadSystem(Ljava/util/function/Consumer;Ljava/util/function/Consumer;Ljava/lang/Class;Ljava/lang/String;)V";
@ModifyArg(method = "<clinit>", at = @At(value = "INVOKE", target = LOAD_SYSTEM_CONSUMERS))
private static Class<?> uglyWindowsHacks(Consumer<String> load, Consumer<String> loadLibrary, Class<?> context, String name) throws IOException {
if (Platform.get() != Platform.WINDOWS) {
return context; // works out of the box on linux
}
name = System.mapLibraryName(name);
URL libURL = context.getClassLoader().getResource(name);
if (libURL == null) {
throw new UnsatisfiedLinkError("Failed to locate library: " + name);
}
String lwjglLibName = Library.JNI_LIBRARY_NAME;
if (!lwjglLibName.endsWith(".dll")) {
lwjglLibName = System.mapLibraryName(lwjglLibName);
}
String paths = System.getProperty("java.library.path");
Path nativesDir = null;
for (String dir : Pattern.compile(File.pathSeparator).split(paths)) {
Path path = Paths.get(dir);
if (Files.isReadable(path.resolve(lwjglLibName))) {
nativesDir = path;
break;
}
}
if (nativesDir == null) {
throw new UnsatisfiedLinkError("Failed to locate natives folder in " + paths);
}
Path libPath = nativesDir.resolve(name);
try (InputStream source = libURL.openStream()) {
Files.copy(source, libPath, StandardCopyOption.REPLACE_EXISTING);
}
return Library.class;
}
}
//#endif

View File

@@ -7,4 +7,6 @@ public interface FrameConsumer<P extends Frame> extends Closeable {
void consume(Map<Channel, P> channels);
boolean isParallelCapable();
}

View File

@@ -10,6 +10,7 @@ import net.minecraft.util.crash.CrashException;
import net.minecraft.util.crash.CrashReport;
import org.lwjgl.glfw.GLFW;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
@@ -25,8 +26,6 @@ public class Pipeline<R extends Frame, P extends Frame> implements Runnable {
private final FrameCapturer<R> capturer;
private final FrameProcessor<R, P> processor;
private final GlToAbsoluteDepthProcessor depthProcessor;
private int consumerNextFrame;
private final Object consumerLock = new Object();
private final FrameConsumer<P> consumer;
private volatile boolean abort;
@@ -35,7 +34,7 @@ public class Pipeline<R extends Frame, P extends Frame> implements Runnable {
this.worldRenderer = worldRenderer;
this.capturer = capturer;
this.processor = processor;
this.consumer = consumer;
this.consumer = new ParallelSafeConsumer<>(consumer);
float near = 0.05f;
float far = getMinecraft().options.viewDistance * 16 * 4;
@@ -44,7 +43,6 @@ public class Pipeline<R extends Frame, P extends Frame> implements Runnable {
@Override
public synchronized void run() {
consumerNextFrame = 0;
int processors = Runtime.getRuntime().availableProcessors();
int processThreads = Math.max(1, processors - 2); // One processor for the main thread and one for ffmpeg, sorry OS :(
ExecutorService processService = new ThreadPoolExecutor(processThreads, processThreads,
@@ -106,7 +104,6 @@ public class Pipeline<R extends Frame, P extends Frame> implements Runnable {
@Override
public void run() {
try {
Integer frameId = null;
Map<Channel, P> processedChannels = new HashMap<>();
for (Map.Entry<Channel, R> entry : rawChannels.entrySet()) {
P processedFrame = processor.process(entry.getValue());
@@ -114,27 +111,57 @@ public class Pipeline<R extends Frame, P extends Frame> implements Runnable {
depthProcessor.process((BitmapFrame) processedFrame);
}
processedChannels.put(entry.getKey(), processedFrame);
frameId = processedFrame.getFrameId();
}
if (frameId == null) {
if (processedChannels.isEmpty()) {
return;
}
synchronized (consumerLock) {
while (consumerNextFrame != frameId) {
try {
consumerLock.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
consumer.consume(processedChannels);
consumerNextFrame++;
consumerLock.notifyAll();
}
consumer.consume(processedChannels);
} catch (Throwable t) {
CrashReport crashReport = CrashReport.create(t, "Processing frame");
MCVer.getMinecraft().setCrashReport(crashReport);
}
}
}
private static class ParallelSafeConsumer<P extends Frame> implements FrameConsumer<P> {
private final FrameConsumer<P> inner;
private int nextFrame;
private final Object lock = new Object();
private ParallelSafeConsumer(FrameConsumer<P> inner) {
this.inner = inner;
}
@Override
public void consume(Map<Channel, P> channels) {
if (inner.isParallelCapable()) {
inner.consume(channels);
} else {
int frameId = channels.values().iterator().next().getFrameId();
synchronized (lock) {
while (nextFrame != frameId) {
try {
lock.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
inner.consume(channels);
nextFrame++;
lock.notifyAll();
}
}
}
@Override
public boolean isParallelCapable() {
return true;
}
@Override
public void close() throws IOException {
inner.close();
}
}
}

View File

@@ -128,6 +128,11 @@ public class Pipelines {
@Override
public void close() {
}
@Override
public boolean isParallelCapable() {
return true;
}
};
return new Pipeline<>(worldRenderer, capturer, new DummyProcessor<>(), consumer);
}

View File

@@ -8,6 +8,7 @@ import com.replaymod.core.versions.MCVer;
import com.replaymod.pathing.player.AbstractTimelinePlayer;
import com.replaymod.pathing.properties.TimestampProperty;
import com.replaymod.render.CameraPathExporter;
import com.replaymod.render.EXRWriter;
import com.replaymod.render.PNGWriter;
import com.replaymod.render.RenderSettings;
import com.replaymod.render.ReplayModRender;
@@ -55,7 +56,6 @@ import org.lwjgl.opengl.GL11;
//#endif
//#if MC>=11400
import com.replaymod.render.EXRWriter;
import net.minecraft.client.gui.screen.Screen;
import java.util.concurrent.CompletableFuture;
//#else
@@ -129,11 +129,7 @@ public class VideoRenderer implements RenderInfo {
} else {
FrameConsumer<BitmapFrame> frameConsumer;
if (settings.getEncodingPreset() == RenderSettings.EncodingPreset.EXR) {
//#if MC>=11400
frameConsumer = new EXRWriter(settings.getOutputFile().toPath(), settings.isIncludeAlphaChannel());
//#else
//$$ throw new UnsupportedOperationException("EXR requires LWJGL3");
//#endif
frameConsumer = EXRWriter.create(settings.getOutputFile().toPath(), settings.isIncludeAlphaChannel());
} else if (settings.getEncodingPreset() == RenderSettings.EncodingPreset.PNG) {
frameConsumer = new PNGWriter(settings.getOutputFile().toPath(), settings.isIncludeAlphaChannel());
} else {
@@ -141,11 +137,19 @@ public class VideoRenderer implements RenderInfo {
}
ffmpegWriter = frameConsumer instanceof FFmpegWriter ? (FFmpegWriter) frameConsumer : null;
FrameConsumer<BitmapFrame> previewingFrameConsumer = new FrameConsumer<BitmapFrame>() {
private int lastFrameId = -1;
@Override
public void consume(Map<Channel, BitmapFrame> channels) {
BitmapFrame bgra = channels.get(Channel.BRGA);
if (bgra != null) {
gui.updatePreview(bgra.getByteBuffer(), bgra.getSize());
synchronized (this) {
int frameId = bgra.getFrameId();
if (lastFrameId < frameId) {
lastFrameId = frameId;
gui.updatePreview(bgra.getByteBuffer(), bgra.getSize());
}
}
}
frameConsumer.consume(channels);
}
@@ -154,6 +158,11 @@ public class VideoRenderer implements RenderInfo {
public void close() throws IOException {
frameConsumer.close();
}
@Override
public boolean isParallelCapable() {
return frameConsumer.isParallelCapable();
}
};
this.renderingPipeline = Pipelines.newPipeline(settings.getRenderMethod(), this, previewingFrameConsumer);
}

View File

@@ -0,0 +1,132 @@
package com.replaymod.render.utils;
import com.replaymod.core.ReplayMod;
import com.replaymod.render.rendering.Frame;
import com.replaymod.render.rendering.FrameConsumer;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.ProtectionDomain;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
public class Lwjgl3Loader extends URLClassLoader {
static { registerAsParallelCapable(); }
private static Path tempJarFile;
private static Lwjgl3Loader instance;
private final Set<String> implClasses = new CopyOnWriteArraySet<>();
private Lwjgl3Loader(Path jarFile) throws IOException, ReflectiveOperationException {
super(new URL[] { jarFile.toUri().toURL() }, Lwjgl3Loader.class.getClassLoader());
// Need to use a different directory for natives than MC because native files can only be loaded once
Path nativesDir = ReplayMod.instance.folders.getCacheFolder().resolve("lwjgl-natives");
Class<?> configClass = Class.forName("org.lwjgl.system.Configuration", true, this);
Object extractDirField = configClass.getField("SHARED_LIBRARY_EXTRACT_DIRECTORY").get(null);
Method setMethod = configClass.getMethod("set", Object.class);
setMethod.invoke(extractDirField, nativesDir.toAbsolutePath().toString());
}
private boolean canBeSharedWithMc(String name) {
if (name.startsWith("org.lwjgl.")) {
return false; // MC may have a different version
}
for (String implClass : implClasses) {
if (name.startsWith(implClass)) {
return false; // depends on above lwjgl
}
}
return true;
}
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (!canBeSharedWithMc(name)) {
synchronized (getClassLoadingLock(name)) {
Class<?> cls = findLoadedClass(name);
if (cls == null) {
cls = findClass(name);
}
if (resolve) {
resolveClass(cls);
}
return cls;
}
} else {
return super.loadClass(name, resolve);
}
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
return super.findClass(name);
} catch (ClassNotFoundException e) {
String path = name.replace('.', '/').concat(".class");
URL url = getParent().getResource(path);
if (url == null) {
throw e;
}
try {
byte[] bytes = IOUtils.toByteArray(url);
return defineClass(name, bytes, 0, bytes.length, (ProtectionDomain) null);
} catch (IOException e1) {
throw new ClassNotFoundException(name, e1);
}
}
}
private static synchronized Path getJarFile() throws IOException {
if (tempJarFile == null) {
Path jarFile = Files.createTempFile("replaymod-lwjgl", ".jar");
jarFile.toFile().deleteOnExit();
try (InputStream in = Lwjgl3Loader.class.getResourceAsStream("lwjgl.jar")) {
if (in == null) {
throw new IOException("Failed to find embedded lwjgl.jar file.");
}
Files.copy(in, jarFile, REPLACE_EXISTING);
}
tempJarFile = jarFile;
}
return tempJarFile;
}
public static synchronized Lwjgl3Loader instance() {
if (instance == null) {
try {
instance = new Lwjgl3Loader(getJarFile());
} catch (IOException | ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
return instance;
}
@SuppressWarnings("unchecked")
public static <P extends Frame> FrameConsumer<P> createFrameConsumer(
Class<? extends FrameConsumer<P>> implClass,
Class<?>[] parameterTypes,
Object[] args
) {
try {
Lwjgl3Loader loader = instance();
loader.implClasses.add(implClass.getName());
Class<?> realClass = Class.forName(implClass.getName(), true, loader);
Constructor<?> constructor = realClass.getConstructor(parameterTypes);
return (FrameConsumer<P>) constructor.newInstance(args);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -44,8 +44,6 @@ import net.minecraft.network.packet.s2c.play.ScreenHandlerSlotUpdateS2CPacket;
import net.minecraft.network.packet.s2c.play.ScreenHandlerPropertyUpdateS2CPacket;
import net.minecraft.network.packet.s2c.play.HealthUpdateS2CPacket;
import net.minecraft.network.packet.s2c.login.LoginSuccessS2CPacket;
import net.minecraft.network.packet.s2c.play.MobSpawnS2CPacket;
import net.minecraft.network.packet.s2c.play.PaintingSpawnS2CPacket;
import net.minecraft.network.packet.s2c.play.ParticleS2CPacket;
import net.minecraft.network.packet.s2c.play.PlayerAbilitiesS2CPacket;
import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket;
@@ -59,6 +57,12 @@ import net.minecraft.util.math.Vec3d;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
//#if MC>=11900
//#else
import net.minecraft.network.packet.s2c.play.MobSpawnS2CPacket;
import net.minecraft.network.packet.s2c.play.PaintingSpawnS2CPacket;
//#endif
//#if MC>=11600
//#else
//$$ import net.minecraft.network.packet.s2c.play.EntitySpawnGlobalS2CPacket;
@@ -472,11 +476,13 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend
boolean relevantPacket = packet instanceof PlayerSpawnS2CPacket
|| packet instanceof EntitySpawnS2CPacket
//#if MC<11900
|| packet instanceof MobSpawnS2CPacket
|| packet instanceof PaintingSpawnS2CPacket
//#endif
//#if MC<11600
//$$ || packet instanceof EntitySpawnGlobalS2CPacket
//#endif
|| packet instanceof PaintingSpawnS2CPacket
|| packet instanceof ExperienceOrbSpawnS2CPacket
|| packet instanceof EntitiesDestroyS2CPacket;
if (!relevantPacket) {
@@ -688,6 +694,9 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend
, packet.isDebugWorld()
, packet.isFlatWorld()
//#endif
//#if MC>=11900
//$$ , java.util.Optional.empty()
//#endif
);
//#else
//#if MC>=10800
@@ -744,6 +753,9 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend
//$$ respawn.getGeneratorType(),
//$$ GameMode.SPECTATOR
//#endif
//#if MC>=11900
//$$ , java.util.Optional.empty()
//#endif
);
//#else
//#if MC>=10809

View File

@@ -31,10 +31,6 @@ import net.minecraft.stat.StatHandler;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.Box;
//#if MC>=11802
//$$ import net.minecraft.tag.TagKey;
//#endif
//#if FABRIC>=1
//#else
//$$ import net.minecraftforge.client.event.EntityViewRenderEvent;
@@ -47,7 +43,11 @@ import net.minecraft.util.math.Box;
//#if MC>=11400
import net.minecraft.client.world.ClientWorld;
import net.minecraft.fluid.Fluid;
//#if MC>=11802
//$$ import net.minecraft.tag.TagKey;
//#else
import net.minecraft.tag.Tag;
//#endif
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.HitResult;
//#else
@@ -547,7 +547,11 @@ public class CameraEntity
//#if MC>=11400
@Override
//#if MC>=11900
//$$ public void onEquipStack(EquipmentSlot slot, ItemStack stack, ItemStack itemStack) {
//#else
protected void onEquipStack(ItemStack itemStack_1) {
//#endif
// Suppress equip sounds
}
//#endif

View File

@@ -9,9 +9,9 @@ import static net.minecraft.util.math.MathHelper.sin;
// TODO: Marius is responsible for this. Please, someone clean it up.
public class ClassicCameraController implements CameraController {
private static final double SPEED_CHANGE = 0.5;
private static final double LOWER_SPEED = 2;
private static final double UPPER_SPEED = 20;
private static final double LOWER_SPEED = 0.2;
private static final double UPPER_SPEED = 200;
private static final double SPEED_CHANGE = (UPPER_SPEED - LOWER_SPEED) / 20000;
private final CameraEntity camera;

View File

@@ -9,7 +9,7 @@ import net.minecraft.client.options.KeyBinding;
* Camera controller performing vanilla creative-like camera movements.
*/
public class VanillaCameraController implements CameraController {
private static final int MAX_SPEED = 1000;
private static final int MAX_SPEED = 2000;
private static final int MIN_SPEED = -1000;
private static final Vector3f[] DIRECTIONS = new Vector3f[]{

View File

@@ -64,11 +64,11 @@ public class GuiHandler extends EventRegistrations {
mod.getReplayHandler().getReplaySender().setReplaySpeed(0);
//#if MC>=11600
final TranslatableText BUTTON_OPTIONS = new TranslatableText("menu.options");
final TranslatableText BUTTON_EXIT_SERVER = new TranslatableText("menu.disconnect");
final TranslatableText BUTTON_ADVANCEMENTS = new TranslatableText("gui.advancements");
final TranslatableText BUTTON_STATS = new TranslatableText("gui.stats");
final TranslatableText BUTTON_OPEN_TO_LAN = new TranslatableText("menu.shareToLan");
final Text BUTTON_OPTIONS = new TranslatableText("menu.options");
final Text BUTTON_EXIT_SERVER = new TranslatableText("menu.disconnect");
final Text BUTTON_ADVANCEMENTS = new TranslatableText("gui.advancements");
final Text BUTTON_STATS = new TranslatableText("gui.stats");
final Text BUTTON_OPEN_TO_LAN = new TranslatableText("menu.shareToLan");
//#else
//#if MC>=11400
//$$ final String BUTTON_OPTIONS = I18n.translate("menu.options");

View File

@@ -43,7 +43,7 @@
"depends": {
"fabricloader": ">=0.7.0",
"fabric-networking-v0": "*",
"fabric-keybindings-v0": "*",
"fabric-key-binding-api-v1": "*",
"fabric-resource-loader-v0": "*"
},

View File

@@ -22,6 +22,9 @@
"GuiScreenAccessor",
"KeyBindingAccessor",
"MinecraftAccessor",
//#if MC>=11900
//$$ "SimpleOptionAccessor",
//#endif
"TimerAccessor"
],
"compatibilityLevel": "JAVA_8",

View File

@@ -8,8 +8,11 @@
"EntityLivingBaseAccessor",
"IntegratedServerAccessor",
"NetworkManagerAccessor",
"SPacketSpawnMobAccessor",
"SPacketSpawnPlayerAccessor",
"MixinClientConnection",
//#if MC<11500
//$$ "SPacketSpawnMobAccessor",
//$$ "SPacketSpawnPlayerAccessor",
//#endif
"MixinServerInfo",
//#if MC>=10800
"MixinDownloadingPackFinder",

View File

@@ -20,11 +20,13 @@
"ItemRendererAccessor",
//#endif
"ParticleAccessor",
//#if MC<11900
"MixinRenderGlobal",
"MixinRenderItem",
"MixinRenderLivingBase",
"MixinRenderManager"
//#endif
//#endif
],
"compatibilityLevel": "JAVA_8",
"minVersion": "0.6.11",

View File

@@ -36,7 +36,6 @@
//#endif
//#if MC>=11400
"Mixin_PreserveDepthDuringHandRendering",
"Mixin_WindowsWorkaroundForTinyEXRNatives",
//#endif
"GameRendererAccessor",
"MainWindowAccessor",

View File

@@ -1 +1 @@
2.6.5
2.6.6

View File

@@ -3,6 +3,8 @@ net.minecraft.potion.EffectInstance net.minecraft.potion.PotionEffect
net.minecraft.client.gui.screen.AddServerScreen net.minecraft.client.gui.GuiScreenAddServer
net.minecraft.resources.AbstractResourcePack net.minecraft.client.resources.AbstractResourcePack
net.minecraft.resources.FolderPack net.minecraft.client.resources.FolderResourcePack
net.minecraft.resources.IResource net.minecraft.client.resources.IResource
net.minecraft.resources.IResourceManager net.minecraft.client.resources.IResourceManager
net.minecraft.client.resources.DownloadingPackFinder net.minecraft.client.resources.ResourcePackRepository
net.minecraft.client.renderer.chunk.ChunkRenderTask net.minecraft.client.renderer.chunk.ChunkCompileTaskGenerator
net.minecraft.client.settings.AbstractOption net.minecraft.client.settings.GameSettings.Options

View File

@@ -0,0 +1,60 @@
{
"schemaVersion": 1,
"id": "replaymod",
"version": "${version}",
"name": "Replay Mod",
"description": "A Mod which allows you to record, replay and share your Minecraft experience.",
"authors": [
"CrushedPixel",
"johni0702"
],
"contact": {
"homepage": "https://replaymod.com/",
"sources": "https://github.com/ReplayMod/ReplayMod"
},
"license": "GPL-3.0-or-later",
"icon": "assets/replaymod/favicon_logo.png",
"environment": "client",
"entrypoints": {
"client": [
"com.replaymod.core.ReplayModBackend"
],
"modmenu": [
"com.replaymod.core.gui.ModMenuApiImpl"
],
"frex_flawless_frames": [
"com.replaymod.render.utils.FlawlessFrames::registerConsumer"
],
"preLaunch": [
"com.replaymod.core.DummyChainLoadEntryPoint"
],
"mm:early_risers": [
"com.replaymod.core.ReplayModMMLauncher"
]
},
"mixins": [
"mixins.jgui.json",
"mixins.nonmmlauncher.replaymod.json"
],
"depends": {
"fabricloader": ">=0.7.0",
"fabric-networking-v0": "*",
"fabric-keybindings-v0": "*",
"fabric-resource-loader-v0": "*"
},
"conflicts": {
"iris": "<1.1.3"
},
"custom": {
"mm:early_risers": [
"com.replaymod.core.ReplayModMMLauncher"
],
"modmenu:clientsideOnly": true
}
}

View File

@@ -0,0 +1,60 @@
{
"schemaVersion": 1,
"id": "replaymod",
"version": "${version}",
"name": "Replay Mod",
"description": "A Mod which allows you to record, replay and share your Minecraft experience.",
"authors": [
"CrushedPixel",
"johni0702"
],
"contact": {
"homepage": "https://replaymod.com/",
"sources": "https://github.com/ReplayMod/ReplayMod"
},
"license": "GPL-3.0-or-later",
"icon": "assets/replaymod/favicon_logo.png",
"environment": "client",
"entrypoints": {
"client": [
"com.replaymod.core.ReplayModBackend"
],
"modmenu": [
"com.replaymod.core.gui.ModMenuApiImpl"
],
"frex_flawless_frames": [
"com.replaymod.render.utils.FlawlessFrames::registerConsumer"
],
"preLaunch": [
"com.replaymod.core.DummyChainLoadEntryPoint"
],
"mm:early_risers": [
"com.replaymod.core.ReplayModMMLauncher"
]
},
"mixins": [
"mixins.jgui.json",
"mixins.nonmmlauncher.replaymod.json"
],
"depends": {
"fabricloader": ">=0.7.0",
"fabric-networking-api-v1": "*",
"fabric-key-binding-api-v1": "*",
"fabric-resource-loader-v0": "*"
},
"conflicts": {
"iris": "<1.1.3"
},
"custom": {
"mm:early_risers": [
"com.replaymod.core.ReplayModMMLauncher"
],
"modmenu:clientsideOnly": true
}
}

0
versions/1.19/.gitkeep Normal file
View File

View File

@@ -0,0 +1,11 @@
package com.replaymod.core.mixin;
import net.minecraft.client.option.SimpleOption;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(SimpleOption.class)
public interface SimpleOptionAccessor<T> {
@Accessor("value")
void setRawValue(T value);
}

View File

@@ -0,0 +1,4 @@
# FIXME remap should be able to map these without us explicitly declaring them
net.minecraft.client.render.WorldRenderer fullUpdateFuture field_34808
net.minecraft.client.render.WorldRenderer updateFinished field_34809
net.minecraft.client.render.WorldRenderer shouldUpdate field_34810