Skip to content

Commit 6f3020e

Browse files
committed
Added Melon color (red). Made recordings end on scene load async again. Made recordings only serialize after next scene loads. Made players not able to be null in infos anymore (hopefully). Made voice recording pipeline fully work. Fixed null files in the explorer making game freak out and break.
1 parent 94908fe commit 6f3020e

12 files changed

Lines changed: 391 additions & 93 deletions

File tree

12 KB
Binary file not shown.

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ The documentation for the API is found [here](docs/API/README.md)
132132
**ERROR** - Development of ReplayMod
133133

134134
**TacoSlayer** - UI design help, logo design, and design feedback
135-
**Blank** - Help with the binary format, GitHub workflow, and design decisions
135+
**Blank** - Help with the binary format, GitHub workflow, voice playback, and design decisions
136136
**MatsuNoKi** - UI feedback and design decisions
137137
**ContagiousPow** - UI design feedback, testing, and other design decisions
138138
**Kurama** - UI feedback
@@ -147,6 +147,11 @@ boxyk9, Roydaboy27, Dr.Rock, MatsuNoKi, ContagiousPow, iListen2Sound,
147147
Footi, Lemmi05, @pple, WillNinja08, Peanut, BlueEyedFox, Fjm12, Pep,
148148
Hudnimynini, Yiak65, Grand, ChimmyTv, Alexderus117, and BIVN
149149

150+
## Third-Party Libraries
151+
152+
- Concentus (Opus codec implementation)
153+
BSD licensed - see [ThirdPartyLicenses.txt](ThirdPartyLicenses.txt)
154+
150155
---
151156

152157
## And thanks to the rest of the rumble community for supporting me through this project!

ThirdPartyLicenses.txt

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
Concentus (Opus codec implementation)
2+
-------------------------------------
3+
4+
Copyright (c) by various holding parties, including (but not limited to):
5+
Skype Limited, Xiph.Org Foundation, CSIRO, Microsoft Corporation,
6+
Jean-Marc Valin, Gregory Maxwell, Mark Borgerding, Timothy B. Terriberry,
7+
Logan Stromberg. All rights are reserved by their respective holders.
8+
9+
Redistribution and use in source and binary forms, with or without
10+
modification, are permitted provided that the following conditions are met:
11+
12+
* Redistributions of source code must retain the above copyright notice, this
13+
list of conditions and the following disclaimer.
14+
15+
* Redistributions in binary form must reproduce the above copyright notice,
16+
this list of conditions and the following disclaimer in the documentation
17+
and/or other materials provided with the distribution.
18+
19+
* Neither the name of Internet Society, IETF or IETF Trust, nor the
20+
names of specific contributors, may be used to endorse or promote
21+
products derived from this software without specific prior written
22+
permission.
23+
24+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
28+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34+
35+
36+
This repository and its redistributable packages contain independently compiled
37+
versions of the Opus C reference library, which is maintained by Xiph.org and the
38+
Opus open-source contributors. The source code for these libraries is freely available
39+
at https://gitlab.xiph.org/xiph/opus/-/tags/v1.5.2, and all binaries are being
40+
redistributed to you under the same terms of the general Opus license dictated above.

src/Core/Main.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@
1010
using Il2CppRUMBLE.Players.Subsystems;
1111
using Il2CppRUMBLE.Slabs.Forms;
1212
using Il2CppRUMBLE.Social.Phone;
13+
using Il2CppSystem;
1314
using Il2CppSystem.IO;
1415
using Il2CppTMPro;
1516
using MelonLoader;
17+
using MelonLoader.Utils;
1618
using ReplayMod.Replay;
1719
using ReplayMod.Replay.Files;
1820
using ReplayMod.Replay.Serialization;
@@ -32,6 +34,7 @@
3234

3335
[assembly: MelonInfo(typeof(Main), BuildInfo.Name, BuildInfo.Version, BuildInfo.Author)]
3436
[assembly: MelonGame("Buckethead Entertainment", "RUMBLE")]
37+
[assembly: MelonColor(255, 255, 0, 0), MelonAuthorColor(255, 255, 0, 0)]
3538
[assembly: MelonAdditionalDependencies("RumbleModdingAPI","UIFramework")]
3639

3740
namespace ReplayMod.Core;
@@ -49,7 +52,9 @@ public class Main : MelonMod
4952
// Runtime
5053
public static Main instance;
5154
public Main() => instance = this;
55+
5256
public static string currentScene => Calls.Scene.GetSceneName();
57+
public static bool isSceneReady = false;
5358

