mirror of
https://github.com/misternebula/quantum-space-buddies.git
synced 2025-01-16 16:15:24 +00:00
commit
688b239043
@ -19,7 +19,7 @@ namespace EpicTransport {
|
||||
private event Action OnConnected;
|
||||
public event Action OnDisconnected;
|
||||
// CHANGED
|
||||
private Action<string> SetTransportError;
|
||||
private event Action<TransportError, string> OnReceivedError;
|
||||
|
||||
private TimeSpan ConnectionTimeout;
|
||||
|
||||
@ -43,7 +43,7 @@ namespace EpicTransport {
|
||||
c.OnDisconnected += () => transport.OnClientDisconnected.Invoke();
|
||||
c.OnReceivedData += (data, channel) => transport.OnClientDataReceived.Invoke(new ArraySegment<byte>(data), channel);
|
||||
// CHANGED
|
||||
c.SetTransportError = transport.SetTransportError;
|
||||
c.OnReceivedError += (error, reason) => transport.OnClientError?.Invoke(error, reason);
|
||||
|
||||
return c;
|
||||
}
|
||||
@ -64,7 +64,7 @@ namespace EpicTransport {
|
||||
|
||||
if (await Task.WhenAny(connectedCompleteTask, Task.Delay(ConnectionTimeout/*, cancelToken.Token*/)) != connectedCompleteTask) {
|
||||
// CHANGED
|
||||
SetTransportError($"Connection to {host} timed out.");
|
||||
OnReceivedError?.Invoke(TransportError.Timeout, $"Connection to {host} timed out.");
|
||||
Debug.LogError($"Connection to {host} timed out.");
|
||||
OnConnected -= SetConnectedComplete;
|
||||
OnConnectionFailed(hostProductId);
|
||||
@ -73,13 +73,13 @@ namespace EpicTransport {
|
||||
OnConnected -= SetConnectedComplete;
|
||||
} catch (FormatException) {
|
||||
// CHANGED
|
||||
SetTransportError("Connection string was not in the right format. Did you enter a ProductId?");
|
||||
OnReceivedError?.Invoke(TransportError.DnsResolve, "Connection string was not in the right format. Did you enter a ProductId?");
|
||||
Debug.LogError($"Connection string was not in the right format. Did you enter a ProductId?");
|
||||
Error = true;
|
||||
OnConnectionFailed(hostProductId);
|
||||
} catch (Exception ex) {
|
||||
// CHANGED
|
||||
SetTransportError(ex.Message);
|
||||
OnReceivedError?.Invoke(TransportError.Unexpected, ex.Message);
|
||||
Debug.LogError(ex.Message);
|
||||
Error = true;
|
||||
OnConnectionFailed(hostProductId);
|
||||
@ -158,7 +158,7 @@ namespace EpicTransport {
|
||||
break;
|
||||
case InternalMessages.DISCONNECT:
|
||||
// CHANGED
|
||||
SetTransportError("host disconnected");
|
||||
OnReceivedError?.Invoke(TransportError.ConnectionClosed, "host disconnected");
|
||||
Connected = false;
|
||||
Debug.Log("Disconnected.");
|
||||
|
||||
|
@ -40,9 +40,6 @@ namespace EpicTransport {
|
||||
public ProductUserId productUserId;
|
||||
|
||||
private int packetId = 0;
|
||||
|
||||
// CHANGED
|
||||
public Action<string> SetTransportError;
|
||||
|
||||
private void Awake() {
|
||||
Debug.Assert(Channels != null && Channels.Length > 0, "No channel configured for EOS Transport.");
|
||||
|
@ -1,5 +1,6 @@
|
||||
using Epic.OnlineServices;
|
||||
using Epic.OnlineServices.P2P;
|
||||
using Mirror;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
@ -9,7 +10,8 @@ namespace EpicTransport {
|
||||
private event Action<int> OnConnected;
|
||||
private event Action<int, byte[], int> OnReceivedData;
|
||||
private event Action<int> OnDisconnected;
|
||||
private event Action<int, Exception> OnReceivedError;
|
||||
// CHANGED
|
||||
private event Action<int, TransportError, string> OnReceivedError;
|
||||
|
||||
private BidirectionalDictionary<ProductUserId, int> epicToMirrorIds;
|
||||
private Dictionary<ProductUserId, SocketId> epicToSocketIds;
|
||||
@ -23,7 +25,7 @@ namespace EpicTransport {
|
||||
s.OnDisconnected += (id) => transport.OnServerDisconnected.Invoke(id);
|
||||
s.OnReceivedData += (id, data, channel) => transport.OnServerDataReceived.Invoke(id, new ArraySegment<byte>(data), channel);
|
||||
// CHANGED
|
||||
s.OnReceivedError += (id, exception) => transport.OnServerError?.Invoke(id, Mirror.TransportError.Unexpected, exception.ToString());
|
||||
s.OnReceivedError += (id, error, reason) => transport.OnServerError?.Invoke(id, error, reason);
|
||||
|
||||
if (!EOSSDKComponent.Initialized) {
|
||||
Debug.LogError("EOS not initialized.");
|
||||
@ -90,7 +92,8 @@ namespace EpicTransport {
|
||||
epicToSocketIds.Remove(clientUserId);
|
||||
Debug.Log($"Client with Product User ID {clientUserId} disconnected.");
|
||||
} else {
|
||||
OnReceivedError.Invoke(-1, new Exception("ERROR Unknown Product User ID"));
|
||||
// CHANGED
|
||||
OnReceivedError?.Invoke(-1, TransportError.InvalidReceive, "ERROR Unknown Product User ID");
|
||||
}
|
||||
|
||||
break;
|
||||
@ -116,7 +119,8 @@ namespace EpicTransport {
|
||||
clientUserId.ToString(out productId);
|
||||
|
||||
Debug.LogError("Data received from epic client thats not known " + productId);
|
||||
OnReceivedError.Invoke(-1, new Exception("ERROR Unknown product ID"));
|
||||
// CHANGED
|
||||
OnReceivedError?.Invoke(-1, TransportError.InvalidReceive, "ERROR Unknown product ID");
|
||||
}
|
||||
}
|
||||
|
||||
@ -153,7 +157,8 @@ namespace EpicTransport {
|
||||
Send(userId, socketId, data, (byte)channelId);
|
||||
} else {
|
||||
Debug.LogError("Trying to send on unknown connection: " + connectionId);
|
||||
OnReceivedError.Invoke(connectionId, new Exception("ERROR Unknown Connection"));
|
||||
// CHANGED
|
||||
OnReceivedError?.Invoke(connectionId, TransportError.InvalidSend, "ERROR Unknown Connection");
|
||||
}
|
||||
|
||||
}
|
||||
@ -165,7 +170,8 @@ namespace EpicTransport {
|
||||
return userIdString;
|
||||
} else {
|
||||
Debug.LogError("Trying to get info on unknown connection: " + connectionId);
|
||||
OnReceivedError.Invoke(connectionId, new Exception("ERROR Unknown Connection"));
|
||||
// CHANGED
|
||||
OnReceivedError?.Invoke(connectionId, TransportError.Unexpected, "ERROR Unknown Connection");
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
Binary file not shown.
@ -1,14 +1,26 @@
|
||||
using QSB.ConversationSync.Messages;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using QSB.ConversationSync.Messages;
|
||||
using QSB.Messaging;
|
||||
using QSB.Player;
|
||||
using QSB.Utility;
|
||||
using QSB.WorldSync;
|
||||
using System.Threading;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.ConversationSync.WorldObjects;
|
||||
|
||||
/// <summary>
|
||||
/// BUG: do conversation leave on player leave so other people can actually talk lol
|
||||
/// </summary>
|
||||
public class QSBCharacterDialogueTree : WorldObject<CharacterDialogueTree>
|
||||
{
|
||||
public override async UniTask Init(CancellationToken ct)
|
||||
{
|
||||
QSBPlayerManager.OnRemovePlayer += OnRemovePlayer;
|
||||
}
|
||||
|
||||
public override void OnRemoval()
|
||||
{
|
||||
QSBPlayerManager.OnRemovePlayer -= OnRemovePlayer;
|
||||
}
|
||||
|
||||
public override void SendInitialState(uint to)
|
||||
{
|
||||
var playerId = ConversationManager.Instance.GetPlayerTalkingToTree(AttachedObject);
|
||||
@ -18,4 +30,15 @@ public class QSBCharacterDialogueTree : WorldObject<CharacterDialogueTree>
|
||||
}
|
||||
// TODO: maybe also sync the dialogue box and player box?
|
||||
}
|
||||
|
||||
private void OnRemovePlayer(PlayerInfo player)
|
||||
{
|
||||
if (player.CurrentCharacterDialogueTree == this)
|
||||
{
|
||||
AttachedObject.GetInteractVolume().EnableInteraction();
|
||||
AttachedObject.RaiseEvent(nameof(CharacterDialogueTree.OnEndConversation));
|
||||
Object.Destroy(ConversationManager.Instance.BoxMappings[AttachedObject]);
|
||||
Object.Destroy(player.CurrentDialogueBox);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
using Mirror;
|
||||
using QSB.ClientServerStateSync;
|
||||
using QSB.HUD;
|
||||
using QSB.Messaging;
|
||||
using QSB.Player;
|
||||
using QSB.RespawnSync;
|
||||
@ -40,7 +41,7 @@ public class PlayerDeathMessage : QSBMessage<DeathType>
|
||||
var deathMessage = Necronomicon.GetPhrase(Data, NecronomiconIndex);
|
||||
if (deathMessage != null)
|
||||
{
|
||||
DebugLog.ToAll(string.Format(deathMessage, playerName));
|
||||
MultiplayerHUDManager.Instance.WriteMessage($"<color=brown>{string.Format(deathMessage, playerName)}</color>");
|
||||
}
|
||||
|
||||
RespawnManager.Instance.OnPlayerDeath(player);
|
||||
|
@ -53,6 +53,9 @@ internal class GhostManager : WorldObjectManager
|
||||
_zone2Director._cityGhosts[i].GetWorldObject<QSBGhostBrain>().OnIdentifyIntruder += CustomOnCityGhostsIdentifiedIntruder;
|
||||
}
|
||||
|
||||
// the collision group sector is smaller than the one for ghost light sensors,
|
||||
// so ghosts can see thru walls.
|
||||
// fix this by just changing the collision group sector :P
|
||||
var allCollisionGroups = Resources.FindObjectsOfTypeAll<SectorCollisionGroup>();
|
||||
var city = allCollisionGroups.First(x => x.name == "City");
|
||||
city.SetSector(_zone2Director._sector);
|
||||
|
@ -14,11 +14,6 @@ using System.Threading;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.LightSensorSync.WorldObjects;
|
||||
|
||||
/// <summary>
|
||||
/// BUG: this breaks in zone2.
|
||||
/// the sector it's enabled in is bigger than the sector the zone2 walls are enabled in :(
|
||||
/// maybe this can be fixed by making the collision group use the same sector.
|
||||
/// </summary>
|
||||
internal class QSBLightSensor : AuthWorldObject<SingleLightSensor>
|
||||
{
|
||||
internal bool _locallyIlluminated;
|
||||
|
20
QSB/HUD/Messages/ChatMessage.cs
Normal file
20
QSB/HUD/Messages/ChatMessage.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using QSB.Messaging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace QSB.HUD.Messages;
|
||||
|
||||
internal class ChatMessage : QSBMessage<string>
|
||||
{
|
||||
public ChatMessage(string msg) : base(msg) { }
|
||||
|
||||
public override void OnReceiveLocal() => OnReceiveRemote();
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
MultiplayerHUDManager.Instance.WriteMessage(Data);
|
||||
}
|
||||
}
|
@ -1,12 +1,16 @@
|
||||
using QSB.HUD.Messages;
|
||||
using OWML.Common;
|
||||
using QSB.HUD.Messages;
|
||||
using QSB.Localization;
|
||||
using QSB.Messaging;
|
||||
using QSB.Player;
|
||||
using QSB.ServerSettings;
|
||||
using QSB.Utility;
|
||||
using QSB.WorldSync;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace QSB.HUD;
|
||||
|
||||
@ -15,6 +19,8 @@ internal class MultiplayerHUDManager : MonoBehaviour, IAddComponentOnStart
|
||||
public static MultiplayerHUDManager Instance;
|
||||
|
||||
private Transform _playerList;
|
||||
private Transform _textChat;
|
||||
private InputField _inputField;
|
||||
private Material _markerMaterial;
|
||||
|
||||
public static Sprite UnknownSprite;
|
||||
@ -32,7 +38,7 @@ internal class MultiplayerHUDManager : MonoBehaviour, IAddComponentOnStart
|
||||
public static Sprite Interloper;
|
||||
public static Sprite WhiteHole;
|
||||
|
||||
public static ListStack<HUDIcon> HUDIconStack = new();
|
||||
public static readonly ListStack<HUDIcon> HUDIconStack = new(true);
|
||||
|
||||
private void Start()
|
||||
{
|
||||
@ -59,6 +65,88 @@ internal class MultiplayerHUDManager : MonoBehaviour, IAddComponentOnStart
|
||||
SpaceSprite = QSBCore.HUDAssetBundle.LoadAsset<Sprite>("Assets/MULTIPLAYER_UI/playerbox_space.png");
|
||||
}
|
||||
|
||||
private const int LINE_COUNT = 11;
|
||||
private const int CHAR_COUNT = 41;
|
||||
|
||||
private bool _writingMessage;
|
||||
private readonly string[] _lines = new string[LINE_COUNT];
|
||||
// this should really be a deque, but eh
|
||||
private readonly ListStack<string> _messages = new(false);
|
||||
|
||||
public void WriteMessage(string message)
|
||||
{
|
||||
/* Tricky problem to solve.
|
||||
* - 11 available lines for text to fit onto
|
||||
* - Each line can be max 41 characters
|
||||
* - Newest messages apepear at the bottom, and get pushed up by newer messages.
|
||||
* - Messages can use several lines.
|
||||
*
|
||||
* From newest to oldest message, work out how many lines it needs
|
||||
* and set the lines correctly bottom-up.
|
||||
*/
|
||||
|
||||
_messages.Push(message);
|
||||
|
||||
if (_messages.Count > LINE_COUNT)
|
||||
{
|
||||
_messages.RemoveFirstElementAndShift();
|
||||
}
|
||||
|
||||
var currentLineIndex = 10;
|
||||
|
||||
foreach (var msg in _messages.Reverse())
|
||||
{
|
||||
var characterCount = msg.Length;
|
||||
var linesNeeded = Mathf.CeilToInt((float)characterCount / CHAR_COUNT);
|
||||
var chunk = 0;
|
||||
for (var i = linesNeeded - 1; i >= 0; i--)
|
||||
{
|
||||
if (currentLineIndex - i < 0)
|
||||
{
|
||||
chunk++;
|
||||
continue;
|
||||
}
|
||||
|
||||
var chunkString = string.Concat(msg.Skip(CHAR_COUNT * chunk).Take(CHAR_COUNT));
|
||||
_lines[currentLineIndex - i] = chunkString;
|
||||
chunk++;
|
||||
}
|
||||
|
||||
currentLineIndex -= linesNeeded;
|
||||
|
||||
if (currentLineIndex < 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var finalText = "";
|
||||
foreach (var line in _lines)
|
||||
{
|
||||
if (line == default)
|
||||
{
|
||||
finalText += Environment.NewLine;
|
||||
}
|
||||
else if (line.Length == 42)
|
||||
{
|
||||
finalText += line;
|
||||
}
|
||||
else
|
||||
{
|
||||
finalText += $"{line}{Environment.NewLine}";
|
||||
}
|
||||
}
|
||||
|
||||
_textChat.Find("Messages").Find("Message").GetComponent<Text>().text = finalText;
|
||||
|
||||
if (Locator.GetPlayerSuit().IsWearingHelmet())
|
||||
{
|
||||
var audioController = Locator.GetPlayerAudioController();
|
||||
audioController.PlayNotificationTextScrolling();
|
||||
Delay.RunFramesLater(10, () => audioController.StopNotificationTextScrolling());
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady || _playerList == null)
|
||||
@ -67,6 +155,34 @@ internal class MultiplayerHUDManager : MonoBehaviour, IAddComponentOnStart
|
||||
}
|
||||
|
||||
_playerList.gameObject.SetActive(ServerSettingsManager.ShowExtraHUD);
|
||||
|
||||
var inSuit = Locator.GetPlayerSuit().IsWearingHelmet();
|
||||
|
||||
if (OWInput.IsNewlyPressed(InputLibrary.enter, InputMode.Character) && !_writingMessage && inSuit)
|
||||
{
|
||||
OWInput.ChangeInputMode(InputMode.KeyboardInput);
|
||||
_writingMessage = true;
|
||||
_inputField.ActivateInputField();
|
||||
}
|
||||
|
||||
if (OWInput.IsNewlyPressed(InputLibrary.enter, InputMode.KeyboardInput) && _writingMessage)
|
||||
{
|
||||
OWInput.RestorePreviousInputs();
|
||||
_writingMessage = false;
|
||||
_inputField.DeactivateInputField();
|
||||
|
||||
var message = _inputField.text;
|
||||
_inputField.text = "";
|
||||
message = message.Replace("\n", "").Replace("\r", "");
|
||||
message = $"{QSBPlayerManager.LocalPlayer.Name}: {message}";
|
||||
new ChatMessage(message).Send();
|
||||
}
|
||||
|
||||
if (OWInput.IsNewlyPressed(InputLibrary.escape, InputMode.KeyboardInput) && _writingMessage)
|
||||
{
|
||||
OWInput.RestorePreviousInputs();
|
||||
_writingMessage = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnWakeUp()
|
||||
@ -119,6 +235,14 @@ internal class MultiplayerHUDManager : MonoBehaviour, IAddComponentOnStart
|
||||
HUDIconStack.Push(HUDIcon.TIMBER_HEARTH);
|
||||
|
||||
new PlanetMessage(HUDIcon.TIMBER_HEARTH).Send();
|
||||
|
||||
_textChat = multiplayerGroup.transform.Find("TextChat");
|
||||
var inputFieldGO = _textChat.Find("InputField");
|
||||
_inputField = inputFieldGO.GetComponent<InputField>();
|
||||
_inputField.text = "";
|
||||
_textChat.Find("Messages").Find("Message").GetComponent<Text>().text = "";
|
||||
_lines.Clear();
|
||||
_messages.Clear();
|
||||
}
|
||||
|
||||
public void UpdateMinimapMarkers(Minimap minimap)
|
||||
@ -136,7 +260,7 @@ internal class MultiplayerHUDManager : MonoBehaviour, IAddComponentOnStart
|
||||
{
|
||||
if (player.Body != null)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - {player.PlayerId}'s RulesetDetector is null.", OWML.Common.MessageType.Error);
|
||||
DebugLog.ToConsole($"Error - {player.PlayerId}'s RulesetDetector is null.", MessageType.Error);
|
||||
}
|
||||
|
||||
continue;
|
||||
@ -157,12 +281,40 @@ internal class MultiplayerHUDManager : MonoBehaviour, IAddComponentOnStart
|
||||
{
|
||||
player.MinimapPlayerMarker.localPosition = GetLocalMapPosition(player, minimap);
|
||||
player.MinimapPlayerMarker.LookAt(minimap._globeMeshTransform, minimap._globeMeshTransform.up);
|
||||
player.MinimapPlayerMarker.GetComponent<MeshRenderer>().enabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
player.MinimapPlayerMarker.localPosition = Vector3.zero;
|
||||
player.MinimapPlayerMarker.localRotation = Quaternion.identity;
|
||||
}
|
||||
player.MinimapPlayerMarker.GetComponent<MeshRenderer>().enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void HideMinimap(Minimap minimap)
|
||||
{
|
||||
foreach (var player in QSBPlayerManager.PlayerList)
|
||||
{
|
||||
if (player.MinimapPlayerMarker == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
player.MinimapPlayerMarker.GetComponent<MeshRenderer>().enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void ShowMinimap(Minimap minimap)
|
||||
{
|
||||
foreach (var player in QSBPlayerManager.PlayerList)
|
||||
{
|
||||
if (player.MinimapPlayerMarker == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
player.MinimapPlayerMarker.GetComponent<MeshRenderer>().enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -221,6 +373,9 @@ internal class MultiplayerHUDManager : MonoBehaviour, IAddComponentOnStart
|
||||
private void OnRemovePlayer(PlayerInfo player)
|
||||
{
|
||||
Destroy(player.HUDBox?.gameObject);
|
||||
Destroy(player.MinimapPlayerMarker);
|
||||
|
||||
WriteMessage($"<color=yellow>{string.Format(QSBLocalization.Current.PlayerLeftTheGame, player.Name)}</color>");
|
||||
}
|
||||
|
||||
private PlanetTrigger CreateTrigger(string parentPath, HUDIcon icon)
|
||||
@ -273,4 +428,4 @@ internal class MultiplayerHUDManager : MonoBehaviour, IAddComponentOnStart
|
||||
|
||||
return go;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,29 @@ internal class MinimapPatches : QSBPatch
|
||||
[HarmonyPatch(nameof(Minimap.UpdateMarkers))]
|
||||
public static void UpdateMarkers(Minimap __instance)
|
||||
{
|
||||
MultiplayerHUDManager.Instance.UpdateMinimapMarkers(__instance);
|
||||
if (__instance._minimapMode == Minimap.MinimapMode.Player)
|
||||
{
|
||||
MultiplayerHUDManager.Instance.UpdateMinimapMarkers(__instance);
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(Minimap.HideMinimap))]
|
||||
public static void HideMinimap(Minimap __instance)
|
||||
{
|
||||
if (__instance._minimapMode == Minimap.MinimapMode.Player)
|
||||
{
|
||||
MultiplayerHUDManager.Instance.HideMinimap(__instance);
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(Minimap.ShowMinimap))]
|
||||
public static void ShowMinimap(Minimap __instance)
|
||||
{
|
||||
if (__instance._minimapMode == Minimap.MinimapMode.Player)
|
||||
{
|
||||
MultiplayerHUDManager.Instance.ShowMinimap(__instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ public class Translation
|
||||
public string AddonMismatch;
|
||||
public string IncompatibleMod;
|
||||
public string PlayerJoinedTheGame;
|
||||
public string PlayerLeftTheGame;
|
||||
public string PlayerWasKicked;
|
||||
public string KickedFromServer;
|
||||
public string RespawnPlayer;
|
||||
|
@ -1,5 +1,6 @@
|
||||
using EpicTransport;
|
||||
using Mirror;
|
||||
using OWML.Common;
|
||||
using QSB.Localization;
|
||||
using QSB.Messaging;
|
||||
using QSB.Player.TransformSync;
|
||||
@ -111,17 +112,17 @@ internal class MenuManager : MonoBehaviour, IAddComponentOnStart
|
||||
_nowLoadingSB.Length = 0;
|
||||
}
|
||||
|
||||
private void OnLanguageChanged()
|
||||
public void OnLanguageChanged()
|
||||
{
|
||||
if (QSBSceneManager.CurrentScene != OWScene.TitleScreen)
|
||||
{
|
||||
DebugLog.ToConsole("Error - Language changed while not in title screen?! Should be impossible!", OWML.Common.MessageType.Error);
|
||||
DebugLog.ToConsole("Error - Language changed while not in title screen?! Should be impossible!", MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
HostButton.transform.GetChild(0).GetChild(1).GetComponent<Text>().text = QSBLocalization.Current.MainMenuHost;
|
||||
ConnectButton.transform.GetChild(0).GetChild(1).GetComponent<Text>().text = QSBLocalization.Current.MainMenuConnect;
|
||||
var text = QSBCore.DebugSettings.UseKcpTransport ? QSBLocalization.Current.PublicIPAddress : QSBLocalization.Current.ProductUserID;
|
||||
var text = QSBCore.UseKcpTransport ? QSBLocalization.Current.PublicIPAddress : QSBLocalization.Current.ProductUserID;
|
||||
ConnectPopup.SetUpPopup(text, InputLibrary.menuConfirm, InputLibrary.cancel, new ScreenPrompt(QSBLocalization.Current.Connect), new ScreenPrompt(QSBLocalization.Current.Cancel), false);
|
||||
ConnectPopup.SetInputFieldPlaceholderText(text);
|
||||
ExistingNewCopyPopup.SetUpPopup(QSBLocalization.Current.HostExistingOrNewOrCopy,
|
||||
@ -337,7 +338,7 @@ internal class MenuManager : MonoBehaviour, IAddComponentOnStart
|
||||
|
||||
private void CreateCommonPopups()
|
||||
{
|
||||
var text = QSBCore.DebugSettings.UseKcpTransport ? QSBLocalization.Current.PublicIPAddress : QSBLocalization.Current.ProductUserID;
|
||||
var text = QSBCore.UseKcpTransport ? QSBLocalization.Current.PublicIPAddress : QSBLocalization.Current.ProductUserID;
|
||||
ConnectPopup = QSBCore.MenuApi.MakeInputFieldPopup(text, text, QSBLocalization.Current.Connect, QSBLocalization.Current.Cancel);
|
||||
ConnectPopup.CloseMenuOnOk(false);
|
||||
ConnectPopup.OnPopupConfirm += () =>
|
||||
@ -434,13 +435,17 @@ internal class MenuManager : MonoBehaviour, IAddComponentOnStart
|
||||
{
|
||||
if (button == null)
|
||||
{
|
||||
DebugLog.DebugWrite($"Warning - Tried to set button to {active}, but it was null.", OWML.Common.MessageType.Warning);
|
||||
DebugLog.DebugWrite($"Warning - Tried to set button to {active}, but it was null.", MessageType.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
var titleAnimationController = QSBWorldSync.GetUnityObject<TitleScreenManager>()._gfxController;
|
||||
var activeAlpha = 1;
|
||||
|
||||
var activeAlpha = titleAnimationController.IsTitleAnimationComplete() ? 1 : 0;
|
||||
if (QSBSceneManager.CurrentScene == OWScene.TitleScreen)
|
||||
{
|
||||
var titleAnimationController = QSBWorldSync.GetUnityObject<TitleScreenManager>()._gfxController;
|
||||
activeAlpha = titleAnimationController.IsTitleAnimationComplete() ? 1 : 0;
|
||||
}
|
||||
|
||||
button.SetActive(active);
|
||||
button.GetComponent<CanvasGroup>().alpha = active ? activeAlpha : 0;
|
||||
@ -628,7 +633,7 @@ internal class MenuManager : MonoBehaviour, IAddComponentOnStart
|
||||
SetButtonActive(NewGameButton, false);
|
||||
_loadingText = HostButton.transform.GetChild(0).GetChild(1).GetComponent<Text>();
|
||||
|
||||
if (!QSBCore.DebugSettings.UseKcpTransport)
|
||||
if (!QSBCore.UseKcpTransport)
|
||||
{
|
||||
var productUserId = EOSSDKComponent.LocalUserProductIdString;
|
||||
|
||||
@ -684,8 +689,6 @@ internal class MenuManager : MonoBehaviour, IAddComponentOnStart
|
||||
Locator.GetMenuInputModule().DisableInputs();
|
||||
|
||||
QSBNetworkManager.singleton.networkAddress = address;
|
||||
// hack to get disconnect call if start client fails immediately (happens on kcp transport when failing to resolve host name)
|
||||
typeof(NetworkClient).GetProperty(nameof(NetworkClient.connection))!.SetValue(null, new NetworkConnectionToServer());
|
||||
QSBNetworkManager.singleton.StartClient();
|
||||
}
|
||||
|
||||
@ -714,7 +717,7 @@ internal class MenuManager : MonoBehaviour, IAddComponentOnStart
|
||||
OpenInfoPopup(string.Format(QSBLocalization.Current.ServerRefusedConnection, reason), QSBLocalization.Current.OK);
|
||||
}
|
||||
|
||||
private void OnDisconnected(string error)
|
||||
private void OnDisconnected(TransportError error, string reason)
|
||||
{
|
||||
QSBCore.IsInMultiplayer = false;
|
||||
|
||||
@ -733,7 +736,7 @@ internal class MenuManager : MonoBehaviour, IAddComponentOnStart
|
||||
}
|
||||
};
|
||||
|
||||
OpenInfoPopup(string.Format(QSBLocalization.Current.ClientDisconnectWithError, error), QSBLocalization.Current.OK);
|
||||
OpenInfoPopup(string.Format(QSBLocalization.Current.ClientDisconnectWithError, reason), QSBLocalization.Current.OK);
|
||||
}
|
||||
|
||||
SetButtonActive(DisconnectButton, false);
|
||||
|
@ -1,6 +1,7 @@
|
||||
using HarmonyLib;
|
||||
using Mirror;
|
||||
using OWML.Common;
|
||||
using QSB.HUD;
|
||||
using QSB.Localization;
|
||||
using QSB.Messaging;
|
||||
using QSB.Utility;
|
||||
@ -125,7 +126,7 @@ public class PlayerJoinMessage : QSBMessage
|
||||
|
||||
var player = QSBPlayerManager.GetPlayer(From);
|
||||
player.Name = PlayerName;
|
||||
DebugLog.ToAll(string.Format(QSBLocalization.Current.PlayerJoinedTheGame, player.Name), MessageType.Info);
|
||||
MultiplayerHUDManager.Instance.WriteMessage($"<color=green>{string.Format(QSBLocalization.Current.PlayerJoinedTheGame, player.Name)}</color>");
|
||||
DebugLog.DebugWrite($"{player} joined. qsbVersion:{QSBVersion}, gameVersion:{GameVersion}, dlcInstalled:{DlcInstalled}", MessageType.Info);
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Mirror;
|
||||
using QSB.HUD;
|
||||
using QSB.Localization;
|
||||
using QSB.Menus;
|
||||
using QSB.Messaging;
|
||||
@ -34,15 +35,15 @@ internal class PlayerKickMessage : QSBMessage<string>
|
||||
{
|
||||
if (QSBPlayerManager.PlayerExists(PlayerId))
|
||||
{
|
||||
DebugLog.ToAll(string.Format(QSBLocalization.Current.PlayerWasKicked, QSBPlayerManager.GetPlayer(PlayerId).Name));
|
||||
MultiplayerHUDManager.Instance.WriteMessage($"<color=red>{string.Format(QSBLocalization.Current.PlayerWasKicked, QSBPlayerManager.GetPlayer(PlayerId).Name)}</color>");
|
||||
return;
|
||||
}
|
||||
|
||||
DebugLog.ToAll(string.Format(QSBLocalization.Current.PlayerWasKicked, PlayerId));
|
||||
MultiplayerHUDManager.Instance.WriteMessage($"<color=red>{string.Format(QSBLocalization.Current.PlayerWasKicked, PlayerId)}</color>");
|
||||
return;
|
||||
}
|
||||
|
||||
DebugLog.ToAll(string.Format(QSBLocalization.Current.KickedFromServer, Data));
|
||||
MultiplayerHUDManager.Instance.WriteMessage($"<color=red>{string.Format(QSBLocalization.Current.KickedFromServer, Data)}</color>");
|
||||
MenuManager.Instance.OnKicked(Data);
|
||||
|
||||
NetworkClient.Disconnect();
|
||||
|
@ -58,10 +58,11 @@ public class QSBCore : ModBehaviour
|
||||
// ignore the last patch numbers like the title screen does
|
||||
Application.version.Split('.').Take(3).Join(delimiter: ".");
|
||||
public static bool DLCInstalled => EntitlementsManager.IsDlcOwned() == EntitlementsManager.AsyncOwnershipStatus.Owned;
|
||||
public static bool UseKcpTransport { get; private set; }
|
||||
public static bool IncompatibleModsAllowed { get; private set; }
|
||||
public static bool ShowPlayerNames { get; private set; }
|
||||
public static bool ShipDamage { get; private set; }
|
||||
public static bool ShowExtraHUDElements { get ; private set; }
|
||||
public static bool ShowExtraHUDElements { get; private set; }
|
||||
public static GameVendor GameVendor { get; private set; } = GameVendor.None;
|
||||
public static bool IsStandalone => GameVendor is GameVendor.Epic or GameVendor.Steam;
|
||||
public static IProfileManager ProfileManager => IsStandalone
|
||||
@ -155,7 +156,7 @@ public class QSBCore : ModBehaviour
|
||||
|
||||
if (DebugSettings.AutoStart)
|
||||
{
|
||||
DebugSettings.UseKcpTransport = true;
|
||||
UseKcpTransport = true;
|
||||
DebugSettings.DebugMode = true;
|
||||
}
|
||||
|
||||
@ -254,6 +255,9 @@ public class QSBCore : ModBehaviour
|
||||
|
||||
public override void Configure(IModConfig config)
|
||||
{
|
||||
UseKcpTransport = config.GetSettingsValue<bool>("useKcpTransport") || DebugSettings.AutoStart;
|
||||
QSBNetworkManager.UpdateTransport();
|
||||
|
||||
DefaultServerIP = config.GetSettingsValue<string>("defaultServerIP");
|
||||
IncompatibleModsAllowed = config.GetSettingsValue<bool>("incompatibleModsAllowed");
|
||||
ShowPlayerNames = config.GetSettingsValue<bool>("showPlayerNames");
|
||||
|
@ -12,6 +12,7 @@ using QSB.EchoesOfTheEye.EclipseDoors.VariableSync;
|
||||
using QSB.EchoesOfTheEye.EclipseElevators.VariableSync;
|
||||
using QSB.EchoesOfTheEye.RaftSync.TransformSync;
|
||||
using QSB.JellyfishSync.TransformSync;
|
||||
using QSB.Menus;
|
||||
using QSB.Messaging;
|
||||
using QSB.ModelShip;
|
||||
using QSB.ModelShip.TransformSync;
|
||||
@ -44,7 +45,7 @@ public class QSBNetworkManager : NetworkManager, IAddComponentOnStart
|
||||
public new static QSBNetworkManager singleton => (QSBNetworkManager)NetworkManager.singleton;
|
||||
|
||||
public event Action OnClientConnected;
|
||||
public event Action<string> OnClientDisconnected;
|
||||
public event Action<TransportError, string> OnClientDisconnected;
|
||||
|
||||
public GameObject OrbPrefab { get; private set; }
|
||||
public GameObject ShipPrefab { get; private set; }
|
||||
@ -64,24 +65,18 @@ public class QSBNetworkManager : NetworkManager, IAddComponentOnStart
|
||||
private GameObject _probePrefab;
|
||||
private bool _everConnected;
|
||||
|
||||
private string _lastTransportError;
|
||||
private static readonly string[] _kcpErrorLogs =
|
||||
{
|
||||
"KcpPeer: received disconnect message",
|
||||
"Failed to resolve host: .*"
|
||||
};
|
||||
private (TransportError error, string reason) _lastTransportError = (TransportError.Unexpected, "transport did not give an error. uh oh");
|
||||
|
||||
private static kcp2k.KcpTransport _kcpTransport;
|
||||
private static EosTransport _eosTransport;
|
||||
|
||||
public override void Awake()
|
||||
{
|
||||
gameObject.SetActive(false);
|
||||
|
||||
if (QSBCore.DebugSettings.UseKcpTransport)
|
||||
{
|
||||
var kcpTransport = gameObject.AddComponent<kcp2k.KcpTransport>();
|
||||
kcpTransport.Timeout = int.MaxValue; // effectively disables kcp ping and timeout (good for testing)
|
||||
transport = kcpTransport;
|
||||
_kcpTransport = gameObject.AddComponent<kcp2k.KcpTransport>();
|
||||
}
|
||||
else
|
||||
{
|
||||
// https://dev.epicgames.com/portal/en-US/qsb/sdk/credentials/qsb
|
||||
var eosApiKey = ScriptableObject.CreateInstance<EosApiKey>();
|
||||
@ -97,10 +92,9 @@ public class QSBNetworkManager : NetworkManager, IAddComponentOnStart
|
||||
eosSdkComponent.apiKeys = eosApiKey;
|
||||
eosSdkComponent.epicLoggerLevel = LogLevel.VeryVerbose;
|
||||
|
||||
var eosTransport = gameObject.AddComponent<EosTransport>();
|
||||
eosTransport.SetTransportError = error => _lastTransportError = error;
|
||||
transport = eosTransport;
|
||||
_eosTransport = gameObject.AddComponent<EosTransport>();
|
||||
}
|
||||
transport = QSBCore.UseKcpTransport ? _kcpTransport : _eosTransport;
|
||||
|
||||
gameObject.SetActive(true);
|
||||
|
||||
@ -163,6 +157,22 @@ public class QSBNetworkManager : NetworkManager, IAddComponentOnStart
|
||||
ConfigureNetworkManager();
|
||||
}
|
||||
|
||||
public static void UpdateTransport()
|
||||
{
|
||||
if (QSBCore.IsInMultiplayer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (singleton != null)
|
||||
{
|
||||
singleton.transport = Transport.active = QSBCore.UseKcpTransport ? _kcpTransport : _eosTransport;
|
||||
}
|
||||
if (MenuManager.Instance != null)
|
||||
{
|
||||
MenuManager.Instance.OnLanguageChanged(); // hack to update text
|
||||
}
|
||||
}
|
||||
|
||||
private void InitPlayerName() =>
|
||||
Delay.RunWhen(PlayerData.IsLoaded, () =>
|
||||
{
|
||||
@ -192,7 +202,6 @@ public class QSBNetworkManager : NetworkManager, IAddComponentOnStart
|
||||
PlayerName = "Player";
|
||||
}
|
||||
|
||||
if (!QSBCore.DebugSettings.UseKcpTransport)
|
||||
{
|
||||
EOSSDKComponent.DisplayName = PlayerName;
|
||||
}
|
||||
@ -225,26 +234,18 @@ public class QSBNetworkManager : NetworkManager, IAddComponentOnStart
|
||||
{
|
||||
networkAddress = QSBCore.DefaultServerIP;
|
||||
|
||||
if (QSBCore.DebugSettings.UseKcpTransport)
|
||||
{
|
||||
kcp2k.Log.Info = s =>
|
||||
{
|
||||
DebugLog.DebugWrite("[KCP] " + s);
|
||||
if (_kcpErrorLogs.Any(p => Regex.IsMatch(s, p)))
|
||||
// hack
|
||||
if (s == "KcpPeer: received disconnect message")
|
||||
{
|
||||
_lastTransportError = s;
|
||||
OnClientError(TransportError.ConnectionClosed, "host disconnected");
|
||||
}
|
||||
};
|
||||
kcp2k.Log.Warning = s =>
|
||||
{
|
||||
DebugLog.DebugWrite("[KCP] " + s, MessageType.Warning);
|
||||
_lastTransportError = s;
|
||||
};
|
||||
kcp2k.Log.Error = s =>
|
||||
{
|
||||
DebugLog.DebugWrite("[KCP] " + s, MessageType.Error);
|
||||
_lastTransportError = s;
|
||||
};
|
||||
kcp2k.Log.Warning = s => DebugLog.DebugWrite("[KCP] " + s, MessageType.Warning);
|
||||
kcp2k.Log.Error = s => DebugLog.DebugWrite("[KCP] " + s, MessageType.Error);
|
||||
}
|
||||
|
||||
QSBSceneManager.OnPostSceneLoad += (_, loadScene) =>
|
||||
@ -342,8 +343,8 @@ public class QSBNetworkManager : NetworkManager, IAddComponentOnStart
|
||||
{
|
||||
DebugLog.DebugWrite("OnClientDisconnect");
|
||||
base.OnClientDisconnect();
|
||||
OnClientDisconnected?.SafeInvoke(_lastTransportError);
|
||||
_lastTransportError = null;
|
||||
OnClientDisconnected?.SafeInvoke(_lastTransportError.error, _lastTransportError.reason);
|
||||
_lastTransportError = (TransportError.Unexpected, "transport did not give an error. uh oh");
|
||||
}
|
||||
|
||||
public override void OnServerDisconnect(NetworkConnectionToClient conn) // Called on the server when any client disconnects
|
||||
@ -409,4 +410,16 @@ public class QSBNetworkManager : NetworkManager, IAddComponentOnStart
|
||||
|
||||
base.OnStopServer();
|
||||
}
|
||||
|
||||
public override void OnServerError(NetworkConnectionToClient conn, TransportError error, string reason)
|
||||
{
|
||||
DebugLog.DebugWrite($"OnServerError({conn}, {error}, {reason})", MessageType.Error);
|
||||
_lastTransportError = (error, reason);
|
||||
}
|
||||
|
||||
public override void OnClientError(TransportError error, string reason)
|
||||
{
|
||||
DebugLog.DebugWrite($"OnClientError({error}, {reason})", MessageType.Error);
|
||||
_lastTransportError = (error, reason);
|
||||
}
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ public class QuantumObjectPatches : QSBPatch
|
||||
|
||||
var worldObject = __instance.GetWorldObject<IQSBQuantumObject>();
|
||||
var visibleToProbePlayers = worldObject.GetVisibleToProbePlayers();
|
||||
__result = visibleToProbePlayers.Any(x => x.ProbeLauncherEquipped != default);
|
||||
__result = visibleToProbePlayers.Any();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -338,6 +338,13 @@ internal abstract class QSBQuantumObject<T> : WorldObject<T>, IQSBQuantumObject
|
||||
}
|
||||
|
||||
label += $"VisibleInProbeSnapshot:{AttachedObject._visibleInProbeSnapshot}\r\n";
|
||||
label += $"IsLockedByProbeSnapshot:{AttachedObject.IsLockedByProbeSnapshot()}\r\n";
|
||||
label += "VisibleToProbePlayers:\r\n";
|
||||
|
||||
foreach (var player in GetVisibleToProbePlayers())
|
||||
{
|
||||
label += $" ID: {player.PlayerId}\r\n";
|
||||
}
|
||||
|
||||
return label;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using QSB.DeathSync.Messages;
|
||||
using Mirror;
|
||||
using QSB.DeathSync.Messages;
|
||||
using QSB.Messaging;
|
||||
using QSB.Patches;
|
||||
using QSB.Player;
|
||||
@ -48,7 +49,7 @@ internal class RespawnManager : MonoBehaviour, IAddComponentOnStart
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisconnected(string error)
|
||||
private void OnDisconnected(TransportError error, string reason)
|
||||
{
|
||||
_owRecoveryPoint?.SetActive(true);
|
||||
_qsbRecoveryPoint?.SetActive(false);
|
||||
|
@ -30,6 +30,7 @@
|
||||
"AddonMismatch": "Addon mismatch. (Client:{0} addons, Server:{1} addons)",
|
||||
"IncompatibleMod": "Using an incompatible/disallowed mod. First mod found was {0}",
|
||||
"PlayerJoinedTheGame": "{0} joined!",
|
||||
"PlayerLeftTheGame": "{0} left!",
|
||||
"PlayerWasKicked": "{0} was kicked.",
|
||||
"KickedFromServer": "Kicked from server. Reason : {0}",
|
||||
"RespawnPlayer": "Respawn Player",
|
||||
|
@ -5,9 +5,6 @@ namespace QSB.Utility;
|
||||
[JsonObject(MemberSerialization.OptIn)]
|
||||
public class DebugSettings
|
||||
{
|
||||
[JsonProperty("useKcpTransport")]
|
||||
public bool UseKcpTransport;
|
||||
|
||||
[JsonProperty("dumpWorldObjects")]
|
||||
public bool DumpWorldObjects;
|
||||
|
||||
|
@ -6,14 +6,23 @@ namespace QSB.Utility;
|
||||
|
||||
public class ListStack<T> : IEnumerable<T>
|
||||
{
|
||||
private readonly List<T> _items = new();
|
||||
private List<T> _items = new();
|
||||
|
||||
public int Count => _items.Count;
|
||||
|
||||
private readonly bool _removeDuplicates;
|
||||
|
||||
public ListStack(bool removeDuplicates)
|
||||
{
|
||||
_removeDuplicates = removeDuplicates;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
=> _items.Clear();
|
||||
|
||||
public void Push(T item)
|
||||
{
|
||||
if (_items.Contains(item))
|
||||
if (_removeDuplicates && _items.Contains(item))
|
||||
{
|
||||
RemoveAll(x => EqualityComparer<T>.Default.Equals(x, item));
|
||||
}
|
||||
@ -33,6 +42,27 @@ public class ListStack<T> : IEnumerable<T>
|
||||
return default;
|
||||
}
|
||||
|
||||
public T RemoveFirstElementAndShift()
|
||||
{
|
||||
if (_items.Count == 0)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
var firstElement = _items[0];
|
||||
|
||||
if (_items.Count == 0)
|
||||
{
|
||||
return firstElement;
|
||||
}
|
||||
|
||||
// shift list left
|
||||
// allocates blehhh who cares
|
||||
_items = _items.GetRange(1, _items.Count - 1);
|
||||
|
||||
return firstElement;
|
||||
}
|
||||
|
||||
public T Peek() => _items.Count > 0
|
||||
? _items[_items.Count - 1]
|
||||
: default;
|
||||
|
@ -1,35 +1,42 @@
|
||||
{
|
||||
"enabled": true,
|
||||
"settings": {
|
||||
"defaultServerIP": {
|
||||
"title": "Last Entered IP/ID",
|
||||
"type": "text",
|
||||
"value": "localhost",
|
||||
"tooltip": "Used if you leave the connect prompt blank."
|
||||
},
|
||||
"incompatibleModsAllowed": {
|
||||
"title": "Incompatible Mods Allowed",
|
||||
"type": "toggle",
|
||||
"value": false,
|
||||
"tooltip": "Kicks players if they have certain mods."
|
||||
},
|
||||
"showPlayerNames": {
|
||||
"title": "Show Player Names",
|
||||
"type": "toggle",
|
||||
"value": true,
|
||||
"tooltip": "Shows player names in the HUD and the map view."
|
||||
},
|
||||
"shipDamage": {
|
||||
"title": "Ship Damage",
|
||||
"type": "toggle",
|
||||
"value": true,
|
||||
"tooltip": "Take impact damage when inside the ship."
|
||||
},
|
||||
"showExtraHud": {
|
||||
"title": "Show Extra HUD Elements",
|
||||
"type": "toggle",
|
||||
"value": true,
|
||||
"tooltip" : "Show extra HUD elements, like player status and minimap icons."
|
||||
}
|
||||
}
|
||||
"$schema": "https://raw.githubusercontent.com/ow-mods/owml/master/schemas/config_schema.json",
|
||||
"enabled": true,
|
||||
"settings": {
|
||||
"useKcpTransport": {
|
||||
"title": "Use KCP Transport",
|
||||
"type": "toggle",
|
||||
"value": false,
|
||||
"tooltip": "Use alternative transport that requires port forwarding but seems to be more reliable. The port to forward is 7777 as TCP/UDP. Use this if you are having trouble connecting. ALL PLAYERS MUST HAVE THIS AS THE SAME VALUE."
|
||||
},
|
||||
"defaultServerIP": {
|
||||
"title": "Last Entered IP/ID",
|
||||
"type": "text",
|
||||
"value": "localhost",
|
||||
"tooltip": "Used if you leave the connect prompt blank."
|
||||
},
|
||||
"incompatibleModsAllowed": {
|
||||
"title": "Incompatible Mods Allowed",
|
||||
"type": "toggle",
|
||||
"value": false,
|
||||
"tooltip": "Kicks players if they have certain mods."
|
||||
},
|
||||
"showPlayerNames": {
|
||||
"title": "Show Player Names",
|
||||
"type": "toggle",
|
||||
"value": true,
|
||||
"tooltip": "Shows player names in the HUD and the map view."
|
||||
},
|
||||
"shipDamage": {
|
||||
"title": "Ship Damage",
|
||||
"type": "toggle",
|
||||
"value": true,
|
||||
"tooltip": "Take impact damage when inside the ship."
|
||||
},
|
||||
"showExtraHud": {
|
||||
"title": "Show Extra HUD Elements",
|
||||
"type": "toggle",
|
||||
"value": true,
|
||||
"tooltip": "Show extra HUD elements, like player status and minimap icons."
|
||||
}
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@
|
||||
"body": "- Disable *all* other mods. (Can heavily affect performance)\n- Make sure you are not running any other network-intensive applications."
|
||||
},
|
||||
"uniqueName": "Raicuparta.QuantumSpaceBuddies",
|
||||
"version": "0.27.0",
|
||||
"version": "0.28.0",
|
||||
"owmlVersion": "2.9.0",
|
||||
"dependencies": [ "_nebula.MenuFramework", "JohnCorby.VanillaFix" ],
|
||||
"pathsToPreserve": [ "debugsettings.json" ],
|
||||
|
20
README.md
20
README.md
@ -46,6 +46,9 @@ Spoilers within!
|
||||
|
||||
## Frequently Asked Questions
|
||||
|
||||
### I keep timing out when trying to connect!
|
||||
Check the mod settings for "Use KCP Transport". You have to forward port 7777 as TCP/UDP, or use Hamachi. ALL PLAYERS MUST HAVE THIS AS THE SAME VALUE.
|
||||
|
||||
### Requirements
|
||||
- Latest version of OWML.
|
||||
- Latest version of Mod Manager. (If using)
|
||||
@ -55,22 +58,28 @@ Spoilers within!
|
||||
|
||||
### How complete is this mod? How far through the game can I play?
|
||||
|
||||
The base game is around 95% done, whereas EotE is around 80% done.
|
||||
You can play the entire game, plus DLC!
|
||||
There still might be one or two small mechanics that aren't synced - let us know if you find an obvious one that we've missed.
|
||||
Also, you might encounter bugs that mean you can't progress in multiplayer. Again, let us know if you find one!
|
||||
|
||||
### Compatibility with other mods
|
||||
TL;DR - Don't use any mods with QSB that aren't marked as QSB compatible.
|
||||
|
||||
QSB relies on object hierarchy to sync objects, so any mod that changes that risks breaking QSB. Also, QSB relies on certain game events being called when things happen in-game. Any mod that makes these things happen without calling the correct events will break QSB. Some mods will work fine and have been tested, like CrouchMod. Others may only work partly, like EnableDebugMode and TAICheat.
|
||||
|
||||
### Will you make this compatible with NomaiVR?
|
||||
### Is this mod compatible with NomaiVR?
|
||||
|
||||
Maybe.
|
||||
Short answer - Kind of.
|
||||
|
||||
Long answer - We've done our best to try to keep them compatible, but no work has been done to explicitly make them play nice. Some things may work, others may not.
|
||||
Getting both mods to work together is a big undertaking, and would require rewrites to a lot of code in both mods.
|
||||
If you want to play with VR, make sure the server host has "Incompatible Mods Allowed" enabled.
|
||||
|
||||
### Why do I keep getting thrown around the ship?
|
||||
|
||||
Boring boring physics stuff. The velocity of the ship is synced, as well as the angular velocity. However, this velocity is not also applied to the player. (Or it is sometimes. I don't 100% know.) This means the ship will accelerate, leaving the player "behind". Which makes you fly into the walls alot.
|
||||
|
||||
**Update**: you can attach/detach yourself to/from the ship using the prompt in the center of the screen.
|
||||
To fix this, whilst in the ship you can attach yourself to it. Look at the top-left of your screen when inside the ship for the buttons to press.
|
||||
|
||||
### What's the difference between QSB and Outer Wilds Online?
|
||||
|
||||
@ -131,7 +140,6 @@ The template for this file is this :
|
||||
|
||||
```
|
||||
{
|
||||
"useKcpTransport": false,
|
||||
"dumpWorldObjects": false,
|
||||
"instanceIdInLogs": false,
|
||||
"hookDebugLogs": false,
|
||||
@ -149,7 +157,6 @@ The template for this file is this :
|
||||
}
|
||||
```
|
||||
|
||||
- useKcpTransport - Allows you to directly connect to IP addresses, rather than use the Epic relay.
|
||||
- dumpWorldObjects - Creates a file with information about the WorldObjects that were created.
|
||||
- instanceIdInLogs - Appends the game instance id to every log message sent.
|
||||
- hookDebugLogs - Print Unity logs and warnings.
|
||||
@ -199,7 +206,6 @@ The template for this file is this :
|
||||
- [Mirror](https://mirror-networking.com/)
|
||||
- [kcp2k](https://github.com/vis2k/kcp2k)
|
||||
- [Telepathy](https://github.com/vis2k/Telepathy)
|
||||
- [where-allocation](https://github.com/vis2k/where-allocation)
|
||||
- [EpicOnlineTransport](https://github.com/FakeByte/EpicOnlineTransport)
|
||||
- [HarmonyX](https://github.com/BepInEx/HarmonyX)
|
||||
- [UniTask](https://github.com/Cysharp/UniTask)
|
||||
|
Loading…
Reference in New Issue
Block a user