From 8deeb927bac9cb5ff07623a9788ff3013e4d1380 Mon Sep 17 00:00:00 2001 From: Apehum Date: Sat, 20 Jul 2024 07:47:35 +0800 Subject: [PATCH] feat: 1.21 support --- .github/workflows/publish-prerelease.yml | 12 +- .github/workflows/publish.yml | 23 +- build.gradle | 141 ------ build.gradle.kts | 9 + changelog.md | 1 + gradle.properties | 28 +- gradle/libs.versions.toml | 10 + gradle/wrapper/gradle-wrapper.properties | 2 +- readme.md | 1 - record/ReplayVoiceAddon.java | 293 ------------ record/audio/AlLoopbackOutputDevice.java | 425 ------------------ record/audio/StreamAlLoopbackSource.java | 316 ------------- record/mixin/MixinReplayPacketListener.java | 90 ---- .../mixin/ReplayPacketListenerAccessor.java | 16 - settings.gradle | 10 - settings.gradle.kts | 40 ++ versions/1.19.2-fabric/.gitkeep | 0 .../replayvoice/network/ByteArrayCodec.java | 33 ++ .../replayvoice/network/ByteArrayPayload.java | 7 + .../plo/replayvoice/network/CodecManager.java | 26 ++ versions/build.gradle.kts | 101 +++++ versions/gradle.properties | 10 + versions/mainProject | 1 + versions/root.gradle.kts | 10 + .../java/su/plo/replayvoice/CameraUtil.java | 0 .../su/plo/replayvoice/ReplayVoiceAddon.java | 67 ++- .../network/ClientNetworkHandler.java | 33 +- .../replayvoice/network/DummyUdpClient.java | 0 .../replayvoice/network/NetworkHelper.java | 35 ++ .../replaymodinterface/ReplayInterface.java | 5 +- .../mixin/FullReplaySenderMixin.java | 0 .../replaymodinterface/mixin/InitMixin.java | 0 .../mixin/ReplayHandlerMixin.java | 0 .../mixin/VideoRendererMixin.java | 0 .../ConnectionEventHandlerAccessor.java | 0 .../accessor/FullReplaySenderAccessor.java | 0 .../mixin/accessor/GuiPathingAccessor.java | 0 .../mixin/accessor/ReplayHandlerAccessor.java | 0 .../mixin/accessor/VideoRendererAccessor.java | 0 .../modules/VoicechatModule.java | 0 .../resources/assets/replayvoicechat/icon.png | Bin .../src}/main/resources/fabric.mod.json | 3 +- .../resources/replaymodinterface.mixins.json | 0 43 files changed, 379 insertions(+), 1369 deletions(-) delete mode 100644 build.gradle create mode 100644 build.gradle.kts create mode 100644 gradle/libs.versions.toml delete mode 100644 record/ReplayVoiceAddon.java delete mode 100644 record/audio/AlLoopbackOutputDevice.java delete mode 100644 record/audio/StreamAlLoopbackSource.java delete mode 100644 record/mixin/MixinReplayPacketListener.java delete mode 100644 record/mixin/ReplayPacketListenerAccessor.java delete mode 100644 settings.gradle create mode 100644 settings.gradle.kts create mode 100644 versions/1.19.2-fabric/.gitkeep create mode 100644 versions/1.21-fabric/src/main/java/su/plo/replayvoice/network/ByteArrayCodec.java create mode 100644 versions/1.21-fabric/src/main/java/su/plo/replayvoice/network/ByteArrayPayload.java create mode 100644 versions/1.21-fabric/src/main/java/su/plo/replayvoice/network/CodecManager.java create mode 100644 versions/build.gradle.kts create mode 100644 versions/gradle.properties create mode 100644 versions/mainProject create mode 100644 versions/root.gradle.kts rename {src => versions/src}/main/java/su/plo/replayvoice/CameraUtil.java (100%) rename {src => versions/src}/main/java/su/plo/replayvoice/ReplayVoiceAddon.java (74%) rename {src => versions/src}/main/java/su/plo/replayvoice/network/ClientNetworkHandler.java (81%) rename {src => versions/src}/main/java/su/plo/replayvoice/network/DummyUdpClient.java (100%) create mode 100644 versions/src/main/java/su/plo/replayvoice/network/NetworkHelper.java rename {src => versions/src}/main/java/xyz/breadloaf/replaymodinterface/ReplayInterface.java (96%) rename {src => versions/src}/main/java/xyz/breadloaf/replaymodinterface/mixin/FullReplaySenderMixin.java (100%) rename {src => versions/src}/main/java/xyz/breadloaf/replaymodinterface/mixin/InitMixin.java (100%) rename {src => versions/src}/main/java/xyz/breadloaf/replaymodinterface/mixin/ReplayHandlerMixin.java (100%) rename {src => versions/src}/main/java/xyz/breadloaf/replaymodinterface/mixin/VideoRendererMixin.java (100%) rename {src => versions/src}/main/java/xyz/breadloaf/replaymodinterface/mixin/accessor/ConnectionEventHandlerAccessor.java (100%) rename {src => versions/src}/main/java/xyz/breadloaf/replaymodinterface/mixin/accessor/FullReplaySenderAccessor.java (100%) rename {src => versions/src}/main/java/xyz/breadloaf/replaymodinterface/mixin/accessor/GuiPathingAccessor.java (100%) rename {src => versions/src}/main/java/xyz/breadloaf/replaymodinterface/mixin/accessor/ReplayHandlerAccessor.java (100%) rename {src => versions/src}/main/java/xyz/breadloaf/replaymodinterface/mixin/accessor/VideoRendererAccessor.java (100%) rename {src => versions/src}/main/java/xyz/breadloaf/replaymodinterface/modules/VoicechatModule.java (100%) rename {src => versions/src}/main/resources/assets/replayvoicechat/icon.png (100%) rename {src => versions/src}/main/resources/fabric.mod.json (91%) rename {src => versions/src}/main/resources/replaymodinterface.mixins.json (100%) diff --git a/.github/workflows/publish-prerelease.yml b/.github/workflows/publish-prerelease.yml index bf99043..ab92205 100644 --- a/.github/workflows/publish-prerelease.yml +++ b/.github/workflows/publish-prerelease.yml @@ -15,13 +15,15 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Set up JDK 17 + - name: Set up JDK uses: actions/setup-java@v3 with: - java-version: '17' - distribution: 'temurin' - server-id: github # Value of the distributionManagement/repository/id field of the pom.xml - settings-path: ${{ github.workspace }} # location for the settings.xml file + distribution: temurin + java-version: | + 8 + 16 + 17 + 21 - name: Build with Gradle uses: gradle/gradle-build-action@v2 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index e323995..4edce40 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -15,13 +15,15 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Set up JDK 17 + - name: Set up JDK uses: actions/setup-java@v3 with: - java-version: '17' - distribution: 'temurin' - server-id: github # Value of the distributionManagement/repository/id field of the pom.xml - settings-path: ${{ github.workspace }} # location for the settings.xml file + distribution: temurin + java-version: | + 8 + 16 + 17 + 21 - name: Build with Gradle uses: gradle/gradle-build-action@v2 @@ -31,12 +33,17 @@ jobs: - name: Publish to Modrinth uses: Apehum/mc-publish@v1.1 with: + name: 'pv-addon-replaymod' + split-releases: true + files-primary: build/libs/*.jar + version-resolver: 'exact' + + changelog-file: client/changelog.md + + modrinth-unfeature-mode: 'subset' modrinth-id: 4iTfB0AP modrinth-token: ${{ secrets.MODRINTH_TOKEN }} - files-primary: build/libs/!(*-@(dev|sources|javadoc)).jar - files-secondary: "" - - name: Publish to GitHub uses: Apehum/mc-publish@v1.1 with: diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 2db4972..0000000 --- a/build.gradle +++ /dev/null @@ -1,141 +0,0 @@ -plugins { - id "fabric-loom" version "1.4-SNAPSHOT" - id "com.github.johnrengelman.shadow" version "7.1.0" - id "com.matthewprenger.cursegradle" version "1.4.0" - id "com.modrinth.minotaur" version "2.+" -} - -sourceCompatibility = JavaLanguageVersion.of(java_version as int) -targetCompatibility = JavaLanguageVersion.of(java_version as int) - -archivesBaseName = archives_base_name -version = mod_version -group = maven_group - -repositories { - mavenCentral() - mavenLocal() - maven { - name = "henkelmax.public" - url = "https://maven.maxhenkel.de/repository/public" - } - maven { - name = "Modrinth" - url = "https://api.modrinth.com/maven" - content { - includeGroup "maven.modrinth" - } - } - maven { - name = "plasmo-repo" - url = "https://repo.plo.su" - } -} - -runClient.doFirst { - args = ["--username", System.getProperty("user.name")] -} - -dependencies { - minecraft "com.mojang:minecraft:${minecraft_version}" - mappings loom.officialMojangMappings() - modImplementation "net.fabricmc:fabric-loader:${loader_version}" - - modImplementation "net.fabricmc.fabric-api:fabric-api:${fabric_version}" - - implementation "com.google.code.findbugs:jsr305:3.0.2" - - implementation "de.maxhenkel.sonic:sonic:1.0.0-SNAPSHOT" - shadow "de.maxhenkel.sonic:sonic:1.0.0-SNAPSHOT" - - implementation "su.plo.voice.api:client:${pv_version}" - - annotationProcessor "org.projectlombok:lombok:1.18.24" - - modImplementation "maven.modrinth:plasmo-voice:fabric-${minecraft_version}-${pv_dependency}" - modImplementation "maven.modrinth:replaymod:${minecraft_version}-2.6.14" -} - -processResources { - inputs.property "version", version - from("LICENSE") - filesMatching("fabric.mod.json") { - expand "version": mod_version, - "java_version": java_version, - "minecraft_dependency": minecraft_dependency, - "loader_version": loader_version, - "pv_dependency": pv_dependency, - "replaymod_dependency": replaymod_dependency - } -} - -tasks.withType(JavaCompile).configureEach { - it.options.release = java_version as int -} - -java { - withSourcesJar() -} - -jar { - from("LICENSE") { - rename { "${it}_${project.archivesBaseName}" } - } -} - -shadowJar { - configurations = [project.configurations.shadow] - archiveClassifier = "shadow-dev" - relocate "de.maxhenkel.sonic", "${group}.de.maxhenkel.sonic" -} - -remapJar { - dependsOn shadowJar - input = shadowJar.archiveFile.get() -} - -curseforge { - apiKey = file("${rootDir}/curseforge_api_key.txt").exists() ? file("${rootDir}/curseforge_api_key.txt").text : "" - project { - id = curse_id - changelogType = "markdown" - changelog = file("changelog.md") - releaseType = release_type - addGameVersion "Fabric" - gameVersionStrings.addAll(curse_supported_versions.split(",")) - mainArtifact(file("${buildDir}/libs/${archivesBaseName}-${version}.jar")) { - displayName = "[Fabric ${minecraft_display_version}] ${mod_name} ${mod_version}" - relations { - requiredDependency "plasmo-voice" - } - } - afterEvaluate { - uploadTask.dependsOn(remapJar) - } - } - options { - forgeGradleIntegration = false - } -} - -import com.modrinth.minotaur.dependencies.ModDependency - -tasks.modrinth.configure({ - group = "upload" -}) - -modrinth { - token = file("${rootDir}/modrinth_token.txt").exists() ? file("${rootDir}/modrinth_token.txt").text : "" - projectId = modrinth_id - versionNumber = "fabric-${minecraft_display_version}-${mod_version}" - versionName = "[Fabric ${minecraft_display_version}] ${mod_name} ${mod_version}" - uploadFile = remapJar - versionType = release_type.toUpperCase() - changelog = file("changelog.md").text - gameVersions.addAll(curse_supported_versions.split(",")) - loaders = ["fabric"] - dependencies = [ - new ModDependency("1bZhdhsH", "required") // Plasmo Voice - ] -} -tasks.modrinth.dependsOn(build) diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..cb3b7a9 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,9 @@ +plugins { + kotlin("jvm") version libs.versions.kotlin.get() + id("com.github.johnrengelman.shadow") version libs.versions.shadow.get() apply false + id("gg.essential.multi-version.root") apply false +} + +tasks.jar { + enabled = false +} \ No newline at end of file diff --git a/changelog.md b/changelog.md index e69de29..a3c8578 100644 --- a/changelog.md +++ b/changelog.md @@ -0,0 +1 @@ +- 1.21 support \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 74636a3..3828396 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,26 +1,4 @@ -org.gradle.jvmargs=-Xmx4G - -java_version=8 - -minecraft_dependency=>=1.16 -minecraft_version=1.20.1 -loader_version=0.15.6 - -fabric_version=0.91.0+1.20.1 +version=2.0.1 -pv_dependency=2.0.7 -pv_version=2.0.0+ALPHA - -replaymod_dependency=1.16.4-2.6.9 - -mod_name=Replay Voice Chat -mod_version=1.16+-2.0.1 -maven_group=su.plo.replayvoicechat -archives_base_name=pv-addon-replaymod - -minecraft_display_version=1.16+ -release_type=beta -curse_id=605799 -curse_supported_versions=1.16,1.16.1,1.16.2.1.16.3,1.16.4,1.16.5,1.17,1.17.1,1.18,1.18.1,1.18.2,Java 8, Java 11,Java 16,Java 17,Java 18 -modrinth_id=yI6ANuOK -modrinth_supported_version=1.16,1.16.1,1.16.2.1.16.3,1.16.4,1.16.5,1.17,1.17.1,1.18,1.18.1,1.18.2 +org.gradle.jvmargs=-Xmx4G +kotlin.stdlib.default.dependency=false \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 0000000..1409196 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,10 @@ +[versions] +kotlin = "1.9.23" +shadow = "8.1.1" + +plasmovoice = "2.0.3" +lombok = "1.18.30" + +[libraries] +plasmovoice = { module = "su.plo.voice.api:client", version.ref = "plasmovoice" } +lombok = { module = "org.projectlombok:lombok", version.ref = "lombok" } \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3fa8f86..a441313 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/readme.md b/readme.md index 2c35606..2fa4dd3 100644 --- a/readme.md +++ b/readme.md @@ -27,5 +27,4 @@ Both Fabric and Forge are supported. The same add-on file works on both platform ## Credits - [Replay Voice Chat](https://github.com/henkelmax/replay-voice-chat) -- [Sonic](https://github.com/waywardgeek/sonic) - [ReplayMod](https://github.com/ReplayMod/ReplayMod) diff --git a/record/ReplayVoiceAddon.java b/record/ReplayVoiceAddon.java deleted file mode 100644 index caa41ff..0000000 --- a/record/ReplayVoiceAddon.java +++ /dev/null @@ -1,293 +0,0 @@ -package su.plo.replayvoice; - -import com.google.common.io.ByteArrayDataOutput; -import com.google.common.io.ByteStreams; -import com.replaymod.recording.ReplayModRecording; -import io.netty.buffer.Unpooled; -import net.fabricmc.api.ClientModInitializer; -import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; -import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; -import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; -import net.minecraft.client.Minecraft; -import net.minecraft.client.player.LocalPlayer; -import net.minecraft.client.player.RemotePlayer; -import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.network.protocol.game.ClientboundCustomPayloadPacket; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.entity.Entity; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.jetbrains.annotations.NotNull; -import org.lwjgl.openal.AL11; -import org.lwjgl.openal.SOFTLoopback; -import org.lwjgl.system.MemoryUtil; -import su.plo.replayvoice.audio.AlLoopbackOutputDevice; -import su.plo.replayvoice.network.ClientNetworkHandler; -import su.plo.replayvoice.network.DummyUdpClient; -import su.plo.voice.api.addon.annotation.Addon; -import su.plo.voice.api.client.PlasmoVoiceClient; -import su.plo.voice.api.client.audio.device.AlAudioDevice; -import su.plo.voice.api.client.audio.device.DeviceException; -import su.plo.voice.api.client.audio.device.DeviceFactory; -import su.plo.voice.api.client.audio.device.OutputDevice; -import su.plo.voice.api.client.audio.device.source.AlSource; -import su.plo.voice.api.client.audio.source.ClientAudioSource; -import su.plo.voice.api.client.event.VoiceClientInitializedEvent; -import su.plo.voice.api.client.event.audio.capture.AudioCaptureInitializeEvent; -import su.plo.voice.api.client.event.audio.capture.AudioCaptureStartEvent; -import su.plo.voice.api.client.event.audio.device.DeviceClosedEvent; -import su.plo.voice.api.client.event.audio.device.DeviceOpenEvent; -import su.plo.voice.api.client.event.audio.device.source.AlSourceBufferQueuedEvent; -import su.plo.voice.api.client.event.audio.device.source.AlSourceBufferUnqueuedEvent; -import su.plo.voice.api.client.event.audio.source.AudioSourceInitializedEvent; -import su.plo.voice.api.client.event.connection.ConnectionKeyPairGenerateEvent; -import su.plo.voice.api.client.event.connection.UdpClientPacketReceivedEvent; -import su.plo.voice.api.client.event.connection.UdpClientPacketSendEvent; -import su.plo.voice.api.client.event.render.HudActivationRenderEvent; -import su.plo.voice.api.client.event.render.VoiceDistanceRenderEvent; -import su.plo.voice.api.client.event.socket.UdpClientConnectEvent; -import su.plo.voice.api.event.EventCancellable; -import su.plo.voice.api.event.EventSubscribe; -import su.plo.voice.proto.packets.Packet; -import su.plo.voice.proto.packets.udp.clientbound.SelfAudioInfoPacket; -import su.plo.voice.proto.packets.udp.clientbound.SourceAudioPacket; -import su.plo.voice.proto.packets.udp.serverbound.PlayerAudioPacket; -import xyz.breadloaf.replaymodinterface.ReplayInterface; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.security.KeyPair; -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.ScheduledExecutorService; - -@Addon(id = "replayvoice", scope = Addon.Scope.CLIENT, version = "1.0.0", authors = "Apehum") -public class ReplayVoiceAddon implements ClientModInitializer { - - public static final Logger LOGGER = LogManager.getLogger(); - public static final ResourceLocation SELF_AUDIO_PACKET = new ResourceLocation("plasmo:voice/v2/self_audio"); - public static final ResourceLocation SELF_AUDIO_INFO_PACKET = new ResourceLocation("plasmo:voice/v2/self_audio_info"); - public static final ResourceLocation SOURCE_AUDIO_PACKET = new ResourceLocation("plasmo:voice/v2/source_audio"); - public static final ResourceLocation KEYPAIR_PACKET = new ResourceLocation("plasmo:voice/v2/keypair"); - - private final Minecraft minecraft = Minecraft.getInstance(); - - private PlasmoVoiceClient voiceClient; - - private AlLoopbackOutputDevice loopback; - - @EventSubscribe - public void onClientInitialized(@NotNull VoiceClientInitializedEvent event) { - this.voiceClient = event.getClient(); - - ClientNetworkHandler network = new ClientNetworkHandler(voiceClient); - - ClientPlayNetworking.registerGlobalReceiver(SOURCE_AUDIO_PACKET, network::handleSourceAudioPacket); - ClientPlayNetworking.registerGlobalReceiver(SELF_AUDIO_INFO_PACKET, network::handleSelfAudioInfoPacket); - ClientPlayNetworking.registerGlobalReceiver(SELF_AUDIO_PACKET, network::handleSelfAudioPacket); - ClientPlayNetworking.registerGlobalReceiver(KEYPAIR_PACKET, network::handleKeyPairPacket); - } - - @Override - public void onInitializeClient() { - } - - @EventSubscribe - public void onDeviceOpen(@NotNull DeviceOpenEvent event) { - if (loopback != null || - !(event.getDevice() instanceof OutputDevice) || - !(event.getDevice() instanceof AlAudioDevice) - ) return; - - DeviceFactory deviceFactory = voiceClient.getDeviceFactoryManager().getDeviceFactory("AL_OUTPUT") - .orElseThrow(() -> new IllegalStateException("Al Output device factory not initialized")); - List devices = deviceFactory.getDeviceNames(); - - OutputDevice device = (OutputDevice) event.getDevice(); - - this.loopback = new AlLoopbackOutputDevice(voiceClient, null); - try { - loopback.open(device.getFormat().get(), device.getParams().get()); - voiceClient.getDeviceManager().add(loopback); - } catch (DeviceException e) { - throw new RuntimeException(e); - } - } - - @EventSubscribe - public void onDeviceClose(@NotNull DeviceClosedEvent event) { - this.loopback = null; - } - - @EventSubscribe - public void onDistanceRender(@NotNull VoiceDistanceRenderEvent event) { - if (!ReplayInterface.INSTANCE.isInReplayEditor || event.isCancelled()) return; - - Entity camera = Minecraft.getInstance().cameraEntity; - // todo: check if it's a player who recorded the replay - if (!(camera instanceof RemotePlayer)) { - event.setCancelled(true); - } - } - - @EventSubscribe - public void onHudActivationRender(@NotNull HudActivationRenderEvent event) { - if (!ReplayInterface.INSTANCE.isInReplayEditor || event.isRender()) return; - - Entity camera = Minecraft.getInstance().cameraEntity; - if (camera instanceof RemotePlayer) { - RemotePlayer player = (RemotePlayer) camera; - - boolean isActivated = voiceClient.getSourceManager().getSelfSourceInfos() - .stream() - .filter((sourceInfo) -> - sourceInfo.getSelfSourceInfo().getPlayerId().equals(player.getUUID()) && - sourceInfo.getSelfSourceInfo().getActivationId().equals(event.getActivation().getId()) - ) - .anyMatch((sourceInfo) -> - voiceClient.getSourceManager() - .getSourceById(sourceInfo.getSelfSourceInfo().getSourceInfo().getId()) - .map(ClientAudioSource::isActivated) - .orElse(false) - ); - - event.setRender(isActivated); - } - } - - @EventSubscribe - public void onSourceAudioPacketReceived(@NotNull UdpClientPacketReceivedEvent event) { - if (!ReplayInterface.INSTANCE.isReplayModActive()) return; - - if (event.getPacket() instanceof SourceAudioPacket) { - System.out.println("record source"); - record(SOURCE_AUDIO_PACKET, event.getPacket()); - } else if (event.getPacket() instanceof SelfAudioInfoPacket) { - System.out.println("record self info"); - record(SELF_AUDIO_INFO_PACKET, event.getPacket()); - } - } - - @EventSubscribe - public void onUdpPacketSend(@NotNull UdpClientPacketSendEvent event) { - if (!ReplayInterface.INSTANCE.isReplayModActive() || - minecraft.player == null || - !(event.getPacket() instanceof PlayerAudioPacket) - ) return; - - PlayerAudioPacket packet = (PlayerAudioPacket) event.getPacket(); - - ByteArrayDataOutput out = ByteStreams.newDataOutput(); - try { - packet.write(out); - } catch (IOException e) { - LOGGER.error("Failed tto encode packet", e); - return; - } - - System.out.println("record self"); - ReplayModRecording.instance.getConnectionEventHandler().getPacketListener().save( - new ClientboundCustomPayloadPacket( - SELF_AUDIO_PACKET, - new FriendlyByteBuf(Unpooled.wrappedBuffer(out.toByteArray())) - ) - ); - } - - @EventSubscribe - public void onEncryptionInitialize(@NotNull ConnectionKeyPairGenerateEvent event) { - if (!ReplayInterface.INSTANCE.isReplayModActive()) return; - - KeyPair keyPair = event.getKeyPair(); - byte[] publicKey = keyPair.getPublic().getEncoded(); - byte[] privateKey = keyPair.getPrivate().getEncoded(); - - FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer()); - buf.writeInt(publicKey.length); - buf.writeBytes(publicKey); - - buf.writeInt(privateKey.length); - buf.writeBytes(privateKey); - - ReplayModRecording.instance.getConnectionEventHandler().getPacketListener().save( - new ClientboundCustomPayloadPacket(KEYPAIR_PACKET, buf) - ); - } - - @EventSubscribe - public void onUdpClientConnect(@NotNull UdpClientConnectEvent event) { - System.out.println(ReplayInterface.INSTANCE.isInReplayEditor); - if (!ReplayInterface.INSTANCE.isInReplayEditor) return; - - DummyUdpClient udpClient = new DummyUdpClient(voiceClient, event.getConnectionPacket().getSecret()); - voiceClient.getEventBus().register(this, udpClient); - - voiceClient.getUdpClientManager().setClient(udpClient); - event.setCancelled(true); - } - - @EventSubscribe - public void onAudioCaptureInitialize(@NotNull AudioCaptureInitializeEvent event) { - cancelCaptureEvent(event); - } - - @EventSubscribe - public void onAudioCaptureStart(@NotNull AudioCaptureStartEvent event) { - if (cancelCaptureEvent(event)) return; - - FriendlyByteBuf buf1 = new FriendlyByteBuf(Unpooled.buffer(4)); - buf1.writeInt(2); - - FriendlyByteBuf buf2 = new FriendlyByteBuf(Unpooled.buffer(4)); - buf2.writeInt(1); - - long time2 = System.currentTimeMillis(); - long time1 = time2 - 1L; - -// System.out.println("record"); -// ((TimedPacketListener) ReplayModRecording.instance.getConnectionEventHandler().getPacketListener()).save( -// time2, new ClientboundCustomPayloadPacket(SOURCE_AUDIO_PACKET, buf2) -// ); -// ((TimedPacketListener) ReplayModRecording.instance.getConnectionEventHandler().getPacketListener()).save( -// time1, new ClientboundCustomPayloadPacket(SOURCE_AUDIO_PACKET, buf1) -// ); - } - - @EventSubscribe - public void onSourceInitializedEvent(@NotNull AudioSourceInitializedEvent event) { - if (!ReplayInterface.INSTANCE.isInReplayEditor) return; - - event.getSource().setCloseTimeoutMs(0L); - event.getSource().getSourceGroup().ifPresent((sourceGroup) -> { - sourceGroup.getSources().forEach((source) -> { - if (source instanceof AlSource) { - ((AlSource) source).setCloseTimeoutMs(0L); - } - }); - }); - } - - private boolean cancelCaptureEvent(@NotNull EventCancellable event) { - if (event.isCancelled() || ReplayInterface.INSTANCE.isInReplayEditor) { - event.setCancelled(true); - return true; - } - - return false; - } - - private void record(@NotNull ResourceLocation resourceLocation, - @NotNull Packet packet) { - try { - ByteArrayDataOutput out = ByteStreams.newDataOutput(); - packet.write(out); - - FriendlyByteBuf buf = new FriendlyByteBuf( - Unpooled.wrappedBuffer(out.toByteArray()) - ); - ReplayInterface.INSTANCE.sendFakePacket(resourceLocation, buf); - } catch (IOException e) { - LOGGER.warn("Failed to serialize packet: {}", e.getMessage()); - } - } -} diff --git a/record/audio/AlLoopbackOutputDevice.java b/record/audio/AlLoopbackOutputDevice.java deleted file mode 100644 index 6b6e4bb..0000000 --- a/record/audio/AlLoopbackOutputDevice.java +++ /dev/null @@ -1,425 +0,0 @@ -package su.plo.replayvoice.audio; - -import com.mojang.math.Quaternion; -import com.mojang.math.Vector3f; -import lombok.Getter; -import net.minecraft.client.Camera; -import net.minecraft.client.Minecraft; -import net.minecraft.client.player.LocalPlayer; -import net.minecraft.world.phys.Vec3; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.lwjgl.BufferUtils; -import org.lwjgl.openal.*; -import su.plo.voice.api.client.PlasmoVoiceClient; -import su.plo.voice.api.client.audio.device.*; -import su.plo.voice.api.client.audio.device.source.AlSource; -import su.plo.voice.api.client.event.audio.device.DeviceClosedEvent; -import su.plo.voice.api.client.event.audio.device.DeviceOpenEvent; -import su.plo.voice.api.client.event.audio.device.DevicePreOpenEvent; -import su.plo.voice.api.client.event.audio.device.source.AlSourceClosedEvent; -import su.plo.voice.api.event.EventPriority; -import su.plo.voice.api.event.EventSubscribe; -import su.plo.voice.api.util.Params; -import su.plo.voice.client.audio.device.BaseAudioDevice; -import su.plo.voice.mod.client.audio.AlUtil; - -import javax.sound.sampled.AudioFormat; -import java.nio.Buffer; -import java.nio.ByteBuffer; -import java.nio.IntBuffer; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.*; - -import static com.google.common.base.Preconditions.checkNotNull; -import static org.lwjgl.openal.ALC10.ALC_FALSE; -import static org.lwjgl.openal.ALC10.ALC_FREQUENCY; -import static org.lwjgl.openal.ALC11.ALC_TRUE; - -public final class AlLoopbackOutputDevice extends BaseAudioDevice implements AlAudioDevice, AlListenerDevice, HrtfAudioDevice, OutputDevice { - - private static final Logger LOGGER = LogManager.getLogger(); - - private final ExecutorService executor; - @Getter - private final Listener listener = new AlListener(); - private final Set sources = new CopyOnWriteArraySet<>(); - - private boolean hrtfSupported; - private long devicePointer; - private long contextPointer; - - public AlLoopbackOutputDevice(PlasmoVoiceClient client, @Nullable String name) { - super(client, name); - this.executor = Executors.newSingleThreadScheduledExecutor(r -> { - Thread thread = new Thread( - null, - r, - "Al Output Device " + name, - 0 - ); - if (thread.isDaemon()) thread.setDaemon(false); - if (thread.getPriority() != Thread.NORM_PRIORITY) thread.setPriority(Thread.NORM_PRIORITY); - - return thread; - }); - } - - @Override - public void open(@NotNull AudioFormat format, @NotNull Params params) throws DeviceException { - checkNotNull(params, "params cannot be null"); - - DevicePreOpenEvent preOpenEvent = new DevicePreOpenEvent(this, params); - client.getEventBus().call(preOpenEvent); - - if (preOpenEvent.isCancelled()) { - throw new DeviceException("Device opening has been canceled"); - } - - if (isOpen()) { - throw new DeviceException("Device already open"); - } - - runInContext(() -> { - synchronized (this) { - this.devicePointer = openDevice(name); - this.format = format; - this.params = params; - this.bufferSize = ((int) format.getSampleRate() / 1_000) * 20; - - System.out.println("Loopback render supported: " + SOFTLoopback.alcIsRenderFormatSupportedSOFT( - devicePointer, - (int) format.getSampleRate(), - SOFTLoopback.ALC_STEREO_SOFT, - SOFTLoopback.ALC_SHORT_SOFT - ) + " device: " + devicePointer - + " rate: " + (int) format.getSampleRate() - + " channels: " + SOFTLoopback.ALC_STEREO_SOFT - + " format: " + SOFTLoopback.ALC_SHORT_SOFT - ); - - IntBuffer attr = BufferUtils.createIntBuffer(10) - .put(SOFTLoopback.ALC_FORMAT_CHANNELS_SOFT).put(SOFTLoopback.ALC_STEREO_SOFT) - .put(SOFTLoopback.ALC_FORMAT_TYPE_SOFT).put(SOFTLoopback.ALC_SHORT_SOFT) - .put(ALC_FREQUENCY).put((int) format.getSampleRate()) - .put(0); - ((Buffer) attr).flip(); - - ALCCapabilities aLCCapabilities = ALC.createCapabilities(devicePointer); - if (AlUtil.checkAlcErrors(devicePointer, "Get capabilities")) { - throw new DeviceException("Failed to get OpenAL capabilities"); - } else if (!aLCCapabilities.OpenALC11) { - throw new DeviceException("OpenAL 1.1 not supported"); - } - - this.contextPointer = ALC11.alcCreateContext(this.devicePointer, attr); - EXTThreadLocalContext.alcSetThreadContext(this.contextPointer); - - ALCapabilities aLCapabilities = AL.createCapabilities(aLCCapabilities); - AlUtil.checkErrors("Initialization"); - if (!aLCapabilities.AL_EXT_source_distance_model) { - throw new DeviceException("AL_EXT_source_distance_model is not supported"); - } - - this.hrtfSupported = aLCCapabilities.ALC_SOFT_HRTF; - - if (params.containsKey("hrtf") && hrtfSupported) { - Object hrtf = params.get("hrtf"); - if (hrtf.equals(true)) enableHrtf(); - } - - AL10.alEnable(512); - if (!aLCapabilities.AL_EXT_LINEAR_DISTANCE) { - throw new DeviceException("AL_EXT_LINEAR_DISTANCE is not supported"); - } - - AlUtil.checkErrors("Enable per-source distance models"); - LOGGER.info("Device " + name + " initialized"); - - AL11.alListenerf(AL11.AL_GAIN, 1.0F); - - AL11.alListener3f(AL11.AL_POSITION, 0.0F, 0.0F, 0.0F); - AL11.alListenerfv(AL11.AL_ORIENTATION, new float[]{ - 0.0F, 0.0F, -1.0F, - 0.0F, 1.0F, 0.0F - }); - - client.getEventBus().call(new DeviceOpenEvent(this)); - } - }); - } - - @Override - public void close() { - if (!isOpen()) return; - - closeSources(); - runInContext(() -> { - synchronized (this) { - EXTThreadLocalContext.alcSetThreadContext(0L); - - ALC11.alcDestroyContext(contextPointer); - if (devicePointer != 0L) { - ALC11.alcCloseDevice(devicePointer); - } - - this.contextPointer = 0L; - this.devicePointer = 0L; - - LOGGER.info("Loopback device " + name + " closed"); - - client.getEventBus().call(new DeviceClosedEvent(this)); - } - }); - } - - @Override - public void reload() throws DeviceException { - close(); - open(format, params); - } - - @Override - public boolean isOpen() { - return devicePointer != 0L; - } - - @Override - public @Nullable String getName() { - return name; - } - - @Override - public Optional getFormat() { - return Optional.ofNullable(format); - } - - @Override - public Optional getParams() { - return Optional.ofNullable(params); - } - - @Override - public AlSource createSource(boolean stereo, @NotNull Params params) throws DeviceException { - checkNotNull(params, "params cannot be null"); - if (!isOpen()) throw new DeviceException("Device is not open"); - - int numBuffers = 0; - if (params.containsKey("numBuffers")) { - try { - numBuffers = params.get("numBuffers"); - } catch (IllegalArgumentException e) { - throw new DeviceException(e); - } - - if (numBuffers < 4) { - throw new DeviceException("Min number of buffers is 4"); - } else if (numBuffers > 64) { - throw new DeviceException("Max number of buffers is 64"); - } - } - - try { - AlSource source = StreamAlLoopbackSource.create(this, client, stereo, numBuffers); - sources.add(source); - return source; - } catch (RuntimeException e) { - if (e.getCause() instanceof DeviceException) { - throw (DeviceException) e.getCause(); - } - - throw new DeviceException("Failed to allocate new source", e); - } - } - - @Override - public void closeSources() { - sources.forEach(AlSource::close); - } - - @Override - public void reload(@Nullable AudioFormat format, @NotNull Params params) throws DeviceException { - if (devicePointer == 0L) { - throw new DeviceException("Device is not open"); - } - - if (format == null) { - if (this.format == null) throw new DeviceException("Device is not open"); - format = this.format; - } - - checkNotNull(params); - - Params.Builder paramsBuilder = Params.builder(); - this.params.entrySet().forEach( - (entry) -> paramsBuilder.set(entry.getKey(), entry.getValue()) - ); - params.entrySet().forEach( - (entry) -> paramsBuilder.set(entry.getKey(), entry.getValue()) - ); - - close(); - open(format, paramsBuilder.build()); - } - - @Override - public Optional getPointer() { - return devicePointer <= 0 ? Optional.empty() : Optional.of(devicePointer); - } - - @Override - public Optional getContextPointer() { - return contextPointer <= 0 ? Optional.empty() : Optional.of(contextPointer); - } - - @Override - public void runInContext(@NotNull DeviceRunnable runnable) { - try { - if (AlUtil.sameDeviceContext(this)) { - runnable.run(); - return; - } - - CompletableFuture future = new CompletableFuture<>(); - - executor.execute(() -> { - try { - runnable.run(); - } catch (Exception e) { - future.completeExceptionally(e); - } finally { - future.complete(null); - } - }); - - future.get(); - } catch (DeviceException | ExecutionException | InterruptedException e) { - throw new RuntimeException(e); - } - } - - @EventSubscribe(priority = EventPriority.LOWEST) - public void onSourceClosed(@NotNull AlSourceClosedEvent event) { - sources.remove(event.getSource()); - } - - private long openDevice(String deviceName) throws DeviceException { - long l; - if (deviceName == null) { - // default device - l = SOFTLoopback.alcLoopbackOpenDeviceSOFT((ByteBuffer) null); - } else { - l = SOFTLoopback.alcLoopbackOpenDeviceSOFT(deviceName); - } - - if (l != 0L && !AlUtil.checkAlcErrors(l, "Open device")) { - return l; - } - - throw new IllegalStateException("Failed to open OpenAL device"); - } - - @Override - public boolean isHrtfSupported() { - int num = ALC11.alcGetInteger(devicePointer, SOFTHRTF.ALC_NUM_HRTF_SPECIFIERS_SOFT); - return num > 0; - } - - @Override - public boolean isHrtfEnabled() { - int state = ALC11.alcGetInteger(devicePointer, SOFTHRTF.ALC_HRTF_SOFT); - return state > 0; - } - - @Override - public void enableHrtf() { - if (!isHrtfSupported()) return; - - toggleHrtf(true); - - if (isHrtfEnabled()) { - String name = ALC11.alcGetString(devicePointer, SOFTHRTF.ALC_HRTF_SPECIFIER_SOFT); - LOGGER.info("HRTF enabled, using {}", name); - } else { - LOGGER.warn("Failed to enable HRTF"); - } - } - - @Override - public void disableHrtf() { - if (!isHrtfEnabled() || !isHrtfEnabled()) return; - - toggleHrtf(false); - LOGGER.info("HRTF disabled"); - } - - private void toggleHrtf(boolean enabled) { - IntBuffer attr = BufferUtils.createIntBuffer(10) - .put(SOFTHRTF.ALC_HRTF_SOFT) - .put(enabled ? ALC_TRUE : ALC_FALSE) - .put(SOFTHRTF.ALC_HRTF_ID_SOFT) - .put(0) - .put(0); - ((Buffer) attr).flip(); - - if (!SOFTHRTF.alcResetDeviceSOFT(devicePointer, attr)) { - LOGGER.warn("Failed to reset device: {}", ALC11.alcGetString(devicePointer, ALC11.alcGetError(devicePointer))); - } - } - - private class AlListener implements Listener { - - private final Quaternion rotation = new Quaternion(0.0F, 0.0F, 0.0F, 1.0F); - - private final Vector3f forwards = new Vector3f(0.0F, 0.0F, 1.0F); - private final Vector3f up = new Vector3f(0.0F, 1.0F, 0.0F); - - @Override - public void update() { - executor.execute(() -> { - Vec3 position; - Vector3f lookVector, upVector; - - if (params.get("listenerCameraRelative")) { - Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera(); - - position = camera.getPosition(); - lookVector = camera.getLookVector(); - upVector = camera.getUpVector(); - } else { - LocalPlayer player = Minecraft.getInstance().player; - if (player == null) return; - - position = player.getEyePosition(); - - - rotation.set(0.0F, 0.0F, 0.0F, 1.0F); - rotation.mul(Vector3f.YP.rotationDegrees(-player.getYRot())); - rotation.mul(Vector3f.XP.rotationDegrees(player.getXRot())); - - forwards.set(0.0F, 0.0F, 1.0F); - forwards.transform(rotation); - up.set(0.0F, 1.0F, 0.0F); - up.transform(rotation); - - lookVector = forwards; - upVector = up; - } - - AL11.alListener3f( - AL11.AL_POSITION, - (float) position.x(), - (float) position.y(), - (float) position.z() - ); - AL11.alListenerfv(AL11.AL_ORIENTATION, new float[]{ - lookVector.x(), lookVector.y(), lookVector.z(), - upVector.x(), upVector.y(), upVector.z() - }); - }); - } - } -} diff --git a/record/audio/StreamAlLoopbackSource.java b/record/audio/StreamAlLoopbackSource.java deleted file mode 100644 index f1fb30d..0000000 --- a/record/audio/StreamAlLoopbackSource.java +++ /dev/null @@ -1,316 +0,0 @@ -package su.plo.replayvoice.audio; - -import lombok.Setter; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.lwjgl.openal.AL11; -import org.lwjgl.openal.EXTThreadLocalContext; -import org.lwjgl.system.MemoryUtil; -import su.plo.voice.api.client.PlasmoVoiceClient; -import su.plo.voice.api.client.audio.device.AlAudioDevice; -import su.plo.voice.api.client.audio.device.DeviceException; -import su.plo.voice.api.client.audio.device.source.AlSource; -import su.plo.voice.api.client.event.audio.device.source.*; -import su.plo.voice.mod.client.audio.AlUtil; -import su.plo.voice.mod.client.audio.device.source.BaseAlSource; -import su.plo.voice.mod.client.audio.device.source.StreamAlSource; - -import java.nio.Buffer; -import java.nio.ByteBuffer; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.atomic.AtomicBoolean; - -public final class StreamAlLoopbackSource extends BaseAlSource { - - private static final Logger LOGGER = LogManager.getLogger(StreamAlSource.class); - private static final int DEFAULT_NUM_BUFFERS = 8; - - @Setter - private long closeTimeoutMs = 25_000L; - - public static AlSource create(AlAudioDevice device, PlasmoVoiceClient client, boolean stereo, int numBuffers) { - CompletableFuture future = new CompletableFuture<>(); - - device.runInContext(() -> { - int[] pointer = new int[1]; - AL11.alGenSources(pointer); - - if (AlUtil.checkErrors("Allocate new source")) { - future.completeExceptionally(new DeviceException("Failed to allocate new source")); - return; - } - - AlSource source = new StreamAlLoopbackSource(client, device, stereo, numBuffers, pointer[0]); - - AlSourceCreatedEvent event = new AlSourceCreatedEvent(source); - client.getEventBus().call(event); - - future.complete(source); - }); - - try { - return future.get(); - } catch (InterruptedException | ExecutionException e) { - throw new RuntimeException(e); - } - } - - private final int numBuffers; - private final LinkedBlockingQueue queue = new LinkedBlockingQueue<>(); - private final AtomicBoolean isStreaming = new AtomicBoolean(false); - private final byte[] emptyBuffer; - - private Thread thread; - private int[] buffers; - private int[] availableBuffer = new int[1]; - private AtomicBoolean emptyFilled = new AtomicBoolean(false); - private long lastBufferTime; - - private StreamAlLoopbackSource(PlasmoVoiceClient client, AlAudioDevice device, boolean stereo, int numBuffers, int pointer) { - super(client, device, stereo, pointer); - this.numBuffers = numBuffers == 0 ? DEFAULT_NUM_BUFFERS : numBuffers; - this.emptyBuffer = new byte[device.getBufferSize()]; - } - - @Override - public void play() { - AlUtil.checkDeviceContext(device); - - AlSourcePlayEvent event = new AlSourcePlayEvent(this); - client.getEventBus().call(event); - if (event.isCancelled()) return; - - boolean isStreaming = this.isStreaming.get(); - State state = getState(); - if (isStreaming && state == State.PAUSED) { - AL11.alSourcePlay(pointer); - AlUtil.checkErrors("Source play"); - return; - } else if (isStreaming) { - return; - } else if (thread != null && !thread.isAlive()) { - stop(); - } - - startStreamThread(); - } - - @Override - public void stop() { - AlUtil.checkDeviceContext(device); - - AlSourceStopEvent event = new AlSourceStopEvent(this); - client.getEventBus().call(event); - if (event.isCancelled()) return; - - AL11.alSourceStop(pointer); - AlUtil.checkErrors("Source stop"); - - isStreaming.set(false); - - try { - thread.interrupt(); - thread.join(); - } catch (InterruptedException ignored) { - } - - queue.clear(); - } - - @Override - public void write(byte[] samples) { - if (!isStreaming.get()) return; - - if (samples == null) { // fill queue with empty buffers - for (int i = 0; i < numBuffers; i++) { - write(emptyBuffer); - } - return; - } else if (samples.length == 0) { - write(emptyBuffer); - return; - } - - ByteBuffer buffer = MemoryUtil.memAlloc(samples.length); - buffer.put(samples); - ((Buffer) buffer).flip(); - - AlSourceWriteEvent event = new AlSourceWriteEvent(this, buffer); - client.getEventBus().call(event); - if (event.isCancelled()) return; - - queue.offer(buffer); - if (samples != emptyBuffer) { - this.emptyFilled.set(false); - this.lastBufferTime = System.currentTimeMillis(); - } - - if (queue.size() > 1_000) { - LOGGER.warn("Queue overflow, stopping stream"); - stop(); - } - } - - @Override - public void close() { - if (!isStreaming.get()) return; - - device.runInContext(() -> { - stop(); - - AlSourceClosedEvent event = new AlSourceClosedEvent(this); - client.getEventBus().call(event); - - removeProcessedBuffers(); - - AL11.alDeleteBuffers(buffers); - AlUtil.checkErrors("Delete buffers"); - - AL11.alDeleteSources(new int[]{ pointer }); - AlUtil.checkErrors("Delete source"); - - this.pointer = 0; - }); - } - - private void startStreamThread() { - isStreaming.set(true); - - this.thread = new Thread(this::stream); - thread.setName("AL Source Stream"); - thread.setDaemon(false); - - thread.start(); - } - - private void stream() { - EXTThreadLocalContext.alcSetThreadContext(device.getContextPointer().get()); - - this.buffers = new int[numBuffers]; - AL11.alGenBuffers(buffers); - AlUtil.checkErrors("Source gen buffers"); - - queueWithEmptyBuffers(); - fillQueue(); - - this.lastBufferTime = System.currentTimeMillis(); - this.availableBuffer[0] = -1; - - while (isStreaming.get()) { - if (!tickStream()) break; - } - - EXTThreadLocalContext.alcSetThreadContext(0L); - } - - private boolean tickStream() { - int queueSize = queue.size(); - - int processedBuffers = getInt(AL11.AL_BUFFERS_PROCESSED); - AlUtil.checkErrors("Get processed buffers"); - - System.out.println(processedBuffers + " " + queueSize); - - while (processedBuffers > 0 || availableBuffer[0] != -1) { - if (availableBuffer[0] == -1) { - AL11.alSourceUnqueueBuffers(pointer, availableBuffer); - AlUtil.checkErrors("Unqueue buffer"); - - // Bits can be 0 if the format or parameters are corrupt, avoid division by zero - int bits = AL11.alGetBufferi(availableBuffer[0], AL11.AL_BITS); - AlUtil.checkErrors("Source get buffer int"); - if (bits == 0) { - LOGGER.warn("Corrupted stream"); - continue; - } - - if (availableBuffer[0] != -1) { - AlSourceBufferUnqueuedEvent unqueuedEvent = new AlSourceBufferUnqueuedEvent(this, availableBuffer[0]); - client.getEventBus().call(unqueuedEvent); - } - } - - if (availableBuffer[0] != -1 && fillAndPushBuffer(availableBuffer[0])) { - availableBuffer[0] = -1; - processedBuffers--; - } else { - break; - } - } - - State state = getState(); - if (state == State.STOPPED && queueSize == 0 && !emptyFilled.get()) { - removeProcessedBuffers(); - availableBuffer[0] = -1; - - queueWithEmptyBuffers(); - fillQueue(); - - client.getEventBus().call(new AlStreamSourceStoppedEvent(this)); - - play(); - AL11.alSourcePlay(pointer); - AlUtil.checkErrors("Source play"); - } else if (state != State.PLAYING && state != State.PAUSED && queueSize > 0) { - AL11.alSourcePlay(pointer); - AlUtil.checkErrors("Source play"); - } - - if (closeTimeoutMs > 0L && System.currentTimeMillis() - lastBufferTime > closeTimeoutMs) { - LOGGER.info("Stream timed out. Closing..."); - close(); - return false; - } - - try { - Thread.sleep(5L); - } catch (InterruptedException e) { - return false; - } - - return true; - } - - private void queueWithEmptyBuffers() { - for (int i = 0; i < numBuffers; ++i) { - write(emptyBuffer); - } - this.emptyFilled.set(true); - } - - private void fillQueue() { - for (int i = 0; i < numBuffers; ++i) { - fillAndPushBuffer(buffers[i]); - } - } - - private boolean fillAndPushBuffer(int buffer) { - ByteBuffer byteBuffer = queue.poll(); - if (byteBuffer == null) return false; - - AL11.alBufferData(buffer, format, byteBuffer, (int) device.getFormat().get().getSampleRate()); - if (AlUtil.checkErrors("Assigning buffer data")) return false; - - AL11.alSourceQueueBuffers(pointer, new int[]{ buffer }); - if (AlUtil.checkErrors("Queue buffer data")) return false; - - AlSourceBufferQueuedEvent event = new AlSourceBufferQueuedEvent(this, byteBuffer, buffer); - client.getEventBus().call(event); - - return true; - } - - private void removeProcessedBuffers() { - int processedBuffers = getInt(AL11.AL_BUFFERS_PROCESSED); - AlUtil.checkErrors("Get processed buffers"); - - while (processedBuffers > 0) { - int[] buffer = new int[1]; - AL11.alSourceUnqueueBuffers(pointer, buffer); - AlUtil.checkErrors("Unqueue buffer"); - processedBuffers--; - } - } -} diff --git a/record/mixin/MixinReplayPacketListener.java b/record/mixin/MixinReplayPacketListener.java deleted file mode 100644 index 23d6329..0000000 --- a/record/mixin/MixinReplayPacketListener.java +++ /dev/null @@ -1,90 +0,0 @@ -package su.plo.replayvoice.mixin; - -import com.replaymod.core.ReplayMod; -import com.replaymod.lib.com.github.steveice10.packetlib.tcp.io.ByteBufNetOutput; -import com.replaymod.recording.packet.PacketListener; -import com.replaymod.replaystudio.PacketData; -import com.replaymod.replaystudio.io.ReplayOutputStream; -import com.replaymod.replaystudio.util.Utils; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.PooledByteBufAllocator; -import net.minecraft.network.ConnectionProtocol; -import net.minecraft.network.protocol.Packet; -import org.apache.logging.log4j.Logger; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import su.plo.replayvoice.replay.TimedPacketListener; - -import java.io.IOException; -import java.util.concurrent.ExecutorService; - -@Mixin(value = PacketListener.class, remap = false) -public abstract class MixinReplayPacketListener implements TimedPacketListener { - - @Shadow protected abstract ConnectionProtocol getConnectionState(); - - @Shadow @Final private static Logger logger; - - @Shadow private volatile boolean serverWasPaused; - - @Shadow private long timePassedWhilePaused; - - @Shadow @Final private long startTime; - - @Shadow private long lastSentPacket; - - @Shadow @Final private ExecutorService saveService; - - @Shadow @Final private ReplayOutputStream packetOutputStream; - - @Override - public void save(long time, Packet mcPacket) { - com.replaymod.replaystudio.protocol.Packet packet; - try { - packet = ReplayPacketListenerAccessor.invokeEncodeMcPacket(this.getConnectionState(), mcPacket); - } catch (Exception var4) { - logger.error("Encoding packet:", var4); - return; - } - - try { - if (this.serverWasPaused) { - this.timePassedWhilePaused = time - this.startTime - this.lastSentPacket; - this.serverWasPaused = false; - } - - int timestamp = (int)(time - this.startTime - this.timePassedWhilePaused); -// this.lastSentPacket = (long)timestamp; - PacketData packetData = new PacketData((long)timestamp, packet); - this.saveService.submit(() -> { - try { - if (ReplayMod.isMinimalMode()) { - ByteBuf packetIdBuf = PooledByteBufAllocator.DEFAULT.buffer(); - ByteBuf packetBuf = packetData.getPacket().getBuf(); - - try { - (new ByteBufNetOutput(packetIdBuf)).writeVarInt(packetData.getPacket().getId()); - int packetIdLen = packetIdBuf.readableBytes(); - int packetBufLen = packetBuf.readableBytes(); - Utils.writeInt(this.packetOutputStream, (int)packetData.getTime()); - Utils.writeInt(this.packetOutputStream, packetIdLen + packetBufLen); - packetIdBuf.readBytes(this.packetOutputStream, packetIdLen); - packetBuf.getBytes(packetBuf.readerIndex(), this.packetOutputStream, packetBufLen); - } finally { - packetIdBuf.release(); - packetBuf.release(); - } - } else { - this.packetOutputStream.write(packetData); - } - - } catch (IOException var10) { - throw new RuntimeException(var10); - } - }); - } catch (Exception var6) { - logger.error("Writing packet:", var6); - } - } -} diff --git a/record/mixin/ReplayPacketListenerAccessor.java b/record/mixin/ReplayPacketListenerAccessor.java deleted file mode 100644 index 320d0e9..0000000 --- a/record/mixin/ReplayPacketListenerAccessor.java +++ /dev/null @@ -1,16 +0,0 @@ -package su.plo.replayvoice.mixin; - -import com.replaymod.recording.packet.PacketListener; -import net.minecraft.network.ConnectionProtocol; -import net.minecraft.network.protocol.Packet; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Invoker; - -@Mixin(PacketListener.class) -public interface ReplayPacketListenerAccessor { - - @Invoker("encodeMcPacket") - static com.replaymod.replaystudio.protocol.Packet invokeEncodeMcPacket(ConnectionProtocol connectionState, Packet packet) throws Exception { - throw new AssertionError(); - } -} diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index b02216b..0000000 --- a/settings.gradle +++ /dev/null @@ -1,10 +0,0 @@ -pluginManagement { - repositories { - maven { - name = 'Fabric' - url = 'https://maven.fabricmc.net/' - } - mavenCentral() - gradlePluginPortal() - } -} diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..3ea2cee --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,40 @@ +import org.gradle.kotlin.dsl.support.listFilesOrdered + +pluginManagement { + repositories { + gradlePluginPortal() + mavenLocal() + mavenCentral() + google() + + maven("https://jitpack.io/") + maven("https://maven.fabricmc.net") + maven("https://maven.architectury.dev/") + maven("https://maven.minecraftforge.net") + maven("https://repo.plasmoverse.com/snapshots") + } + + plugins { + val egtVersion = "0.7.0-SNAPSHOT" + id("gg.essential.defaults") version egtVersion + id("gg.essential.multi-version.root") version egtVersion + } +} + +rootProject.name = "pv-addon-replaymod" + +include("versions") +project(":versions").apply { + projectDir = file("versions/") + buildFileName = "root.gradle.kts" +} + +file("versions").listFilesOrdered { + return@listFilesOrdered it.isDirectory && it.name.contains("-") +}.forEach { + include("versions:${it.name}") + project(":versions:${it.name}").apply { + projectDir = file("versions/${it.name}") + buildFileName = "../build.gradle.kts" + } +} \ No newline at end of file diff --git a/versions/1.19.2-fabric/.gitkeep b/versions/1.19.2-fabric/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/versions/1.21-fabric/src/main/java/su/plo/replayvoice/network/ByteArrayCodec.java b/versions/1.21-fabric/src/main/java/su/plo/replayvoice/network/ByteArrayCodec.java new file mode 100644 index 0000000..0c6baa4 --- /dev/null +++ b/versions/1.21-fabric/src/main/java/su/plo/replayvoice/network/ByteArrayCodec.java @@ -0,0 +1,33 @@ +package su.plo.replayvoice.network; + +import lombok.Getter; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.NotNull; + +@Getter +public class ByteArrayCodec implements StreamCodec { + + private final CustomPacketPayload.Type type; + + public ByteArrayCodec(@NotNull ResourceLocation channelKey) { + this.type = new CustomPacketPayload.Type<>(channelKey); + } + + @Override + public ByteArrayPayload decode(RegistryFriendlyByteBuf buf) { + int length = buf.readableBytes(); + + byte[] data = new byte[length]; + buf.readBytes(data); + + return new ByteArrayPayload(type, data); + } + + @Override + public void encode(RegistryFriendlyByteBuf buf, ByteArrayPayload payload) { + buf.writeBytes(payload.data()); + } +} diff --git a/versions/1.21-fabric/src/main/java/su/plo/replayvoice/network/ByteArrayPayload.java b/versions/1.21-fabric/src/main/java/su/plo/replayvoice/network/ByteArrayPayload.java new file mode 100644 index 0000000..c34c770 --- /dev/null +++ b/versions/1.21-fabric/src/main/java/su/plo/replayvoice/network/ByteArrayPayload.java @@ -0,0 +1,7 @@ +package su.plo.replayvoice.network; + +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; + +public record ByteArrayPayload(CustomPacketPayload.Type type, byte[] data) implements CustomPacketPayload { + +} \ No newline at end of file diff --git a/versions/1.21-fabric/src/main/java/su/plo/replayvoice/network/CodecManager.java b/versions/1.21-fabric/src/main/java/su/plo/replayvoice/network/CodecManager.java new file mode 100644 index 0000000..46a5b47 --- /dev/null +++ b/versions/1.21-fabric/src/main/java/su/plo/replayvoice/network/CodecManager.java @@ -0,0 +1,26 @@ +package su.plo.replayvoice.network; + +import lombok.experimental.UtilityClass; +import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry; +import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.Map; + +@UtilityClass +public final class CodecManager { + + private static final Map codecs = new HashMap<>(); + + public static @NotNull ByteArrayCodec getCodec(@NotNull final ResourceLocation location) { + return codecs.computeIfAbsent(location, key -> { + ByteArrayCodec codec = new ByteArrayCodec(key); + + PayloadTypeRegistry.playC2S().register(codec.getType(), codec); + PayloadTypeRegistry.playS2C().register(codec.getType(), codec); + + return codec; + }); + } +} diff --git a/versions/build.gradle.kts b/versions/build.gradle.kts new file mode 100644 index 0000000..d9da074 --- /dev/null +++ b/versions/build.gradle.kts @@ -0,0 +1,101 @@ +import org.gradle.jvm.tasks.Jar + +plugins { + id("gg.essential.multi-version") + id("gg.essential.defaults") + id("com.github.johnrengelman.shadow") +} + +base.archivesName.set("${rootProject.name}-${platform.mcVersionStr}") + +val minecraftSupportedVersions = mapOf( + 11902 to "[\">=1.19.2\", \"<=1.20.4\"]", + 12100 to "\">=1.21\"" +) + +repositories { + maven { + name = "Modrinth" + url = uri("https://api.modrinth.com/maven") + + content { + includeGroup("maven.modrinth") + } + } + maven("https://repo.plo.su") +} + +dependencies { + if (platform.mcVersion >= 12100) { + modImplementation("net.fabricmc.fabric-api:fabric-api:0.100.7+1.21") + } else { + modImplementation("net.fabricmc.fabric-api:fabric-api:0.77.0+1.19.2") + } + + implementation(libs.plasmovoice) + + annotationProcessor(libs.lombok) + + modImplementation("maven.modrinth:plasmo-voice:fabric-${platform.mcVersionStr}-2.0.10") + + if (platform.mcVersion >= 12100) { + modImplementation("maven.modrinth:replaymod:${platform.mcVersionStr}-2.6.17") + } else { + modImplementation("maven.modrinth:replaymod:${platform.mcVersionStr}-2.6.14") + } +} + +tasks { + processResources { + inputs.property("version", version) + + from("LICENSE") + filesMatching("fabric.mod.json") { + val fabricLoaderVersion: String by project + + expand(mapOf( + "version" to version, + "loader_version" to fabricLoaderVersion, + "minecraft_dependency" to minecraftSupportedVersions[platform.mcVersion], + "pv_dependency" to libs.versions.plasmovoice.get(), + "replaymod_dependency" to "1.16.4-2.6.9" // todo: ??? + )) + } + } + + java { + withSourcesJar() + } + + jar { + from("LICENSE") { + rename { "${it}_${rootProject.name}" } + } + } + + shadowJar { + configurations = listOf(project.configurations.shadow.get()) + archiveClassifier = "shadow-dev" + } + + remapJar { + inputFile.set(shadowJar.get().archiveFile.get()) + dependsOn(shadowJar) + } + + build { + doLast { + copyJarToRootProject(remapJar.get()) + } + } +} + +fun Project.copyJarToRootProject(task: Jar) { + val file = task.archiveFile.get().asFile + val destinationFile = rootProject.layout.buildDirectory + .file("libs/${file.name.replace("-all", "")}") + .get() + .asFile + + file.copyTo(destinationFile, true) +} diff --git a/versions/gradle.properties b/versions/gradle.properties new file mode 100644 index 0000000..628cb33 --- /dev/null +++ b/versions/gradle.properties @@ -0,0 +1,10 @@ +essential.defaults.loom=1 +essential.defaults.loom.mappings=official +essential.defaults.loom.fabric-loader=net.fabricmc:fabric-loader:0.15.11 +fabricLoaderVersion=0.15.11 + +org.gradle.daemon=false +org.gradle.parallel=true +org.gradle.configureoncommand=true +org.gradle.parallel.threads=4 +org.gradle.jvmargs=-Xmx8G diff --git a/versions/mainProject b/versions/mainProject new file mode 100644 index 0000000..14dd63e --- /dev/null +++ b/versions/mainProject @@ -0,0 +1 @@ +1.19.2-fabric \ No newline at end of file diff --git a/versions/root.gradle.kts b/versions/root.gradle.kts new file mode 100644 index 0000000..abc9654 --- /dev/null +++ b/versions/root.gradle.kts @@ -0,0 +1,10 @@ +plugins { + id("gg.essential.multi-version.root") +} + +preprocess { + val fabric12100 = createNode("1.21-fabric", 12100, "official") + val fabric11902 = createNode("1.19.2-fabric", 11902, "official") + + fabric12100.link(fabric11902) +} diff --git a/src/main/java/su/plo/replayvoice/CameraUtil.java b/versions/src/main/java/su/plo/replayvoice/CameraUtil.java similarity index 100% rename from src/main/java/su/plo/replayvoice/CameraUtil.java rename to versions/src/main/java/su/plo/replayvoice/CameraUtil.java diff --git a/src/main/java/su/plo/replayvoice/ReplayVoiceAddon.java b/versions/src/main/java/su/plo/replayvoice/ReplayVoiceAddon.java similarity index 74% rename from src/main/java/su/plo/replayvoice/ReplayVoiceAddon.java rename to versions/src/main/java/su/plo/replayvoice/ReplayVoiceAddon.java index 92594d3..10c739d 100644 --- a/src/main/java/su/plo/replayvoice/ReplayVoiceAddon.java +++ b/versions/src/main/java/su/plo/replayvoice/ReplayVoiceAddon.java @@ -4,10 +4,10 @@ import com.google.common.io.ByteStreams; import com.google.inject.Inject; import com.replaymod.recording.ReplayModRecording; +import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; -import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; import net.minecraft.client.Minecraft; import net.minecraft.client.player.RemotePlayer; import net.minecraft.network.FriendlyByteBuf; @@ -17,6 +17,7 @@ import org.jetbrains.annotations.NotNull; import su.plo.replayvoice.network.ClientNetworkHandler; import su.plo.replayvoice.network.DummyUdpClient; +import su.plo.replayvoice.network.NetworkHelper; import su.plo.voice.api.addon.AddonInitializer; import su.plo.voice.api.addon.AddonLoaderScope; import su.plo.voice.api.addon.ClientAddonsLoader; @@ -43,14 +44,18 @@ import java.io.IOException; import java.security.KeyPair; -@Addon(id = "pv-addon-replaymod", scope = AddonLoaderScope.CLIENT, version = "2.0.0", authors = "Apehum") +//#if MC>=12100 +//$$ import su.plo.replayvoice.network.CodecManager; +//#endif + +@Addon(id = "pv-addon-replaymod", scope = AddonLoaderScope.CLIENT, version = "2.0.1", authors = "Apehum") public class ReplayVoiceAddon implements ClientModInitializer, AddonInitializer { public static final Logger LOGGER = LogManager.getLogger(); - public static final ResourceLocation SELF_AUDIO_PACKET = new ResourceLocation("plasmo:voice/v2/self_audio"); - public static final ResourceLocation SELF_AUDIO_INFO_PACKET = new ResourceLocation("plasmo:voice/v2/self_audio_info"); - public static final ResourceLocation SOURCE_AUDIO_PACKET = new ResourceLocation("plasmo:voice/v2/source_audio"); - public static final ResourceLocation KEYPAIR_PACKET = new ResourceLocation("plasmo:voice/v2/keypair"); + public static final ResourceLocation SELF_AUDIO_PACKET = ResourceLocation.tryParse("plasmo:voice/v2/self_audio"); + public static final ResourceLocation SELF_AUDIO_INFO_PACKET = ResourceLocation.tryParse("plasmo:voice/v2/self_audio_info"); + public static final ResourceLocation SOURCE_AUDIO_PACKET = ResourceLocation.tryParse("plasmo:voice/v2/source_audio"); + public static final ResourceLocation KEYPAIR_PACKET = ResourceLocation.tryParse("plasmo:voice/v2/keypair"); private final Minecraft minecraft = Minecraft.getInstance(); @@ -61,10 +66,41 @@ public class ReplayVoiceAddon implements ClientModInitializer, AddonInitializer public void onAddonInitialize() { ClientNetworkHandler network = new ClientNetworkHandler(voiceClient); - ClientPlayNetworking.registerGlobalReceiver(SOURCE_AUDIO_PACKET, network::handleSourceAudioPacket); - ClientPlayNetworking.registerGlobalReceiver(SELF_AUDIO_INFO_PACKET, network::handleSelfAudioInfoPacket); - ClientPlayNetworking.registerGlobalReceiver(SELF_AUDIO_PACKET, network::handleSelfAudioPacket); - ClientPlayNetworking.registerGlobalReceiver(KEYPAIR_PACKET, network::handleKeyPairPacket); + //#if MC>=12100 + //$$ ClientPlayNetworking.registerGlobalReceiver( + //$$ CodecManager.getCodec(SOURCE_AUDIO_PACKET).getType(), + //$$ (payload, context) -> network.handleSourceAudioPacket(payload.data()) + //$$ ); + //$$ ClientPlayNetworking.registerGlobalReceiver( + //$$ CodecManager.getCodec(SELF_AUDIO_INFO_PACKET).getType(), + //$$ (payload, context) -> network.handleSelfAudioInfoPacket(payload.data()) + //$$ ); + //$$ ClientPlayNetworking.registerGlobalReceiver( + //$$ CodecManager.getCodec(SELF_AUDIO_PACKET).getType(), + //$$ (payload, context) -> network.handleSelfAudioPacket(payload.data()) + //$$ ); + //$$ ClientPlayNetworking.registerGlobalReceiver( + //$$ CodecManager.getCodec(KEYPAIR_PACKET).getType(), + //$$ (payload, context) -> network.handleKeyPairPacket(payload.data()) + //$$ ); + //#else + ClientPlayNetworking.registerGlobalReceiver( + SOURCE_AUDIO_PACKET, + (client, handler, buf, sender) -> network.handleSourceAudioPacket(ByteBufUtil.getBytes(buf)) + ); + ClientPlayNetworking.registerGlobalReceiver( + SELF_AUDIO_INFO_PACKET, + (client, handler, buf, sender) -> network.handleSelfAudioInfoPacket(ByteBufUtil.getBytes(buf)) + ); + ClientPlayNetworking.registerGlobalReceiver( + SELF_AUDIO_PACKET, + (client, handler, buf, sender) -> network.handleSelfAudioPacket(ByteBufUtil.getBytes(buf)) + ); + ClientPlayNetworking.registerGlobalReceiver( + KEYPAIR_PACKET, + (client, handler, buf, sender) -> network.handleKeyPairPacket(ByteBufUtil.getBytes(buf)) + ); + //#endif } @Override @@ -132,12 +168,9 @@ public void onUdpPacketSend(@NotNull UdpClientPacketSendEvent event) { return; } - ReplayModRecording.instance.getConnectionEventHandler().getPacketListener().save( - ServerPlayNetworking.createS2CPacket( - SELF_AUDIO_PACKET, - new FriendlyByteBuf(Unpooled.wrappedBuffer(out.toByteArray())) - ) - ); + ReplayModRecording.instance.getConnectionEventHandler() + .getPacketListener() + .save(NetworkHelper.createS2CPacket(SELF_AUDIO_PACKET, out.toByteArray())); } @EventSubscribe @@ -156,7 +189,7 @@ public void onEncryptionInitialize(@NotNull ConnectionKeyPairGenerateEvent event buf.writeBytes(privateKey); ReplayModRecording.instance.getConnectionEventHandler().getPacketListener().save( - ServerPlayNetworking.createS2CPacket(KEYPAIR_PACKET, buf) + NetworkHelper.createS2CPacket(KEYPAIR_PACKET, ByteBufUtil.getBytes(buf)) ); } diff --git a/src/main/java/su/plo/replayvoice/network/ClientNetworkHandler.java b/versions/src/main/java/su/plo/replayvoice/network/ClientNetworkHandler.java similarity index 81% rename from src/main/java/su/plo/replayvoice/network/ClientNetworkHandler.java rename to versions/src/main/java/su/plo/replayvoice/network/ClientNetworkHandler.java index d4262a1..5f9592b 100644 --- a/src/main/java/su/plo/replayvoice/network/ClientNetworkHandler.java +++ b/versions/src/main/java/su/plo/replayvoice/network/ClientNetworkHandler.java @@ -42,11 +42,13 @@ public class ClientNetworkHandler { private int currentPacketIndex = 0; - public void handleKeyPairPacket(Minecraft client, ClientPacketListener handler, FriendlyByteBuf buf, PacketSender sender) { + public void handleKeyPairPacket(byte[] data) { + ByteArrayDataInput buf = ByteStreams.newDataInput(data); + byte[] publicKey = new byte[buf.readInt()]; - buf.readBytes(publicKey); + buf.readFully(publicKey); byte[] privateKey = new byte[buf.readInt()]; - buf.readBytes(privateKey); + buf.readFully(privateKey); voiceClient.getServerConnection().ifPresent((connection) -> { try { @@ -65,12 +67,12 @@ public void handleKeyPairPacket(Minecraft client, ClientPacketListener handler, }); } - public void handleSelfAudioPacket(Minecraft client, ClientPacketListener handler, FriendlyByteBuf buf, PacketSender sender) { + public void handleSelfAudioPacket(byte[] data) { if (ReplayInterface.INSTANCE.skipping) return; PlayerAudioPacket packet; try { - packet = getPacket(buf, PlayerAudioPacket.class); + packet = getPacket(data, PlayerAudioPacket.class); } catch (Exception ignored) { ignored.printStackTrace(); return; @@ -91,12 +93,12 @@ public void handleSelfAudioPacket(Minecraft client, ClientPacketListener handler currentPacketIndex = (currentPacketIndex + 1) % MAX_PACKETS; } - public void handleSelfAudioInfoPacket(Minecraft client, ClientPacketListener handler, FriendlyByteBuf buf, PacketSender sender) { + public void handleSelfAudioInfoPacket(byte[] data) { if (ReplayInterface.INSTANCE.skipping) return; SelfAudioInfoPacket packet; try { - packet = getPacket(buf, SelfAudioInfoPacket.class); + packet = getPacket(data, SelfAudioInfoPacket.class); } catch (Exception ignored) { return; } @@ -115,17 +117,17 @@ public void handleSelfAudioInfoPacket(Minecraft client, ClientPacketListener han source.closeAsync(); } else if (!shouldPlay) return; - Optional data = packet.getData(); - if (!data.isPresent()) { + Optional packetData = packet.getData(); + if (!packetData.isPresent()) { if (packet.getSequenceNumber() == 0) return; int index = packetIndex.getOrDefault(packet.getSequenceNumber(), -1); if (index == -1) return; - data = Optional.of(packets.get(index).getData()); + packetData = Optional.of(packets.get(index).getData()); } - data.ifPresent((bytes) -> source.process(new SourceAudioPacket( + packetData.ifPresent((bytes) -> source.process(new SourceAudioPacket( packet.getSequenceNumber(), source.getSourceInfo().getState(), bytes, @@ -135,12 +137,12 @@ public void handleSelfAudioInfoPacket(Minecraft client, ClientPacketListener han }); } - public void handleSourceAudioPacket(Minecraft client, ClientPacketListener handler, FriendlyByteBuf buf, PacketSender sender) { + public void handleSourceAudioPacket(byte[] data) { if (ReplayInterface.INSTANCE.skipping) return; SourceAudioPacket packet; try { - packet = getPacket(buf, SourceAudioPacket.class); + packet = getPacket(data, SourceAudioPacket.class); } catch (Exception ignored) { return; } @@ -152,10 +154,7 @@ public void handleSourceAudioPacket(Minecraft client, ClientPacketListener handl }); } - private > T getPacket(FriendlyByteBuf buf, Class packetClass) throws Exception { - byte[] data = new byte[buf.readableBytes()]; - buf.readBytes(data); - + private > T getPacket(byte[] data, Class packetClass) throws Exception { ByteArrayDataInput in = ByteStreams.newDataInput(data); T packet = (T) packetClass.getConstructor().newInstance(); packet.read(in); diff --git a/src/main/java/su/plo/replayvoice/network/DummyUdpClient.java b/versions/src/main/java/su/plo/replayvoice/network/DummyUdpClient.java similarity index 100% rename from src/main/java/su/plo/replayvoice/network/DummyUdpClient.java rename to versions/src/main/java/su/plo/replayvoice/network/DummyUdpClient.java diff --git a/versions/src/main/java/su/plo/replayvoice/network/NetworkHelper.java b/versions/src/main/java/su/plo/replayvoice/network/NetworkHelper.java new file mode 100644 index 0000000..aeb4998 --- /dev/null +++ b/versions/src/main/java/su/plo/replayvoice/network/NetworkHelper.java @@ -0,0 +1,35 @@ +package su.plo.replayvoice.network; + +import lombok.experimental.UtilityClass; +import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; +import net.minecraft.network.protocol.Packet; +import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.NotNull; + +//#if MC>=12100 +//$$ import net.minecraft.network.protocol.common.ClientCommonPacketListener; +//#else +import io.netty.buffer.Unpooled; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.game.ClientGamePacketListener; +//#endif + +@UtilityClass +public final class NetworkHelper { + + //#if MC>=12100 + //$$ public static Packet createS2CPacket(@NotNull ResourceLocation resourceLocation, byte[] data) { + //$$ //#if MC>=12100 + //$$ ByteArrayCodec codec = CodecManager.getCodec(resourceLocation); + //$$ + //$$ return ServerPlayNetworking.createS2CPacket(new ByteArrayPayload(codec.getType(), data)); + //$$ } + //#else + public static Packet createS2CPacket(@NotNull ResourceLocation resourceLocation, byte[] data) { + return ServerPlayNetworking.createS2CPacket( + resourceLocation, + new FriendlyByteBuf(Unpooled.wrappedBuffer(data)) + ); + } + //#endif +} diff --git a/src/main/java/xyz/breadloaf/replaymodinterface/ReplayInterface.java b/versions/src/main/java/xyz/breadloaf/replaymodinterface/ReplayInterface.java similarity index 96% rename from src/main/java/xyz/breadloaf/replaymodinterface/ReplayInterface.java rename to versions/src/main/java/xyz/breadloaf/replaymodinterface/ReplayInterface.java index b2d14e2..4db6811 100644 --- a/src/main/java/xyz/breadloaf/replaymodinterface/ReplayInterface.java +++ b/versions/src/main/java/xyz/breadloaf/replaymodinterface/ReplayInterface.java @@ -11,13 +11,14 @@ import com.replaymod.replaystudio.pathing.path.Path; import com.replaymod.replaystudio.pathing.path.Timeline; import com.replaymod.simplepathing.ReplayModSimplePathing; +import io.netty.buffer.ByteBufUtil; import net.fabricmc.api.ClientModInitializer; -import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.protocol.Packet; import net.minecraft.resources.ResourceLocation; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import su.plo.replayvoice.network.NetworkHelper; import xyz.breadloaf.replaymodinterface.mixin.accessor.ConnectionEventHandlerAccessor; import xyz.breadloaf.replaymodinterface.mixin.accessor.GuiPathingAccessor; @@ -56,7 +57,7 @@ public boolean isReplayModActive() { //Adds a fake packet into recording data public void sendFakePacket(ResourceLocation resourceLocation, FriendlyByteBuf packetData) { - sendFakePacket(ServerPlayNetworking.createS2CPacket(resourceLocation, packetData)); + sendFakePacket(NetworkHelper.createS2CPacket(resourceLocation, ByteBufUtil.getBytes(packetData))); } public void sendFakePacket(Packet packet) { diff --git a/src/main/java/xyz/breadloaf/replaymodinterface/mixin/FullReplaySenderMixin.java b/versions/src/main/java/xyz/breadloaf/replaymodinterface/mixin/FullReplaySenderMixin.java similarity index 100% rename from src/main/java/xyz/breadloaf/replaymodinterface/mixin/FullReplaySenderMixin.java rename to versions/src/main/java/xyz/breadloaf/replaymodinterface/mixin/FullReplaySenderMixin.java diff --git a/src/main/java/xyz/breadloaf/replaymodinterface/mixin/InitMixin.java b/versions/src/main/java/xyz/breadloaf/replaymodinterface/mixin/InitMixin.java similarity index 100% rename from src/main/java/xyz/breadloaf/replaymodinterface/mixin/InitMixin.java rename to versions/src/main/java/xyz/breadloaf/replaymodinterface/mixin/InitMixin.java diff --git a/src/main/java/xyz/breadloaf/replaymodinterface/mixin/ReplayHandlerMixin.java b/versions/src/main/java/xyz/breadloaf/replaymodinterface/mixin/ReplayHandlerMixin.java similarity index 100% rename from src/main/java/xyz/breadloaf/replaymodinterface/mixin/ReplayHandlerMixin.java rename to versions/src/main/java/xyz/breadloaf/replaymodinterface/mixin/ReplayHandlerMixin.java diff --git a/src/main/java/xyz/breadloaf/replaymodinterface/mixin/VideoRendererMixin.java b/versions/src/main/java/xyz/breadloaf/replaymodinterface/mixin/VideoRendererMixin.java similarity index 100% rename from src/main/java/xyz/breadloaf/replaymodinterface/mixin/VideoRendererMixin.java rename to versions/src/main/java/xyz/breadloaf/replaymodinterface/mixin/VideoRendererMixin.java diff --git a/src/main/java/xyz/breadloaf/replaymodinterface/mixin/accessor/ConnectionEventHandlerAccessor.java b/versions/src/main/java/xyz/breadloaf/replaymodinterface/mixin/accessor/ConnectionEventHandlerAccessor.java similarity index 100% rename from src/main/java/xyz/breadloaf/replaymodinterface/mixin/accessor/ConnectionEventHandlerAccessor.java rename to versions/src/main/java/xyz/breadloaf/replaymodinterface/mixin/accessor/ConnectionEventHandlerAccessor.java diff --git a/src/main/java/xyz/breadloaf/replaymodinterface/mixin/accessor/FullReplaySenderAccessor.java b/versions/src/main/java/xyz/breadloaf/replaymodinterface/mixin/accessor/FullReplaySenderAccessor.java similarity index 100% rename from src/main/java/xyz/breadloaf/replaymodinterface/mixin/accessor/FullReplaySenderAccessor.java rename to versions/src/main/java/xyz/breadloaf/replaymodinterface/mixin/accessor/FullReplaySenderAccessor.java diff --git a/src/main/java/xyz/breadloaf/replaymodinterface/mixin/accessor/GuiPathingAccessor.java b/versions/src/main/java/xyz/breadloaf/replaymodinterface/mixin/accessor/GuiPathingAccessor.java similarity index 100% rename from src/main/java/xyz/breadloaf/replaymodinterface/mixin/accessor/GuiPathingAccessor.java rename to versions/src/main/java/xyz/breadloaf/replaymodinterface/mixin/accessor/GuiPathingAccessor.java diff --git a/src/main/java/xyz/breadloaf/replaymodinterface/mixin/accessor/ReplayHandlerAccessor.java b/versions/src/main/java/xyz/breadloaf/replaymodinterface/mixin/accessor/ReplayHandlerAccessor.java similarity index 100% rename from src/main/java/xyz/breadloaf/replaymodinterface/mixin/accessor/ReplayHandlerAccessor.java rename to versions/src/main/java/xyz/breadloaf/replaymodinterface/mixin/accessor/ReplayHandlerAccessor.java diff --git a/src/main/java/xyz/breadloaf/replaymodinterface/mixin/accessor/VideoRendererAccessor.java b/versions/src/main/java/xyz/breadloaf/replaymodinterface/mixin/accessor/VideoRendererAccessor.java similarity index 100% rename from src/main/java/xyz/breadloaf/replaymodinterface/mixin/accessor/VideoRendererAccessor.java rename to versions/src/main/java/xyz/breadloaf/replaymodinterface/mixin/accessor/VideoRendererAccessor.java diff --git a/src/main/java/xyz/breadloaf/replaymodinterface/modules/VoicechatModule.java b/versions/src/main/java/xyz/breadloaf/replaymodinterface/modules/VoicechatModule.java similarity index 100% rename from src/main/java/xyz/breadloaf/replaymodinterface/modules/VoicechatModule.java rename to versions/src/main/java/xyz/breadloaf/replaymodinterface/modules/VoicechatModule.java diff --git a/src/main/resources/assets/replayvoicechat/icon.png b/versions/src/main/resources/assets/replayvoicechat/icon.png similarity index 100% rename from src/main/resources/assets/replayvoicechat/icon.png rename to versions/src/main/resources/assets/replayvoicechat/icon.png diff --git a/src/main/resources/fabric.mod.json b/versions/src/main/resources/fabric.mod.json similarity index 91% rename from src/main/resources/fabric.mod.json rename to versions/src/main/resources/fabric.mod.json index c7abc2c..07708f6 100644 --- a/src/main/resources/fabric.mod.json +++ b/versions/src/main/resources/fabric.mod.json @@ -27,8 +27,7 @@ ], "depends": { "fabricloader": ">=${loader_version}", - "minecraft": "${minecraft_dependency}", - "java": ">=${java_version}", + "minecraft": ${minecraft_dependency}, "replaymod": ">=${replaymod_dependency}", "plasmovoice": ">=${pv_dependency}" } diff --git a/src/main/resources/replaymodinterface.mixins.json b/versions/src/main/resources/replaymodinterface.mixins.json similarity index 100% rename from src/main/resources/replaymodinterface.mixins.json rename to versions/src/main/resources/replaymodinterface.mixins.json