Release 2.6.6
This commit is contained in:
42
build.gradle
42
build.gradle
@@ -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
2
jGui
Submodule jGui updated: 5e41452b0e...c1e43fc9f0
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
// 1.19+
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<>();
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -63,4 +63,9 @@ public class ScreenshotWriter implements FrameConsumer<BitmapFrame> {
|
||||
public void close() throws IOException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isParallelCapable() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -71,4 +71,9 @@ public class PNGWriter implements FrameConsumer<BitmapFrame> {
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isParallelCapable() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//#if MC>=10800
|
||||
//#if MC>=10800 && MC<11900
|
||||
package com.replaymod.render.blend;
|
||||
|
||||
import com.replaymod.render.blend.data.DMaterial;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//#if MC>=10800
|
||||
//#if MC>=10800 && MC<11900
|
||||
package com.replaymod.render.blend.exporters;
|
||||
|
||||
import com.replaymod.core.versions.MCVer;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//#if MC>=10800
|
||||
//#if MC>=10800 && MC<11900
|
||||
package com.replaymod.render.blend.exporters;
|
||||
|
||||
import com.replaymod.render.blend.BlendMeshBuilder;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//#if MC>=10800
|
||||
//#if MC>=10800 && MC<11900
|
||||
package com.replaymod.render.blend.exporters;
|
||||
|
||||
import com.replaymod.render.blend.BlendMeshBuilder;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//#if MC>=10800
|
||||
//#if MC>=10800 && MC<11900
|
||||
package com.replaymod.render.blend.exporters;
|
||||
|
||||
import com.replaymod.core.versions.MCVer;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//#if MC>=10800
|
||||
//#if MC>=10800 && MC<11900
|
||||
package com.replaymod.render.blend.exporters;
|
||||
|
||||
import com.replaymod.render.blend.BlendState;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//#if MC>=10800
|
||||
//#if MC>=10800 && MC<11900
|
||||
package com.replaymod.render.blend.exporters;
|
||||
|
||||
import com.replaymod.core.versions.MCVer;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//#if MC>=10800
|
||||
//#if MC>=10800 && MC<11900
|
||||
package com.replaymod.render.blend.mixin;
|
||||
|
||||
import com.replaymod.render.blend.BlendState;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//#if MC>=10800
|
||||
//#if MC>=10800 && MC<11900
|
||||
package com.replaymod.render.blend.mixin;
|
||||
|
||||
import com.replaymod.render.blend.BlendState;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//#if MC>=10800
|
||||
//#if MC>=10800 && MC<11900
|
||||
package com.replaymod.render.blend.mixin;
|
||||
|
||||
import com.replaymod.render.blend.BlendState;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//#if MC>=10800
|
||||
//#if MC>=10800 && MC<11900
|
||||
package com.replaymod.render.blend.mixin;
|
||||
|
||||
import com.replaymod.render.blend.BlendState;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -7,4 +7,6 @@ public interface FrameConsumer<P extends Frame> extends Closeable {
|
||||
|
||||
void consume(Map<Channel, P> channels);
|
||||
|
||||
boolean isParallelCapable();
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
132
src/main/java/com/replaymod/render/utils/Lwjgl3Loader.java
Normal file
132
src/main/java/com/replaymod/render/utils/Lwjgl3Loader.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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[]{
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
"depends": {
|
||||
"fabricloader": ">=0.7.0",
|
||||
"fabric-networking-v0": "*",
|
||||
"fabric-keybindings-v0": "*",
|
||||
"fabric-key-binding-api-v1": "*",
|
||||
"fabric-resource-loader-v0": "*"
|
||||
},
|
||||
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
"GuiScreenAccessor",
|
||||
"KeyBindingAccessor",
|
||||
"MinecraftAccessor",
|
||||
//#if MC>=11900
|
||||
//$$ "SimpleOptionAccessor",
|
||||
//#endif
|
||||
"TimerAccessor"
|
||||
],
|
||||
"compatibilityLevel": "JAVA_8",
|
||||
|
||||
@@ -8,8 +8,11 @@
|
||||
"EntityLivingBaseAccessor",
|
||||
"IntegratedServerAccessor",
|
||||
"NetworkManagerAccessor",
|
||||
"SPacketSpawnMobAccessor",
|
||||
"SPacketSpawnPlayerAccessor",
|
||||
"MixinClientConnection",
|
||||
//#if MC<11500
|
||||
//$$ "SPacketSpawnMobAccessor",
|
||||
//$$ "SPacketSpawnPlayerAccessor",
|
||||
//#endif
|
||||
"MixinServerInfo",
|
||||
//#if MC>=10800
|
||||
"MixinDownloadingPackFinder",
|
||||
|
||||
@@ -20,11 +20,13 @@
|
||||
"ItemRendererAccessor",
|
||||
//#endif
|
||||
"ParticleAccessor",
|
||||
//#if MC<11900
|
||||
"MixinRenderGlobal",
|
||||
"MixinRenderItem",
|
||||
"MixinRenderLivingBase",
|
||||
"MixinRenderManager"
|
||||
//#endif
|
||||
//#endif
|
||||
],
|
||||
"compatibilityLevel": "JAVA_8",
|
||||
"minVersion": "0.6.11",
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
//#endif
|
||||
//#if MC>=11400
|
||||
"Mixin_PreserveDepthDuringHandRendering",
|
||||
"Mixin_WindowsWorkaroundForTinyEXRNatives",
|
||||
//#endif
|
||||
"GameRendererAccessor",
|
||||
"MainWindowAccessor",
|
||||
|
||||
@@ -1 +1 @@
|
||||
2.6.5
|
||||
2.6.6
|
||||
|
||||
@@ -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
|
||||
|
||||
60
versions/1.15.2/src/main/resources/fabric.mod.json
Normal file
60
versions/1.15.2/src/main/resources/fabric.mod.json
Normal 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
|
||||
}
|
||||
}
|
||||
60
versions/1.17/src/main/resources/fabric.mod.json
Normal file
60
versions/1.17/src/main/resources/fabric.mod.json
Normal 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
0
versions/1.19/.gitkeep
Normal 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);
|
||||
}
|
||||
4
versions/mapping-fabric-1.19-1.18.2.txt
Normal file
4
versions/mapping-fabric-1.19-1.18.2.txt
Normal 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
|
||||
Reference in New Issue
Block a user