5459
public static ReplayPlayback Playback;
5560
public static ReplayRecording Recording;
@@ -90,6 +95,8 @@ public class Main : MelonMod
9095
public MelonPreferences_Entry<bool> HandFingerRecording = new();
9196
public MelonPreferences_Entry<bool> CloseHandsOnPose = new();
9297

98+
public MelonPreferences_Entry<bool> VoiceRecording = new();
99+
93100
// Automatic Markers - Match End
94101
public MelonPreferences_Entry<bool> EnableMatchEndMarker = new();
95102

@@ -341,8 +348,6 @@ public override void OnLateInitializeMelon()
341348
{
342349
if (EnableMatchEndMarker.Value)
343350
Recording.AddMarker("core.matchEnded", Color.black);
344-
345-
Recording.StopRecording();
346351
};
347352

348353
ReplayFiles.Init();
@@ -361,6 +366,10 @@ public override void OnApplicationQuit()
361366
{
362367
if (Recording.isRecording)
363368
Recording.StopRecording();
369+
370+
string directory = Path.Combine(MelonEnvironment.UserDataDirectory, "ReplayMod", "TempReplayVoices");
371+
if (System.IO.Directory.Exists(directory))
372+
System.IO.Directory.Delete(directory, true);
364373
}
365374

366375
public override void OnSceneWasLoaded(int buildIndex, string sceneName)
@@ -556,6 +565,8 @@ IEnumerator ShiftstoneApplyDelay()
556565

557566
Playback.SetPlaybackSpeed(1f);
558567
ReplayPlayback.isReplayScene = false;
568+
569+
isSceneReady = true;
559570
}
560571

561572
IEnumerator DelayedParkLoad()

src/Core/Patches.cs

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -257,15 +257,21 @@ static void Postfix(PlayerVisuals __instance)
257257
Main.Recording.RegisterPlayer(player);
258258
}
259259

260-
public static IEnumerator VisualDataDelay(Player player)
260+
public static IEnumerator VisualDataDelay(Player player, int slot)
261261
{
262262
yield return new WaitForSeconds(0.1f);
263263

264264
if (player == null)
265265
yield break;
266266

267-
var id = player.Data.GeneralData.PlayFabMasterId;
268-
Main.Recording.PlayerInfos[id] = new PlayerInfo(player);
267+
var info = new PlayerInfo(player);
268+
269+
Main.Recording.PlayerInfos[player.Data.GeneralData.PlayFabMasterId] = info;
270+
271+
while (Main.Recording.RecordedPlayerInfos.Count <= slot)
272+
Main.Recording.RecordedPlayerInfos.Add(null);
273+
274+
Main.Recording.RecordedPlayerInfos[slot] = new PlayerInfo(player);
269275
}
270276
}
271277

@@ -315,6 +321,12 @@ static void Prefix()
315321
{
316322
if (!Main.instance.UIInitialized)
317323
return;
324+
325+
if (Main.Recording.isRecording)
326+
{
327+
Main.isSceneReady = false;
328+
Main.Recording.StopRecording();
329+
}
318330

319331
Main.Playback.StopReplay();
320332

@@ -384,15 +396,5 @@ static bool Prefix(PlayerHealth __instance)
384396
return false;
385397
}
386398
}
387-
[HarmonyPatch(typeof(VoiceClient), nameof(VoiceClient.createLocalVoice))]
388-
public static class Patch_VoiceClient_createLocalVoice
389-
{
390-
static void Postfix(Il2CppPhoton.Voice.VoiceClient __instance, Il2CppPhoton.Voice.LocalVoice __result, int __0, Il2CppSystem.Func<byte, int, Il2CppPhoton.Voice.LocalVoice> __1)
391-
{
392-
if (Main.Recording.isRecording){
393-
__result.DebugEchoMode = true;
394-
}
395-
}
396-
}
397399
}
398400

src/Core/ReplayPlayback.cs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,14 @@
1717
using Il2CppRUMBLE.Poses;
1818
using Il2CppRUMBLE.Utilities;
1919
using MelonLoader;
20+
using MelonLoader.Utils;
2021
using ReplayMod.Replay;
2122
using ReplayMod.Replay.Serialization;
2223
using ReplayMod.Replay.UI;
2324
using RumbleModdingAPI.RMAPI;
2425
using UnityEngine;
2526
using UnityEngine.InputSystem.XR;
27+
using UnityEngine.Networking;
2628
using UnityEngine.VFX;
2729
using static UnityEngine.Mathf;
2830
using AudioManager = Il2CppRUMBLE.Managers.AudioManager;
@@ -302,6 +304,26 @@ public void LoadReplay(string path, bool allowDifferentSceneLoad = false)
302304

