1
0
mirror of https://github.com/misternebula/quantum-space-buddies.git synced 2025-02-13 21:40:50 +00:00

Merge branch 'dev' into pr/649

This commit is contained in:
_nebula 2023-11-12 15:17:32 +00:00
commit 7280e0d08a
25 changed files with 341 additions and 32 deletions

@ -29,7 +29,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mirror", "Mirror", "{851AB4
Mirror\Telepathy.dll = Mirror\Telepathy.dll
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "APITestMod", "APITestMod\APITestMod.csproj", "{0A10143E-6C00-409B-B3A5-C54C1B01599D}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "APITestMod", "APITestMod\APITestMod.csproj", "{0A10143E-6C00-409B-B3A5-C54C1B01599D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QSBPatcher", "QSBPatcher\QSBPatcher.csproj", "{CA4CBA2B-54D5-4C4B-9B51-957BC6D77D6B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -57,6 +59,10 @@ Global
{0A10143E-6C00-409B-B3A5-C54C1B01599D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0A10143E-6C00-409B-B3A5-C54C1B01599D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0A10143E-6C00-409B-B3A5-C54C1B01599D}.Release|Any CPU.Build.0 = Release|Any CPU
{CA4CBA2B-54D5-4C4B-9B51-957BC6D77D6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CA4CBA2B-54D5-4C4B-9B51-957BC6D77D6B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CA4CBA2B-54D5-4C4B-9B51-957BC6D77D6B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CA4CBA2B-54D5-4C4B-9B51-957BC6D77D6B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

@ -10,6 +10,7 @@
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/CASE_BLOCK_BRACES/@EntryValue">NEXT_LINE_SHIFTED_2</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=API/@EntryIndexedValue">API</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=ID/@EntryIndexedValue">ID</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=OW/@EntryIndexedValue">OW</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=QSB/@EntryIndexedValue">QSB</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=UWP/@EntryIndexedValue">UWP</s:String>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>

@ -37,6 +37,19 @@ public interface IQSBAPI
/// <param name="playerID">The ID of the player you want the name of.</param>
string GetPlayerName(uint playerID);
/// <summary>
/// Returns the position of a given player.
/// This position is in world space, where (0, 0, 0) is roughly where the local player is located.
/// </summary>
/// <param name="playerID">The ID of the player you want the position of.</param>
Vector3 GetPlayerPosition(uint playerID);
/// <summary>
/// Returns true if a given player has fully loaded into the game. If the local player is still loading into the game, this will return false.
/// </summary>
/// <param name="playerID">The ID of the player.</param>
bool GetPlayerReady(uint playerID);
/// <summary>
/// Returns the list of IDs of all connected players.
///

@ -25,6 +25,13 @@ public class QSBAPI : IQSBAPI
public uint GetLocalPlayerID() => QSBPlayerManager.LocalPlayerId;
public string GetPlayerName(uint playerId) => QSBPlayerManager.GetPlayer(playerId).Name;
public Vector3 GetPlayerPosition(uint playerId) => QSBPlayerManager.GetPlayer(playerId).Body.transform.position;
public bool GetPlayerReady(uint playerId)
{
var player = QSBPlayerManager.GetPlayer(playerId);
return player.IsReady && player.Body != null;
}
public uint[] GetPlayerIDs() => QSBPlayerManager.PlayerList.Select(x => x.PlayerId).ToArray();
public UnityEvent<uint> OnPlayerJoin() => QSBAPIEvents.OnPlayerJoinEvent;

@ -0,0 +1,95 @@
using OWML.Common;
using QSB.PlayerBodySetup.Remote;
using QSB.Utility;
using UnityEngine;
namespace QSB.Animation.Player;
[UsedInUnityProject]
public class HelmetAnimator : MonoBehaviour
{
public Transform FakeHelmet;
public Transform FakeHead;
public GameObject SuitGroup;
private QSBDitheringAnimator _fakeHelmetDitheringAnimator;
private const float ANIM_TIME = 0.5f;
private bool _isPuttingOnHelmet;
private bool _isTakingOffHelmet;
public void Start()
{
_fakeHelmetDitheringAnimator = FakeHelmet.GetComponent<QSBDitheringAnimator>();
FakeHead.gameObject.SetActive(false);
}
public void RemoveHelmet()
{
if (!SuitGroup.activeSelf)
{
DebugLog.DebugWrite($"Trying to remove helmet when player is not wearing suit!", MessageType.Error);
return;
}
_fakeHelmetDitheringAnimator.SetVisible(true);
FakeHelmet.gameObject.SetActive(true);
FakeHead.gameObject.SetActive(true);
_fakeHelmetDitheringAnimator.SetVisible(false, ANIM_TIME);
_isTakingOffHelmet = true;
}
public void PutOnHelmet()
{
if (!SuitGroup.activeSelf)
{
DebugLog.DebugWrite($"Trying to put on helmet when player is not wearing suit!", MessageType.Error);
return;
}
_fakeHelmetDitheringAnimator.SetVisible(false);
FakeHead.gameObject.SetActive(true);
FakeHelmet.gameObject.SetActive(true);
_fakeHelmetDitheringAnimator.SetVisible(true, ANIM_TIME);
_isPuttingOnHelmet = true;
}
public void SetHelmetInstant(bool helmetOn)
{
if (helmetOn)
{
FakeHelmet.gameObject.SetActive(true);
_fakeHelmetDitheringAnimator.SetVisible(true);
FakeHead.gameObject.SetActive(false);
}
else
{
_fakeHelmetDitheringAnimator.SetVisible(false);
FakeHelmet.gameObject.SetActive(false);
if (!SuitGroup.activeSelf)
{
FakeHead.gameObject.SetActive(false);
}
}
}
private void Update()
{
if (_isPuttingOnHelmet && _fakeHelmetDitheringAnimator.FullyVisible)
{
_isPuttingOnHelmet = false;
FakeHead.gameObject.SetActive(false);
}
if (_isTakingOffHelmet && _fakeHelmetDitheringAnimator.FullyInvisible)
{
FakeHelmet.gameObject.SetActive(false);
if (!SuitGroup.activeSelf)
{
FakeHead.gameObject.SetActive(false);
}
}
}
}

@ -0,0 +1,43 @@
using QSB.Messaging;
using QSB.Player.TransformSync;
using QSB.WorldSync;
using QSB.Player;
namespace QSB.Animation.Player.Messages;
public class PlayerHelmetMessage : QSBMessage<bool>
{
static PlayerHelmetMessage()
{
GlobalMessenger.AddListener(OWEvents.PutOnHelmet, () => Handle(true));
GlobalMessenger.AddListener(OWEvents.RemoveHelmet, () => Handle(false));
}
private static void Handle(bool on)
{
if (PlayerTransformSync.LocalInstance)
{
new PlayerHelmetMessage(on).Send();
}
}
public PlayerHelmetMessage(bool on) : base(on) { }
public override bool ShouldReceive => QSBWorldSync.AllObjectsReady;
public override void OnReceiveRemote()
{
var player = QSBPlayerManager.GetPlayer(From);
var animator = player.HelmetAnimator;
if (Data)
{
animator.PutOnHelmet();
player.AudioController.PlayWearHelmet();
}
else
{
animator.RemoveHelmet();
player.AudioController.PlayRemoveHelmet();
}
}
}

Binary file not shown.

Binary file not shown.

BIN
QSB/AssetBundles/qsb_skins Normal file

Binary file not shown.

@ -12,6 +12,7 @@ public class QSBPlayerAudioController : MonoBehaviour
public OWAudioSource _damageAudioSource;
private AudioManager _audioManager;
private float _playWearHelmetTime;
public void Start()
{
@ -30,6 +31,15 @@ public class QSBPlayerAudioController : MonoBehaviour
damageAudio.SetActive(true);
}
private void Update()
{
if (Time.time > this._playWearHelmetTime)
{
enabled = false;
PlayOneShot(global::AudioType.PlayerSuitWearHelmet);
}
}
public void PlayEquipTool()
=> _oneShotExternalSource?.PlayOneShot(AudioType.ToolTranslatorEquip);
@ -48,6 +58,18 @@ public class QSBPlayerAudioController : MonoBehaviour
public void PlayRemoveSuit()
=> PlayOneShot(AudioType.PlayerSuitRemoveSuit);
public void PlayRemoveHelmet()
{
enabled = false;
PlayOneShot(AudioType.PlayerSuitRemoveHelmet);
}
public void PlayWearHelmet()
{
enabled = true;
_playWearHelmetTime = Time.time + 0.4f;
}
public void PlayOneShot(AudioType audioType, float pitch = 1f, float volume = 1f)
{
if (_oneShotExternalSource)

@ -1,4 +1,5 @@
using System.Collections.Generic;
using OWML.Common;
using QSB.Utility;
using UnityEngine;
@ -9,13 +10,26 @@ public class BodyCustomizer : MonoBehaviour, IAddComponentOnStart
private Dictionary<string, (Texture2D albedo, Texture2D normal)> skinMap = new();
private Dictionary<string, Texture2D> jetpackMap = new();
public AssetBundle SkinsBundle { get; private set; }
public static BodyCustomizer Instance { get; private set; }
private void Start()
{
Instance = this;
}
skinMap.Add("Default", (QSBCore.BigBundle.LoadAsset<Texture2D>("Assets/GameAssets/Texture2D/Traveller_HEA_Player_Skin_d.png"), QSBCore.BigBundle.LoadAsset<Texture2D>("Assets/GameAssets/Texture2D/Traveller_HEA_Player_Skin_n.png")));
public void OnBundleLoaded(AssetBundle bundle)
{
SkinsBundle = bundle;
LoadAssets();
}
private void LoadAssets()
{
DebugLog.DebugWrite($"Loading skin assets...", MessageType.Info);
skinMap.Add("Default", (SkinsBundle.LoadAsset<Texture2D>("Assets/GameAssets/Texture2D/Traveller_HEA_Player_Skin_d.png"), SkinsBundle.LoadAsset<Texture2D>("Assets/GameAssets/Texture2D/Traveller_HEA_Player_Skin_n.png")));
skinMap.Add("Type 1", LoadSkin("Type 1"));
skinMap.Add("Type 2", LoadSkin("Type 2"));
skinMap.Add("Type 3", LoadSkin("Type 3"));
@ -34,7 +48,7 @@ public class BodyCustomizer : MonoBehaviour, IAddComponentOnStart
skinMap.Add("Type 16", LoadSkin("Type 16"));
skinMap.Add("Type 17", LoadSkin("Type 17"));
jetpackMap.Add("Orange", QSBCore.BigBundle.LoadAsset<Texture2D>("Assets/GameAssets/Texture2D/Props_HEA_Jetpack_d.png"));
jetpackMap.Add("Orange", SkinsBundle.LoadAsset<Texture2D>("Assets/GameAssets/Texture2D/Props_HEA_Jetpack_d.png"));
jetpackMap.Add("Yellow", LoadJetpack("yellow"));
jetpackMap.Add("Red", LoadJetpack("red"));
jetpackMap.Add("Pink", LoadJetpack("pink"));
@ -48,22 +62,26 @@ public class BodyCustomizer : MonoBehaviour, IAddComponentOnStart
private (Texture2D d, Texture2D n) LoadSkin(string skinName)
{
var number = skinName.Replace($"Type ", "");
return (QSBCore.BigBundle.LoadAsset<Texture2D>($"Assets/GameAssets/Texture2D/Skin Variations/{number}d.png"), QSBCore.BigBundle.LoadAsset<Texture2D>($"Assets/GameAssets/Texture2D/Skin Variations/{number}n.png"));
return (SkinsBundle.LoadAsset<Texture2D>($"Assets/GameAssets/Texture2D/Skin Variations/{number}d.png"), SkinsBundle.LoadAsset<Texture2D>($"Assets/GameAssets/Texture2D/Skin Variations/{number}n.png"));
}
private Texture2D LoadJetpack(string jetpackName)
{
return QSBCore.BigBundle.LoadAsset<Texture2D>($"Assets/GameAssets/Texture2D/Jetpack Variations/{jetpackName}.png");
return SkinsBundle.LoadAsset<Texture2D>($"Assets/GameAssets/Texture2D/Jetpack Variations/{jetpackName}.png");
}
public void CustomizeRemoteBody(GameObject REMOTE_Traveller_HEA_Player_v2, string skinType, string jetpackType)
public void CustomizeRemoteBody(GameObject REMOTE_Traveller_HEA_Player_v2, GameObject fakeHead, string skinType, string jetpackType)
{
var headMesh = REMOTE_Traveller_HEA_Player_v2.transform.Find("player_mesh_noSuit:Traveller_HEA_Player/player_mesh_noSuit:Player_Head");
var skinMaterial = headMesh.GetComponent<SkinnedMeshRenderer>().material;
var skinMaterial = headMesh.GetComponent<SkinnedMeshRenderer>().material;
skinMaterial.SetTexture("_MainTex", skinMap[skinType].albedo);
skinMaterial.SetTexture("_BumpMap", skinMap[skinType].normal);
var fakeHeadMaterial = fakeHead.GetComponent<SkinnedMeshRenderer>().material;
fakeHeadMaterial.SetTexture("_MainTex", skinMap[skinType].albedo);
fakeHeadMaterial.SetTexture("_BumpMap", skinMap[skinType].normal);
var jetpackMesh = REMOTE_Traveller_HEA_Player_v2.transform.Find("Traveller_Mesh_v01:Traveller_Geo/Traveller_Mesh_v01:Props_HEA_Jetpack");
var jetpackMaterial = jetpackMesh.GetComponent<SkinnedMeshRenderer>().material;

@ -41,4 +41,6 @@ public static class OWEvents
public const string ProbeExitQuantumMoon = nameof(ProbeExitQuantumMoon);
public const string EnterCloak = nameof(EnterCloak);
public const string ExitCloak = nameof(ExitCloak);
public const string PutOnHelmet = nameof(PutOnHelmet);
public const string RemoveHelmet = nameof(RemoveHelmet);
}

@ -87,10 +87,10 @@ public static class QSBPatchManager
}
OnPatchType?.SafeInvoke(type);
DebugLog.DebugWrite($"Patch block {Enum.GetName(typeof(QSBPatchTypes), type)}", MessageType.Info);
//DebugLog.DebugWrite($"Patch block {Enum.GetName(typeof(QSBPatchTypes), type)}", MessageType.Info);
foreach (var patch in _patchList.Where(x => x.Type == type && x.PatchVendor.HasFlag(QSBCore.GameVendor)))
{
DebugLog.DebugWrite($" - Patching in {patch.GetType().Name}", MessageType.Info);
//DebugLog.DebugWrite($" - Patching in {patch.GetType().Name}", MessageType.Info);
try
{
patch.DoPatches(TypeToInstance[type]);

@ -13,6 +13,7 @@ public class PlayerInformationMessage : QSBMessage
private bool IsReady;
private bool FlashlightActive;
private bool SuitedUp;
private bool HelmetOn;
private bool LocalProbeLauncherEquipped;
private bool SignalscopeEquipped;
private bool TranslatorEquipped;
@ -31,6 +32,7 @@ public class PlayerInformationMessage : QSBMessage
IsReady = player.IsReady;
FlashlightActive = player.FlashlightActive;
SuitedUp = player.SuitedUp;
HelmetOn = Locator.GetPlayerSuit() != null && Locator.GetPlayerSuit().IsWearingHelmet();
LocalProbeLauncherEquipped = player.LocalProbeLauncherEquipped;
SignalscopeEquipped = player.SignalscopeEquipped;
TranslatorEquipped = player.TranslatorEquipped;
@ -50,6 +52,7 @@ public class PlayerInformationMessage : QSBMessage
writer.Write(IsReady);
writer.Write(FlashlightActive);
writer.Write(SuitedUp);
writer.Write(HelmetOn);
writer.Write(LocalProbeLauncherEquipped);
writer.Write(SignalscopeEquipped);
writer.Write(TranslatorEquipped);
@ -69,6 +72,7 @@ public class PlayerInformationMessage : QSBMessage
IsReady = reader.Read<bool>();
FlashlightActive = reader.Read<bool>();
SuitedUp = reader.Read<bool>();
HelmetOn = reader.Read<bool>();
LocalProbeLauncherEquipped = reader.Read<bool>();
SignalscopeEquipped = reader.Read<bool>();
TranslatorEquipped = reader.Read<bool>();
@ -91,22 +95,23 @@ public class PlayerInformationMessage : QSBMessage
player.IsReady = IsReady;
player.FlashlightActive = FlashlightActive;
player.SuitedUp = SuitedUp;
player.LocalProbeLauncherEquipped = LocalProbeLauncherEquipped;
player.SignalscopeEquipped = SignalscopeEquipped;
player.TranslatorEquipped = TranslatorEquipped;
player.ProbeActive = ProbeActive;
player.IsInShip = IsInShip;
if (QSBPlayerManager.LocalPlayer.IsReady && player.IsReady)
Delay.RunWhen(() => player.IsReady && QSBPlayerManager.LocalPlayer.IsReady, () =>
{
player.UpdateObjectsFromStates();
player.HelmetAnimator.SetHelmetInstant(HelmetOn);
var REMOTE_Traveller_HEA_Player_v2 = player.Body.transform.Find("REMOTE_Traveller_HEA_Player_v2");
BodyCustomization.BodyCustomizer.Instance.CustomizeRemoteBody(REMOTE_Traveller_HEA_Player_v2.gameObject, SkinType, JetpackType);
}
BodyCustomization.BodyCustomizer.Instance.CustomizeRemoteBody(REMOTE_Traveller_HEA_Player_v2.gameObject, player.HelmetAnimator.FakeHead.gameObject, SkinType, JetpackType);
Delay.RunWhen(
() => player.Camera != null,
() => player.Camera.fieldOfView = FieldOfView);
player.Camera.fieldOfView = FieldOfView;
});
player.State = ClientState;

@ -12,4 +12,5 @@ public partial class PlayerInfo
internal QSBDitheringAnimator _ditheringAnimator;
public DreamWorldSpawnAnimator DreamWorldSpawnAnimator { get; set; }
public RemotePlayerFluidDetector FluidDetector { get; set; }
public HelmetAnimator HelmetAnimator { get; set; }
}

@ -35,6 +35,12 @@ public class PlayerTransformSync : SectoredTransformSync
{
var player = new PlayerInfo(this);
QSBPlayerManager.PlayerList.SafeAdd(player);
if (isLocalPlayer)
{
LocalInstance = this;
}
base.OnStartClient();
QSBPatch.Remote = !isLocalPlayer;
QSBPlayerManager.OnAddPlayer?.SafeInvoke(Player);

@ -59,6 +59,10 @@ public class QSBDitheringAnimator : MonoBehaviour
private void UpdateRenderers()
{
_renderers ??= GetComponentsInChildren<Renderer>(true)
.Select(x => (x.gameObject.GetAddComponent<OWRenderer>(), x.shadowCastingMode != ShadowCastingMode.Off))
.ToArray();
foreach (var (renderer, updateShadows) in _renderers)
{
if (renderer == null)

@ -1,4 +1,5 @@
using QSB.Audio;
using QSB.Animation.Player;
using QSB.Audio;
using QSB.EchoesOfTheEye.LightSensorSync;
using QSB.Player;
using QSB.RoastingSync;
@ -54,6 +55,7 @@ public static class RemotePlayerCreation
player.ThrusterLightTracker = player.Body.GetComponentInChildren<ThrusterLightTracker>();
player.FluidDetector = REMOTE_PlayerDetector.GetComponent<RemotePlayerFluidDetector>();
player.RulesetDetector = REMOTE_PlayerDetector.GetComponent<RemotePlayerRulesetDetector>();
player.HelmetAnimator = REMOTE_Traveller_HEA_Player_v2.GetComponent<HelmetAnimator>();
player.AnimationSync.InitRemote(REMOTE_Traveller_HEA_Player_v2.transform);

@ -79,6 +79,7 @@
<Reference Include="..\Lib\*.dll" />
<ProjectReference Include="..\FizzySteamworks\FizzySteamworks.csproj" />
<ProjectReference Include="..\SteamRerouter\SteamRerouter.csproj" />
<ProjectReference Include="..\QSBPatcher\QSBPatcher.csproj" />
<ProjectReference Include="..\MirrorWeaver\MirrorWeaver.csproj" ReferenceOutputAssembly="false" />
</ItemGroup>
</Project>

@ -14,10 +14,12 @@ using QSB.WorldSync;
using Steamworks;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using QSB.API;
using QSB.BodyCustomization;
using QSB.Player.Messages;
using UnityEngine;
using UnityEngine.InputSystem;
@ -54,7 +56,6 @@ public class QSBCore : ModBehaviour
public static AssetBundle ConversationAssetBundle { get; private set; }
public static AssetBundle DebugAssetBundle { get; private set; }
public static AssetBundle HUDAssetBundle { get; private set; }
public static AssetBundle BigBundle { get; private set; }
public static bool IsHost => NetworkServer.active;
public static bool IsInMultiplayer;
public static string QSBVersion => Helper.Manifest.Version;
@ -78,8 +79,6 @@ public class QSBCore : ModBehaviour
public static IMenuAPI MenuApi { get; private set; }
public static DebugSettings DebugSettings { get; private set; } = new();
public const string NEW_HORIZONS = "xen.NewHorizons";
public const string NEW_HORIZONS_COMPAT = "xen.NHQSBCompat";
public static readonly string[] IncompatibleMods =
{
@ -92,6 +91,8 @@ public class QSBCore : ModBehaviour
"PacificEngine.OW_Randomizer",
};
public static event Action OnSkinsBundleLoaded;
public override object GetApi() => new QSBAPI();
private static void DetermineGameVendor()
@ -161,7 +162,7 @@ public class QSBCore : ModBehaviour
if (!SteamAPI.Init())
{
DebugLog.ToConsole($"FATAL - SteamAPI.Init() failed. Refer to Valve's documentation.", MessageType.Fatal);
DebugLog.ToConsole($"FATAL - SteamAPI.Init() failed. Do you have Steam open, and are you logged in?", MessageType.Fatal);
return;
}
@ -258,18 +259,15 @@ public class QSBCore : ModBehaviour
MenuApi = ModHelper.Interaction.TryGetModApi<IMenuAPI>(ModHelper.Manifest.Dependencies[0]);
DebugLog.DebugWrite("loading qsb_network_big bundle", MessageType.Info);
var path = Path.Combine(ModHelper.Manifest.ModFolderPath, "AssetBundles/qsb_network_big");
var request = AssetBundle.LoadFromFileAsync(path);
request.completed += _ => DebugLog.DebugWrite("qsb_network_big bundle loaded", MessageType.Success);
BigBundle = request.assetBundle;
LoadBundleAsync("qsb_network_big");
LoadBundleAsync("qsb_skins", request => BodyCustomizer.Instance.OnBundleLoaded(request.assetBundle));
NetworkAssetBundle = Helper.Assets.LoadBundle("AssetBundles/qsb_network");
ConversationAssetBundle = Helper.Assets.LoadBundle("AssetBundles/qsb_conversation");
DebugAssetBundle = Helper.Assets.LoadBundle("AssetBundles/qsb_debug");
HUDAssetBundle = Helper.Assets.LoadBundle("AssetBundles/qsb_hud");
NetworkAssetBundle = LoadBundle("qsb_network");
ConversationAssetBundle = LoadBundle("qsb_conversation");
DebugAssetBundle = LoadBundle("qsb_debug");
HUDAssetBundle = LoadBundle("qsb_hud");
if (NetworkAssetBundle == null || ConversationAssetBundle == null || DebugAssetBundle == null)
if (NetworkAssetBundle == null || ConversationAssetBundle == null || DebugAssetBundle == null || HUDAssetBundle == null)
{
DebugLog.ToConsole($"FATAL - An assetbundle is missing! Re-install mod or contact devs.", MessageType.Fatal);
return;
@ -287,6 +285,31 @@ public class QSBCore : ModBehaviour
QSBPatchManager.OnUnpatchType += OnUnpatchType;
}
private AssetBundle LoadBundle(string name)
{
var timer = new Stopwatch();
timer.Start();
var ret = Helper.Assets.LoadBundle($"AssetBundles/{name}");
timer.Stop();
DebugLog.ToConsole($"Bundle {name} loaded in {timer.ElapsedMilliseconds} ms!", MessageType.Success);
return ret;
}
private void LoadBundleAsync(string bundleName, Action<AssetBundleCreateRequest> runOnLoaded = null)
{
DebugLog.DebugWrite($"Loading {bundleName}...", MessageType.Info);
var timer = new Stopwatch();
timer.Start();
var path = Path.Combine(ModHelper.Manifest.ModFolderPath, $"AssetBundles/{bundleName}");
var request = AssetBundle.LoadFromFileAsync(path);
request.completed += _ =>
{
timer.Stop();
DebugLog.ToConsole($"Bundle {bundleName} loaded in {timer.ElapsedMilliseconds} ms!", MessageType.Success);
runOnLoaded?.Invoke(request);
};
}
private static void OnPatchType(QSBPatchTypes type)
{
if (type == QSBPatchTypes.OnClientConnect)

@ -12,5 +12,6 @@
"owmlVersion": "2.9.8",
"dependencies": [ "_nebula.MenuFramework", "JohnCorby.VanillaFix" ],
"pathsToPreserve": [ "debugsettings.json" ],
"requireLatestVersion": true
"requireLatestVersion": true,
"patcher": "QSBPatcher.exe"
}

45
QSBPatcher/QSBPatcher.cs Normal file

@ -0,0 +1,45 @@
using System;
using System.IO;
namespace QSBPatcher;
public static class QSBPatcher
{
public static void Main(string[] args)
{
var basePath = args.Length > 0 ? args[0] : ".";
var gamePath = AppDomain.CurrentDomain.BaseDirectory;
var steamDLLPath = Path.Combine(basePath, "com.rlabrecque.steamworks.net.dll");
var managedPath = Path.Combine(gamePath, GetDataPath(gamePath), "Managed");
File.Copy(steamDLLPath, Path.Combine(managedPath, "com.rlabrecque.steamworks.net.dll"), true);
}
private static string GetDataDirectoryName()
{
var gamePath = AppDomain.CurrentDomain.BaseDirectory;
return $"{GetExecutableName(gamePath)}_Data";
}
private static string GetDataPath(string gamePath)
{
return Path.Combine(gamePath, $"{GetDataDirectoryName()}");
}
private static string GetExecutableName(string gamePath)
{
var executableNames = new[] { "Outer Wilds.exe", "OuterWilds.exe" };
foreach (var executableName in executableNames)
{
var executablePath = Path.Combine(gamePath, executableName);
if (File.Exists(executablePath))
{
return Path.GetFileNameWithoutExtension(executablePath);
}
}
throw new FileNotFoundException($"Outer Wilds exe file not found in {gamePath}");
}
}

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<AssemblyTitle>QSB Patcher</AssemblyTitle>
<Product>QSB Patcher</Product>
<Title>QSB Patcher</Title>
<Description>Copies steamworks into the game for non-steam vendors</Description>
<Authors>William Corby, Henry Pointer</Authors>
<Company>William Corby, Henry Pointer</Company>
<Copyright>Copyright © William Corby, Henry Pointer 2022-2023</Copyright>
</PropertyGroup>
</Project>

@ -34,6 +34,7 @@ Spoilers within!
#### Connecting to a server
- Make sure to have Steam open and logged in.
- On the title screen, click the option `CONNECT TO MULTIPLAYER`.
- Enter the Steam ID of the person you are trying to connect to.
- If "Use KCP Transport" is enabled, enter the public IP address of the person instead.
@ -41,6 +42,7 @@ Spoilers within!
#### Hosting a server
- Make sure to have Steam open and logged in.
- On the title screen, click the option `OPEN TO MULTIPLAYER`.
- Share your Steam ID with the people who want to connect.
- If "Use KCP Transport" is enabled, share your public IP address instead. This can be found on websites like https://www.whatismyip.com/.

@ -53,7 +53,7 @@ public static class Program
if (!SteamAPI.Init())
{
LogError($"FATAL - SteamAPI.Init() failed. Refer to Valve's documentation.");
LogError($"FATAL - SteamAPI.Init() failed. Do you have Steam open, and are you logged in?");
return -1;
}