303305
ReorderPlayers();
304306

307+
foreach (var info in currentReplay.Header.VoiceTrackInfos)
308+
{
309+
var clone = PlaybackPlayers.FirstOrDefault(p =>
310+
p.Controller.assignedPlayer.Data.GeneralData.PlayFabMasterId == info.MasterId);
311+
312+
if (clone == null) continue;
313+
314+
string voicePath = Path.Combine(currentReplay.Header.VoiceFolder, info.FileName);
315+
316+
var clip = ReplayVoices.LoadVoiceClipFromFile(voicePath);
317+
318+
if (clip == null) return;
319+
320+
clone.VoiceTracks.Add(new Clone.VoiceTrack
321+
{
322+
StartTime = info.StartTime,
323+
Clip = clip
324+
});
325+
}
326+
305327
if (currentReplay.Header.Scene == "Gym")
306328
{
307329
foreach (var playbackPlayer in PlaybackPlayers)
@@ -363,6 +385,10 @@ public void StopReplay()
363385
isPlaying = false;
364386
ReplayPlaybackControls.Close();
365387

388+
string directory = Path.Combine(MelonEnvironment.UserDataDirectory, "ReplayMod", "TempReplayVoices");
389+
if (Directory.Exists(directory))
390+
Directory.Delete(directory, true);
391+
366392
UpdateReplayCameraPOV(Main.LocalPlayer);
367393
TogglePlayback(true, ignoreIsPlaying: true);
368394
SetPlaybackSpeed(1f);
@@ -1524,6 +1550,11 @@ public class Clone : MonoBehaviour
15241550
public PlayerMovement pm;
15251551
public PlayerPoseSystem ps;
15261552

1553+
public List<VoiceTrack> VoiceTracks = new();
1554+
public AudioSource VoiceSource;
1555+
1556+
VoiceTrack currentTrack;
1557+
15271558
public void ApplyInterpolatedPose(PlayerState a, PlayerState b, float t)
15281559
{
15291560
VRRig.transform.localPosition = Vector3.Lerp(a.VRRigPos, b.VRRigPos, t);
@@ -1559,6 +1590,12 @@ public void Update()
15591590
ps = Controller.PlayerPoseSystem;
15601591
}
15611592

1593+
if (VoiceSource == null)
1594+
{
1595+
VoiceSource = Controller.PlayerVoiceSystem.GetComponent<AudioSource>();
1596+
VoiceSource.spatialBlend = 1f;
1597+
}
1598+
15621599
int state;
15631600

15641601
if (pm.IsGrounded())
@@ -1570,6 +1607,53 @@ public void Update()
15701607

15711608
if (Main.instance.CloseHandsOnPose.Value)
15721609
pa.animator.SetBool(PoseFistsActiveHash, ps.IsDoingAnyPose());
1610+
1611+
float replayTime = Main.Playback.elapsedPlaybackTime;
1612+
1613+
VoiceTrack active = null;
1614+
1615+
foreach (var t in VoiceTracks)
1616+
{
1617+
if (t.Clip == null) continue;
1618+
1619+
float end = t.StartTime + (t.Clip.samples / (float)t.Clip.frequency);
1620+
1621+
if (replayTime >= t.StartTime && replayTime < end)
1622+
{
1623+
active = t;
1624+
break;
1625+
}
1626+
}
1627+
1628+
if (active != currentTrack)
1629+
{
1630+
VoiceSource.Stop();
1631+
currentTrack = active;
1632+
1633+
if (active != null)
1634+
{
1635+
VoiceSource.clip = active.Clip;
1636+
VoiceSource.time = replayTime - active.StartTime;
1637+
VoiceSource.Play();
1638+
}
1639+
} else if (currentTrack != null)
1640+
{
1641+
float localTime = replayTime - currentTrack.StartTime;
1642+
1643+
if (Abs(VoiceSource.time - localTime) > 0.1f)
1644+
VoiceSource.time = localTime;
1645+
1646+
VoiceSource.pitch = Main.Playback.playbackSpeed;
1647+
1648+
if (!VoiceSource.isPlaying)
1649+
VoiceSource.Play();
1650+
}
1651+
}
1652+
1653+
public class VoiceTrack
1654+
{
1655+
public float StartTime;
1656+
public AudioClip Clip;
15731657
}
15741658
}
15751659

0 commit comments

Comments
 (0)