Merge pull request #282 from misternebula/probe-sync-tweak

network transform rewrite aaaaaaa
This commit is contained in:
_nebula 2021-04-21 22:26:45 +01:00 committed by GitHub
commit 88119389d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 475 additions and 917 deletions

View File

@ -12,7 +12,6 @@ namespace QSB.Animation
public class AnimationSync : PlayerSyncObject public class AnimationSync : PlayerSyncObject
{ {
private Animator _anim; private Animator _anim;
private Animator _bodyAnim;
private QNetworkAnimator _netAnim; private QNetworkAnimator _netAnim;
private RuntimeAnimatorController _suitedAnimController; private RuntimeAnimatorController _suitedAnimController;
@ -30,7 +29,7 @@ namespace QSB.Animation
public AnimatorMirror Mirror { get; private set; } public AnimatorMirror Mirror { get; private set; }
public AnimationType CurrentType { get; set; } public AnimationType CurrentType { get; set; }
public Animator VisibleAnimator => _bodyAnim; public Animator VisibleAnimator { get; private set; }
protected void Awake() protected void Awake()
{ {
@ -73,15 +72,15 @@ namespace QSB.Animation
LoadControllers(); LoadControllers();
} }
_netAnim.enabled = true; _netAnim.enabled = true;
_bodyAnim = body.GetComponent<Animator>(); VisibleAnimator = body.GetComponent<Animator>();
Mirror = body.gameObject.AddComponent<AnimatorMirror>(); Mirror = body.gameObject.AddComponent<AnimatorMirror>();
if (IsLocalPlayer) if (IsLocalPlayer)
{ {
Mirror.Init(_bodyAnim, _anim); Mirror.Init(VisibleAnimator, _anim);
} }
else else
{ {
Mirror.Init(_anim, _bodyAnim); Mirror.Init(_anim, VisibleAnimator);
} }
for (var i = 0; i < _anim.parameterCount; i++) for (var i = 0; i < _anim.parameterCount; i++)
@ -138,7 +137,7 @@ namespace QSB.Animation
private void InitCrouchSync() private void InitCrouchSync()
{ {
_crouchSync = gameObject.AddComponent<CrouchSync>(); _crouchSync = gameObject.AddComponent<CrouchSync>();
_crouchSync.Init(this, _playerController, _bodyAnim); _crouchSync.Init(this, _playerController, VisibleAnimator);
} }
private void OnJump() => _netAnim.SetTrigger("Jump"); private void OnJump() => _netAnim.SetTrigger("Jump");
@ -228,16 +227,16 @@ namespace QSB.Animation
break; break;
} }
_anim.runtimeAnimatorController = controller; _anim.runtimeAnimatorController = controller;
_bodyAnim.runtimeAnimatorController = controller; VisibleAnimator.runtimeAnimatorController = controller;
if (type != AnimationType.PlayerSuited && type != AnimationType.PlayerUnsuited) if (type != AnimationType.PlayerSuited && type != AnimationType.PlayerUnsuited)
{ {
_bodyAnim.SetTrigger("Playing"); VisibleAnimator.SetTrigger("Playing");
_anim.SetTrigger("Playing"); _anim.SetTrigger("Playing");
} }
else else
{ {
// Avoids "jumping" when exiting instrument and putting on suit // Avoids "jumping" when exiting instrument and putting on suit
_bodyAnim.SetTrigger("Grounded"); VisibleAnimator.SetTrigger("Grounded");
_anim.SetTrigger("Grounded"); _anim.SetTrigger("Grounded");
} }
_netAnim.animator = _anim; // Probably not needed. _netAnim.animator = _anim; // Probably not needed.

View File

@ -28,7 +28,6 @@
public static string QSBPlayerDeath = "QSBPlayerDeath"; public static string QSBPlayerDeath = "QSBPlayerDeath";
public static string QSBPlayerJoin = "QSBPlayerJoin"; public static string QSBPlayerJoin = "QSBPlayerJoin";
public static string QSBPlayerReady = "QSBPlayerReady"; public static string QSBPlayerReady = "QSBPlayerReady";
public static string QSBSectorChange = "QSBSectorChange";
public static string QSBPlayerStatesRequest = "QSBPlayerStatesRequest"; public static string QSBPlayerStatesRequest = "QSBPlayerStatesRequest";
public static string QSBServerTime = "QSBServerTime"; public static string QSBServerTime = "QSBServerTime";
public static string QSBStartLift = "QSBStartLift"; public static string QSBStartLift = "QSBStartLift";

View File

@ -13,7 +13,6 @@
SuitActiveChange, SuitActiveChange,
PlayerJoin, PlayerJoin,
PlayerDeath, PlayerDeath,
PlayerSectorChange,
PlayerReady, PlayerReady,
ProbeActiveChange, ProbeActiveChange,
Elevator, Elevator,

View File

@ -41,7 +41,6 @@ namespace QSB.Events
new PlayerTranslatorEvent(), new PlayerTranslatorEvent(),
new PlayerProbeLauncherEvent(), new PlayerProbeLauncherEvent(),
new PlayerProbeEvent(), new PlayerProbeEvent(),
new PlayerSectorEvent(),
new PlayerDeathEvent(), new PlayerDeathEvent(),
new PlayerStatesRequestEvent(), new PlayerStatesRequestEvent(),
new ServerSendPlayerStatesEvent(), new ServerSendPlayerStatesEvent(),

View File

@ -38,7 +38,7 @@ namespace QSB.Instruments.QSBCamera
_cameraObj.transform.localPosition = new Vector3(0, 0, -5f); _cameraObj.transform.localPosition = new Vector3(0, 0, -5f);
_cameraObj.transform.localRotation = Quaternion.Euler(0, 0, 0); _cameraObj.transform.localRotation = Quaternion.Euler(0, 0, 0);
_camera = _cameraObj.AddComponent<Camera>(); _camera = _cameraObj.AddComponent<Camera>();
_camera.cullingMask = Locator.GetPlayerCamera().mainCamera.cullingMask & ~(1 << 27) | (1 << 22); _camera.cullingMask = (Locator.GetPlayerCamera().mainCamera.cullingMask & ~(1 << 27)) | (1 << 22);
_camera.clearFlags = CameraClearFlags.Color; _camera.clearFlags = CameraClearFlags.Color;
_camera.backgroundColor = Color.black; _camera.backgroundColor = Color.black;
_camera.fieldOfView = 90f; _camera.fieldOfView = 90f;

View File

@ -1,46 +0,0 @@
using OWML.Common;
using QSB.Events;
using QSB.SectorSync.WorldObjects;
using QSB.TransformSync;
using QSB.Utility;
using QSB.WorldSync;
using QSB.WorldSync.Events;
namespace QSB.Player.Events
{
public class PlayerSectorEvent : QSBEvent<WorldObjectMessage>
{
public override EventType Type => EventType.PlayerSectorChange;
public override void SetupListener() => GlobalMessenger<uint, QSBSector>.AddListener(EventNames.QSBSectorChange, Handler);
public override void CloseListener() => GlobalMessenger<uint, QSBSector>.RemoveListener(EventNames.QSBSectorChange, Handler);
private void Handler(uint netId, QSBSector sector) => SendEvent(CreateMessage(netId, sector));
private WorldObjectMessage CreateMessage(uint netId, QSBSector sector) => new WorldObjectMessage
{
AboutId = netId,
ObjectId = sector.ObjectId
};
public override void OnReceiveRemote(bool server, WorldObjectMessage message)
{
if (!QSBSceneManager.IsInUniverse)
{
return;
}
var sector = QSBWorldSync.GetWorldFromId<QSBSector>(message.ObjectId);
if (sector == null)
{
DebugLog.ToConsole($"Sector with index id {message.ObjectId} not found!", MessageType.Warning);
return;
}
var transformSync = QSBPlayerManager.GetSyncObject<SyncObjectTransformSync>(message.AboutId);
QSBCore.UnityEvents.RunWhen(() => transformSync?.SyncedTransform != null,
() => transformSync?.SetReferenceSector(sector));
}
}
}

View File

@ -3,7 +3,6 @@ using QSB.CampfireSync.WorldObjects;
using QSB.Events; using QSB.Events;
using QSB.Messaging; using QSB.Messaging;
using QSB.QuantumSync; using QSB.QuantumSync;
using QSB.TransformSync;
using QSB.TranslationSync; using QSB.TranslationSync;
using QSB.TranslationSync.WorldObjects; using QSB.TranslationSync.WorldObjects;
using QSB.Utility; using QSB.Utility;
@ -31,11 +30,6 @@ namespace QSB.Player.Events
{ {
DebugLog.DebugWrite($"Get state request from {message.FromId} - isServer?{server}"); DebugLog.DebugWrite($"Get state request from {message.FromId} - isServer?{server}");
QSBEventManager.FireEvent(EventNames.QSBServerSendPlayerStates); QSBEventManager.FireEvent(EventNames.QSBServerSendPlayerStates);
foreach (var item in QSBPlayerManager.GetSyncObjects<SyncObjectTransformSync>()
.Where(x => x != null && x.IsReady && x.ReferenceSector != null))
{
QSBEventManager.FireEvent(EventNames.QSBSectorChange, item.NetId.Value, item.ReferenceSector);
}
if (!server) if (!server)
{ {

View File

@ -1,6 +1,5 @@
using OWML.Common; using OWML.Common;
using QSB.Events; using QSB.Events;
using QSB.TransformSync;
using QSB.Utility; using QSB.Utility;
namespace QSB.Player.Events namespace QSB.Player.Events
@ -31,9 +30,10 @@ namespace QSB.Player.Events
public override void OnReceiveRemote(bool server, PlayerStateMessage message) public override void OnReceiveRemote(bool server, PlayerStateMessage message)
{ {
DebugLog.DebugWrite($"Received playerstate of player ID {message.AboutId}", MessageType.Info); DebugLog.DebugWrite($"Received playerstate of player ID {message.AboutId}", MessageType.Info);
QSBCore.UnityEvents.RunWhen( if (QSBPlayerManager.PlayerExists(message.AboutId))
() => QSBPlayerManager.GetSyncObject<SyncObjectTransformSync>(message.AboutId) != null, {
() => QSBPlayerManager.HandleFullStateMessage(message)); QSBPlayerManager.HandleFullStateMessage(message);
}
} }
} }
} }

View File

@ -1,5 +1,6 @@
using QSB.Animation; using QSB.Animation;
using QSB.CampfireSync.WorldObjects; using QSB.CampfireSync.WorldObjects;
using QSB.Player.TransformSync;
using QSB.ProbeSync; using QSB.ProbeSync;
using QSB.QuantumSync; using QSB.QuantumSync;
using QSB.RoastingSync; using QSB.RoastingSync;
@ -15,6 +16,7 @@ namespace QSB.Player
public string Name { get; set; } public string Name { get; set; }
public PlayerHUDMarker HudMarker { get; set; } public PlayerHUDMarker HudMarker { get; set; }
public PlayerState PlayerStates { get; set; } = new PlayerState(); public PlayerState PlayerStates { get; set; } = new PlayerState();
public PlayerTransformSync TransformSync { get; set; }
// Body Objects // Body Objects
public OWCamera Camera { get; set; } public OWCamera Camera { get; set; }

View File

@ -6,9 +6,9 @@ using UnityEngine;
namespace QSB.Player.TransformSync namespace QSB.Player.TransformSync
{ {
public class PlayerCameraSync : SyncObjectTransformSync public class PlayerCameraSync : QSBNetworkTransform
{ {
protected override Transform InitLocalTransform() protected override GameObject InitLocalTransform()
{ {
SectorSync.SetSectorDetector(Locator.GetPlayerSectorDetector()); SectorSync.SetSectorDetector(Locator.GetPlayerSectorDetector());
var body = Locator.GetPlayerCamera().gameObject.transform; var body = Locator.GetPlayerCamera().gameObject.transform;
@ -21,10 +21,10 @@ namespace QSB.Player.TransformSync
DebugLog.DebugWrite("PlayerCameraSync init done - Request state!"); DebugLog.DebugWrite("PlayerCameraSync init done - Request state!");
QSBEventManager.FireEvent(EventNames.QSBPlayerStatesRequest); QSBEventManager.FireEvent(EventNames.QSBPlayerStatesRequest);
return body; return body.gameObject;
} }
protected override Transform InitRemoteTransform() protected override GameObject InitRemoteTransform()
{ {
var body = new GameObject("RemotePlayerCamera"); var body = new GameObject("RemotePlayerCamera");
@ -39,16 +39,7 @@ namespace QSB.Player.TransformSync
Player.Camera = owcamera; Player.Camera = owcamera;
Player.CameraBody = body; Player.CameraBody = body;
return body.transform; return body;
}
private void OnRenderObject()
{
if (!QSBCore.HasWokenUp || !Player.PlayerStates.IsReady || !QSBCore.DebugMode || !QSBCore.ShowLinesInDebug)
{
return;
}
Popcron.Gizmos.Frustum(Player.Camera);
} }
public override bool IsReady => Locator.GetPlayerTransform() != null public override bool IsReady => Locator.GetPlayerTransform() != null

View File

@ -5,7 +5,7 @@ using UnityEngine;
namespace QSB.Player.TransformSync namespace QSB.Player.TransformSync
{ {
public class PlayerTransformSync : SyncObjectTransformSync public class PlayerTransformSync : QSBNetworkTransform
{ {
public static PlayerTransformSync LocalInstance { get; private set; } public static PlayerTransformSync LocalInstance { get; private set; }
@ -14,10 +14,15 @@ namespace QSB.Player.TransformSync
public override void OnStartLocalPlayer() public override void OnStartLocalPlayer()
=> LocalInstance = this; => LocalInstance = this;
protected override void OnDestroy() public override void Start()
{
base.Start();
Player.TransformSync = this;
}
protected void OnDestroy()
{ {
QSBPlayerManager.OnRemovePlayer?.Invoke(PlayerId); QSBPlayerManager.OnRemovePlayer?.Invoke(PlayerId);
base.OnDestroy();
if (QSBPlayerManager.PlayerExists(PlayerId)) if (QSBPlayerManager.PlayerExists(PlayerId))
{ {
Player.HudMarker?.Remove(); Player.HudMarker?.Remove();
@ -28,7 +33,7 @@ namespace QSB.Player.TransformSync
private Transform GetPlayerModel() => private Transform GetPlayerModel() =>
Locator.GetPlayerTransform().Find("Traveller_HEA_Player_v2"); Locator.GetPlayerTransform().Find("Traveller_HEA_Player_v2");
protected override Transform InitLocalTransform() protected override GameObject InitLocalTransform()
{ {
SectorSync.SetSectorDetector(Locator.GetPlayerSectorDetector()); SectorSync.SetSectorDetector(Locator.GetPlayerSectorDetector());
var body = GetPlayerModel(); var body = GetPlayerModel();
@ -38,10 +43,10 @@ namespace QSB.Player.TransformSync
Player.Body = body.gameObject; Player.Body = body.gameObject;
return body; return body.gameObject;
} }
protected override Transform InitRemoteTransform() protected override GameObject InitRemoteTransform()
{ {
var body = Instantiate(GetPlayerModel()); var body = Instantiate(GetPlayerModel());
@ -55,17 +60,7 @@ namespace QSB.Player.TransformSync
Player.Body = body.gameObject; Player.Body = body.gameObject;
return body; return body.gameObject;
}
private void OnRenderObject()
{
if (!QSBCore.HasWokenUp || !Player.PlayerStates.IsReady || !QSBCore.DebugMode || !QSBCore.ShowLinesInDebug)
{
return;
}
Popcron.Gizmos.Line(ReferenceSector.Position, Player.Body.transform.position, Color.blue, true);
Popcron.Gizmos.Sphere(ReferenceSector.Position, 5f, Color.cyan);
} }
public override bool IsReady => Locator.GetPlayerTransform() != null public override bool IsReady => Locator.GetPlayerTransform() != null

View File

@ -7,25 +7,29 @@ using UnityEngine;
namespace QSB.ProbeSync.TransformSync namespace QSB.ProbeSync.TransformSync
{ {
public class PlayerProbeSync : SyncObjectTransformSync public class PlayerProbeSync : QSBNetworkTransform
{ {
private Transform _disabledSocket; public static PlayerProbeSync LocalInstance { get; private set; }
protected override float DistanceLeeway => 10f;
public override void OnStartAuthority()
=> LocalInstance = this;
private Transform GetProbe() => private Transform GetProbe() =>
Locator.GetProbe().transform.Find("CameraPivot").Find("Geometry"); Locator.GetProbe().transform.Find("CameraPivot").Find("Geometry");
protected override Transform InitLocalTransform() protected override GameObject InitLocalTransform()
{ {
SectorSync.SetSectorDetector(Locator.GetProbe().GetSectorDetector()); SectorSync.SetSectorDetector(Locator.GetProbe().GetSectorDetector());
var body = GetProbe(); var body = GetProbe();
SetSocket(Player.CameraBody.transform);
Player.ProbeBody = body.gameObject; Player.ProbeBody = body.gameObject;
return body; return body.gameObject;
} }
protected override Transform InitRemoteTransform() protected override GameObject InitRemoteTransform()
{ {
var probe = GetProbe(); var probe = GetProbe();
@ -42,51 +46,11 @@ namespace QSB.ProbeSync.TransformSync
PlayerToolsManager.CreateProbe(body, Player); PlayerToolsManager.CreateProbe(body, Player);
QSBCore.UnityEvents.RunWhen(
() => Player.ProbeLauncher != null,
() => SetSocket(Player.ProbeLauncher.ToolGameObject.transform));
Player.ProbeBody = body.gameObject; Player.ProbeBody = body.gameObject;
return body; return body.gameObject;
} }
private void SetSocket(Transform socket)
{
DebugLog.DebugWrite($"Set DisabledSocket of id:{PlayerId}.");
_disabledSocket = socket;
}
/*
protected override void UpdateTransform()
{
base.UpdateTransform();
if (Player == null)
{
DebugLog.ToConsole($"Player is null for {AttachedNetId}!", MessageType.Error);
return;
}
if (_disabledSocket == null)
{
DebugLog.ToConsole($"DisabledSocket is null for {PlayerId}! (ProbeLauncher null? : {Player.ProbeLauncher == null})", MessageType.Error);
return;
}
if (Player.PlayerStates.ProbeActive || ReferenceSector?.AttachedObject == null)
{
return;
}
if (HasAuthority)
{
transform.position = ReferenceSector.Transform.InverseTransformPoint(_disabledSocket.position);
return;
}
if (SyncedTransform.position == Vector3.zero)
{
return;
}
SyncedTransform.localPosition = ReferenceSector.Transform.InverseTransformPoint(_disabledSocket.position);
}
*/
public override bool IsReady => Locator.GetProbe() != null public override bool IsReady => Locator.GetProbe() != null
&& Player != null && Player != null
&& QSBPlayerManager.PlayerExists(Player.PlayerId) && QSBPlayerManager.PlayerExists(Player.PlayerId)

View File

@ -231,6 +231,7 @@
<Compile Include="StatueSync\Patches\StatuePatches.cs" /> <Compile Include="StatueSync\Patches\StatuePatches.cs" />
<Compile Include="StatueSync\StatueManager.cs" /> <Compile Include="StatueSync\StatueManager.cs" />
<Compile Include="RoastingSync\TransformSync\RoastingStickTransformSync.cs" /> <Compile Include="RoastingSync\TransformSync\RoastingStickTransformSync.cs" />
<Compile Include="TransformSync\QSBNetworkTransform.cs" />
<Compile Include="TranslationSync\Events\SetAsTranslatedEvent.cs" /> <Compile Include="TranslationSync\Events\SetAsTranslatedEvent.cs" />
<Compile Include="TranslationSync\Events\SetAsTranslatedMessage.cs" /> <Compile Include="TranslationSync\Events\SetAsTranslatedMessage.cs" />
<Compile Include="TranslationSync\NomaiTextType.cs" /> <Compile Include="TranslationSync\NomaiTextType.cs" />
@ -247,7 +248,6 @@
<Compile Include="ProbeSync\Events\PlayerProbeEvent.cs" /> <Compile Include="ProbeSync\Events\PlayerProbeEvent.cs" />
<Compile Include="Player\Events\PlayerReadyEvent.cs" /> <Compile Include="Player\Events\PlayerReadyEvent.cs" />
<Compile Include="OrbSync\TransformSync\NomaiOrbTransformSync.cs" /> <Compile Include="OrbSync\TransformSync\NomaiOrbTransformSync.cs" />
<Compile Include="Player\Events\PlayerSectorEvent.cs" />
<Compile Include="Player\Events\PlayerStatesRequestEvent.cs" /> <Compile Include="Player\Events\PlayerStatesRequestEvent.cs" />
<Compile Include="Animation\Events\PlayerSuitEvent.cs" /> <Compile Include="Animation\Events\PlayerSuitEvent.cs" />
<Compile Include="TimeSync\Events\ServerTimeEvent.cs" /> <Compile Include="TimeSync\Events\ServerTimeEvent.cs" />
@ -262,7 +262,6 @@
<Compile Include="Messaging\ToggleMessage.cs" /> <Compile Include="Messaging\ToggleMessage.cs" />
<Compile Include="SectorSync\WorldObjects\QSBSector.cs" /> <Compile Include="SectorSync\WorldObjects\QSBSector.cs" />
<Compile Include="SectorSync\SectorSync.cs" /> <Compile Include="SectorSync\SectorSync.cs" />
<Compile Include="TransformSync\SyncObjectTransformSync.cs" />
<Compile Include="Utility\CustomCallbacks.cs" /> <Compile Include="Utility\CustomCallbacks.cs" />
<Compile Include="Utility\DebugBoxManager.cs" /> <Compile Include="Utility\DebugBoxManager.cs" />
<Compile Include="Utility\DebugZOverride.cs" /> <Compile Include="Utility\DebugZOverride.cs" />

View File

@ -11,6 +11,7 @@ using QSB.Patches;
using QSB.Player; using QSB.Player;
using QSB.Player.TransformSync; using QSB.Player.TransformSync;
using QSB.PoolSync; using QSB.PoolSync;
using QSB.ProbeSync.TransformSync;
using QSB.QuantumSync; using QSB.QuantumSync;
using QSB.QuantumSync.WorldObjects; using QSB.QuantumSync.WorldObjects;
using QSB.SectorSync; using QSB.SectorSync;
@ -146,13 +147,9 @@ namespace QSB
} }
var offset3 = 10f; var offset3 = 10f;
GUI.Label(new Rect(420, offset3, 200f, 20f), $"Current synced sector :"); GUI.Label(new Rect(420, offset3, 400f, 20f), $"Current sector : {PlayerTransformSync.LocalInstance.ReferenceSector.Name}");
offset3 += _debugLineSpacing; offset3 += _debugLineSpacing;
var sector = PlayerTransformSync.LocalInstance.ReferenceSector; GUI.Label(new Rect(420, offset3, 400f, 20f), $"Probe sector : {PlayerProbeSync.LocalInstance.ReferenceSector.Name}");
var text = sector == null
? "NULL SECTOR"
: $"{sector.AttachedObject.name} : {sector.IsFakeSector}";
GUI.Label(new Rect(420, offset3, 400f, 20f), $"- {text}");
offset3 += _debugLineSpacing; offset3 += _debugLineSpacing;
var offset2 = 10f; var offset2 = 10f;
@ -169,16 +166,14 @@ namespace QSB
return; return;
} }
GUI.Label(new Rect(220, offset, 200f, 20f), $"QM Illuminated : {Locator.GetQuantumMoon().IsIlluminated()}"); GUI.Label(new Rect(220, offset, 200f, 20f), $"Probe Active : {Locator.GetProbe().gameObject.activeInHierarchy}");
offset += _debugLineSpacing; offset += _debugLineSpacing;
GUI.Label(new Rect(220, offset, 200f, 20f), $"Shrine player in dark? : {QuantumManager.Instance.Shrine.IsPlayerInDarkness()}"); GUI.Label(new Rect(220, offset, 200f, 20f), $"Player positions :");
offset += _debugLineSpacing; offset += _debugLineSpacing;
GUI.Label(new Rect(220, offset, 200f, 20f), $"QM Visible by :"); foreach (var player in QSBPlayerManager.PlayerList.Where(x => x.PlayerStates.IsReady))
offset += _debugLineSpacing;
var tracker = Locator.GetQuantumMoon().GetValue<ShapeVisibilityTracker>("_visibilityTracker");
foreach (var player in QSBPlayerManager.GetPlayersWithCameras())
{ {
GUI.Label(new Rect(220, offset, 200f, 20f), $" - {player.PlayerId} : {tracker.GetType().GetMethod("IsInFrustum", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(tracker, new object[] { player.Camera.GetFrustumPlanes() })}"); var networkTransform = player.TransformSync;
GUI.Label(new Rect(220, offset, 400f, 20f), $"- {player.PlayerId} : {networkTransform.transform.localPosition} from {networkTransform.ReferenceSector.Name}");
offset += _debugLineSpacing; offset += _debugLineSpacing;
} }

View File

@ -104,8 +104,6 @@ namespace QSB
private void SetupNetworkTransform(GameObject go) private void SetupNetworkTransform(GameObject go)
{ {
var trans = go.AddComponent<QNetworkTransform>();
trans.SyncRotationAxis = QNetworkTransform.AxisSyncMode.AxisXYZ;
foreach (var item in go.GetComponents<NetworkTransformChild>()) foreach (var item in go.GetComponents<NetworkTransformChild>())
{ {
var child = go.AddComponent<QNetworkTransformChild>(); var child = go.AddComponent<QNetworkTransformChild>();

View File

@ -1,13 +1,12 @@
using OWML.Utils; using OWML.Utils;
using QSB.Player; using QSB.Player;
using QSB.TransformSync; using QSB.TransformSync;
using QSB.Utility;
using System.Linq; using System.Linq;
using UnityEngine; using UnityEngine;
namespace QSB.RoastingSync.TransformSync namespace QSB.RoastingSync.TransformSync
{ {
internal class RoastingStickTransformSync : SyncObjectTransformSync internal class RoastingStickTransformSync : QSBNetworkTransform
{ {
private Transform _stickTip; private Transform _stickTip;
private Transform _networkStickTip => gameObject.transform.GetChild(0); private Transform _networkStickTip => gameObject.transform.GetChild(0);
@ -18,15 +17,15 @@ namespace QSB.RoastingSync.TransformSync
private Transform GetPivot() private Transform GetPivot()
=> Resources.FindObjectsOfTypeAll<RoastingStickController>().First().transform.Find("Stick_Root/Stick_Pivot"); => Resources.FindObjectsOfTypeAll<RoastingStickController>().First().transform.Find("Stick_Root/Stick_Pivot");
protected override Transform InitLocalTransform() protected override GameObject InitLocalTransform()
{ {
var pivot = GetPivot(); var pivot = GetPivot();
Player.RoastingStick = pivot.gameObject; Player.RoastingStick = pivot.gameObject;
_stickTip = pivot.Find("Stick_Tip"); _stickTip = pivot.Find("Stick_Tip");
return pivot; return pivot.gameObject;
} }
protected override Transform InitRemoteTransform() protected override GameObject InitRemoteTransform()
{ {
var newPivot = Instantiate(GetPivot()); var newPivot = Instantiate(GetPivot());
newPivot.parent = null; newPivot.parent = null;
@ -56,9 +55,10 @@ namespace QSB.RoastingSync.TransformSync
Player.Marshmallow = newMarshmallow; Player.Marshmallow = newMarshmallow;
mallowRoot.gameObject.SetActive(true); mallowRoot.gameObject.SetActive(true);
_stickTip = newPivot.Find("Stick_Tip"); _stickTip = newPivot.Find("Stick_Tip");
return newPivot; return newPivot.gameObject;
} }
/*
protected override void UpdateTransform() protected override void UpdateTransform()
{ {
base.UpdateTransform(); base.UpdateTransform();
@ -78,6 +78,7 @@ namespace QSB.RoastingSync.TransformSync
_stickTip.localPosition = Vector3.SmoothDamp(_stickTip.localPosition, _networkStickTip.localPosition, ref _positionSmoothVelocity, SmoothTime); _stickTip.localPosition = Vector3.SmoothDamp(_stickTip.localPosition, _networkStickTip.localPosition, ref _positionSmoothVelocity, SmoothTime);
_stickTip.localRotation = QuaternionHelper.SmoothDamp(_stickTip.localRotation, _networkStickTip.localRotation, ref _rotationSmoothVelocity, SmoothTime); _stickTip.localRotation = QuaternionHelper.SmoothDamp(_stickTip.localRotation, _networkStickTip.localRotation, ref _rotationSmoothVelocity, SmoothTime);
} }
*/
public override bool IsReady => Locator.GetPlayerTransform() != null public override bool IsReady => Locator.GetPlayerTransform() != null
&& Player != null && Player != null

View File

@ -1,7 +1,6 @@
using OWML.Common; using OWML.Common;
using QSB.Events;
using QSB.Player;
using QSB.SectorSync.WorldObjects; using QSB.SectorSync.WorldObjects;
using QSB.TransformSync;
using QSB.Utility; using QSB.Utility;
using QSB.WorldSync; using QSB.WorldSync;
using System.Collections.Generic; using System.Collections.Generic;
@ -21,8 +20,17 @@ namespace QSB.SectorSync
public void Invoke() public void Invoke()
{ {
QSBPlayerManager.GetSyncObjects<TransformSync.SyncObjectTransformSync>() foreach (var sync in Resources.FindObjectsOfTypeAll<QSBNetworkTransform>()) // todo : cache these!
.Where(x => x.HasAuthority).ToList().ForEach(CheckTransformSyncSector); {
if (sync.AttachedObject == null)
{
continue;
}
if (sync.HasAuthority && sync.AttachedObject.activeInHierarchy)
{
CheckTransformSyncSector(sync);
}
}
} }
public override void Awake() public override void Awake()
@ -54,14 +62,14 @@ namespace QSB.SectorSync
IsReady = QSBWorldSync.GetWorldObjects<QSBSector>().Any(); IsReady = QSBWorldSync.GetWorldObjects<QSBSector>().Any();
} }
private void CheckTransformSyncSector(TransformSync.SyncObjectTransformSync transformSync) private void CheckTransformSyncSector(QSBNetworkTransform transformSync)
{ {
var syncedTransform = transformSync.SyncedTransform; var syncedTransform = transformSync.AttachedObject;
if (syncedTransform == null || syncedTransform.position == Vector3.zero) if (syncedTransform == null || syncedTransform.transform.position == Vector3.zero)
{ {
return; return;
} }
var closestSector = transformSync.SectorSync.GetClosestSector(syncedTransform); var closestSector = transformSync.SectorSync.GetClosestSector(syncedTransform.transform);
if (closestSector == default(QSBSector)) if (closestSector == default(QSBSector))
{ {
return; return;
@ -71,10 +79,6 @@ namespace QSB.SectorSync
return; return;
} }
transformSync.SetReferenceSector(closestSector); transformSync.SetReferenceSector(closestSector);
SendSector(transformSync.NetId.Value, closestSector);
} }
private void SendSector(uint id, QSBSector sector) =>
QSBEventManager.FireEvent(EventNames.QSBSectorChange, id, sector);
} }
} }

View File

@ -1,4 +1,5 @@
using OWML.Common; using OWML.Common;
using OWML.Utils;
using QSB.SectorSync.WorldObjects; using QSB.SectorSync.WorldObjects;
using QSB.Utility; using QSB.Utility;
using QSB.WorldSync; using QSB.WorldSync;
@ -12,6 +13,7 @@ namespace QSB.SectorSync
{ {
public List<QSBSector> SectorList = new List<QSBSector>(); public List<QSBSector> SectorList = new List<QSBSector>();
private OWRigidbody _attachedOWRigidbody;
private SectorDetector _sectorDetector; private SectorDetector _sectorDetector;
private void OnDestroy() private void OnDestroy()
@ -33,6 +35,12 @@ namespace QSB.SectorSync
_sectorDetector = detector; _sectorDetector = detector;
_sectorDetector.OnEnterSector += AddSector; _sectorDetector.OnEnterSector += AddSector;
_sectorDetector.OnExitSector += RemoveSector; _sectorDetector.OnExitSector += RemoveSector;
_attachedOWRigidbody = _sectorDetector.GetValue<OWRigidbody>("_attachedRigidbody");
if (_attachedOWRigidbody == null)
{
DebugLog.ToConsole($"Warning - OWRigidbody for {_sectorDetector.name} is null!", MessageType.Warning);
}
} }
private void AddSector(Sector sector) private void AddSector(Sector sector)
@ -94,9 +102,12 @@ namespace QSB.SectorSync
{ {
return default; return default;
} }
//var ordered = activeNotNullNotBlacklisted
//.OrderBy(sector => Vector3.Distance(sector.Position, trans.position))
//.ThenBy(sector => GetRelativeVelocity(sector, _attachedOWRigidbody))
//.ThenBy(sector => GetRadius(sector));
var ordered = activeNotNullNotBlacklisted var ordered = activeNotNullNotBlacklisted
.OrderBy(sector => Vector3.Distance(sector.Position, trans.position)) .OrderBy(sector => CalculateSectorScore(sector, trans, _attachedOWRigidbody));
.ThenBy(sector => GetRadius(sector));
if ( if (
// if any fake sectors are *roughly* in the same place as other sectors - we want fake sectors to override other sectors // if any fake sectors are *roughly* in the same place as other sectors - we want fake sectors to override other sectors
@ -111,7 +122,16 @@ namespace QSB.SectorSync
return ordered.FirstOrDefault(); return ordered.FirstOrDefault();
} }
private float GetRadius(QSBSector sector) public static float CalculateSectorScore(QSBSector sector, Transform trans, OWRigidbody rigidbody)
{
var distance = Vector3.Distance(sector.Position, trans.position); // want to be small
var radius = GetRadius(sector); // want to be small
var velocity = GetRelativeVelocity(sector, rigidbody); // want to be small
return (distance * distance) + (radius * radius) + (velocity * velocity);
}
public static float GetRadius(QSBSector sector)
{ {
if (sector == null) if (sector == null)
{ {
@ -128,5 +148,17 @@ namespace QSB.SectorSync
} }
return 0f; return 0f;
} }
public static float GetRelativeVelocity(QSBSector sector, OWRigidbody rigidbody)
{
var sectorRigidBody = sector.AttachedObject.GetOWRigidbody();
if (sectorRigidBody != null && rigidbody != null)
{
var relativeVelocity = sectorRigidBody.GetRelativeVelocity(rigidbody);
var relativeVelocityMagnitude = relativeVelocity.sqrMagnitude; // Remember this is squared for efficiency!
return relativeVelocityMagnitude;
}
return 0;
}
} }
} }

View File

@ -49,6 +49,7 @@ namespace QSB.SectorSync.WorldObjects
{ {
return false; return false;
} }
if (AttachedObject.name == "Sector_Shuttle" || AttachedObject.name == "Sector_NomaiShuttleInterior") if (AttachedObject.name == "Sector_Shuttle" || AttachedObject.name == "Sector_NomaiShuttleInterior")
{ {
if (QSBSceneManager.CurrentScene == OWScene.SolarSystem) if (QSBSceneManager.CurrentScene == OWScene.SolarSystem)

View File

@ -4,17 +4,19 @@ using UnityEngine;
namespace QSB.ShipSync.TransformSync namespace QSB.ShipSync.TransformSync
{ {
public class ShipTransformSync : SyncObjectTransformSync public class ShipTransformSync : QSBNetworkTransform
{ {
protected override float DistanceLeeway => 20f;
private Transform GetShipModel() => Locator.GetShipTransform(); private Transform GetShipModel() => Locator.GetShipTransform();
protected override Transform InitLocalTransform() protected override GameObject InitLocalTransform()
{ {
SectorSync.SetSectorDetector(Locator.GetShipDetector().GetComponent<SectorDetector>()); SectorSync.SetSectorDetector(Locator.GetShipDetector().GetComponent<SectorDetector>());
return GetShipModel().Find("Module_Cockpit/Geo_Cockpit/Cockpit_Geometry/Cockpit_Exterior"); return GetShipModel().Find("Module_Cockpit/Geo_Cockpit/Cockpit_Geometry/Cockpit_Exterior").gameObject;
} }
protected override Transform InitRemoteTransform() protected override GameObject InitRemoteTransform()
{ {
var shipModel = GetShipModel(); var shipModel = GetShipModel();
@ -39,7 +41,7 @@ namespace QSB.ShipSync.TransformSync
= landingGearRight.localPosition = landingGearRight.localPosition
+= Vector3.up * 3.762f; += Vector3.up * 3.762f;
return remoteTransform; return remoteTransform.gameObject;
} }
public override bool IsReady => GetShipModel() != null public override bool IsReady => GetShipModel() != null

View File

@ -0,0 +1,201 @@
using OWML.Common;
using QSB.Player;
using QSB.Player.TransformSync;
using QSB.SectorSync.WorldObjects;
using QSB.Utility;
using QSB.WorldSync;
using QuantumUNET.Components;
using QuantumUNET.Transport;
using System.Linq;
using UnityEngine;
namespace QSB.TransformSync
{
public abstract class QSBNetworkTransform : QNetworkTransform
{
public uint AttachedNetId => NetIdentity?.NetId.Value ?? uint.MaxValue;
public uint PlayerId => NetIdentity.RootIdentity?.NetId.Value ?? NetIdentity.NetId.Value;
public PlayerInfo Player => QSBPlayerManager.GetPlayer(PlayerId);
public QSBSector ReferenceSector { get; set; }
public GameObject AttachedObject { get; set; }
public SectorSync.SectorSync SectorSync { get; private set; }
public abstract bool IsReady { get; }
protected abstract GameObject InitLocalTransform();
protected abstract GameObject InitRemoteTransform();
private bool _isInitialized;
private const float SmoothTime = 0.1f;
protected virtual float DistanceLeeway { get; } = 5f;
private float _previousDistance;
private Vector3 _positionSmoothVelocity;
private Quaternion _rotationSmoothVelocity;
public virtual void Start()
{
var lowestBound = Resources.FindObjectsOfTypeAll<PlayerTransformSync>()
.Where(x => x.NetId.Value <= NetId.Value).OrderBy(x => x.NetId.Value).Last();
NetIdentity.SetRootIdentity(lowestBound.NetIdentity);
SectorSync = gameObject.AddComponent<SectorSync.SectorSync>();
DontDestroyOnLoad(gameObject);
}
protected void Init()
{
AttachedObject = HasAuthority ? InitLocalTransform() : InitRemoteTransform();
SetReferenceSector(SectorSync.GetClosestSector(AttachedObject.transform));
_isInitialized = true;
}
public override void SerializeTransform(QNetworkWriter writer)
{
if (ReferenceSector != null)
{
writer.Write(ReferenceSector.ObjectId);
}
else
{
writer.Write(-1);
}
writer.Write(transform.localPosition);
SerializeRotation(writer, transform.localRotation);
_prevPosition = transform.localPosition;
_prevRotation = transform.localRotation;
}
public override void DeserializeTransform(QNetworkReader reader)
{
if (!QSBCore.HasWokenUp)
{
reader.ReadInt32();
reader.ReadVector3();
DeserializeRotation(reader);
return;
}
var sectorId = reader.ReadInt32();
var sector = sectorId == -1
? null
: QSBWorldSync.GetWorldFromId<QSBSector>(sectorId);
if (sector != ReferenceSector)
{
SetReferenceSector(sector);
}
var localPosition = reader.ReadVector3();
var localRotation = DeserializeRotation(reader);
if (HasAuthority)
{
return;
}
transform.localPosition = localPosition;
transform.localRotation = localRotation;
if (transform.position == Vector3.zero)
{
DebugLog.ToConsole($"Warning - {PlayerId}.{GetType().Name} at (0,0,0)! - Given localPosition was {localPosition} at sector {sector?.Name}", MessageType.Warning);
}
}
public override void Update()
{
if (!_isInitialized && IsReady)
{
DebugLog.ToConsole($"Warning - {PlayerId}.{GetType().Name} is not initialized and ready.", MessageType.Warning);
Init();
}
else if (_isInitialized && !IsReady)
{
DebugLog.ToConsole($"Warning - {PlayerId}.{GetType().Name} is initialized and not ready.", MessageType.Warning);
_isInitialized = false;
return;
}
if (!_isInitialized)
{
return;
}
if (AttachedObject == null)
{
DebugLog.ToConsole($"Warning - AttachedObject {Player.PlayerId}.{GetType().Name} is null.", MessageType.Warning);
return;
}
UpdateTransform();
base.Update();
}
protected virtual void UpdateTransform()
{
if (HasAuthority)
{
transform.position = AttachedObject.transform.position;
transform.rotation = AttachedObject.transform.rotation;
}
else
{
AttachedObject.transform.localPosition = SmartSmoothDamp(AttachedObject.transform.localPosition, transform.localPosition);
AttachedObject.transform.localRotation = QuaternionHelper.SmoothDamp(AttachedObject.transform.localRotation, transform.localRotation, ref _rotationSmoothVelocity, SmoothTime);
}
}
public override bool HasMoved()
{
var displacementMagnitude = (transform.localPosition - _prevPosition).magnitude;
return displacementMagnitude > 1E-05f
|| Quaternion.Angle(transform.localRotation, _prevRotation) > 1E-05f;
}
public void SetReferenceSector(QSBSector sector)
{
if (ReferenceSector == sector)
{
return;
}
ReferenceSector = sector;
transform.SetParent(sector.Transform, true);
if (!HasAuthority && AttachedObject != null)
{
AttachedObject.transform.SetParent(sector.Transform, true);
AttachedObject.transform.localScale = GetType() == typeof(PlayerTransformSync)
? Vector3.one / 10
: Vector3.one;
}
}
private Vector3 SmartSmoothDamp(Vector3 currentPosition, Vector3 targetPosition)
{
var distance = Vector3.Distance(currentPosition, targetPosition);
if (distance > _previousDistance + DistanceLeeway)
{
// moved too far! assume sector sync warp / actual warp
_previousDistance = distance;
return targetPosition;
}
_previousDistance = distance;
return Vector3.SmoothDamp(currentPosition, targetPosition, ref _positionSmoothVelocity, SmoothTime);
}
private void OnRenderObject()
{
if (!QSBCore.HasWokenUp || !QSBCore.DebugMode || !QSBCore.ShowLinesInDebug || !IsReady)
{
return;
}
Popcron.Gizmos.Cube(transform.position, transform.rotation, Vector3.one / 2, Color.red);
Popcron.Gizmos.Cube(AttachedObject.transform.position, AttachedObject.transform.rotation, Vector3.one / 2, Color.green);
Popcron.Gizmos.Line(AttachedObject.transform.position, ReferenceSector.Transform.position, Color.cyan);
}
}
}

View File

@ -1,183 +0,0 @@
using OWML.Common;
using QSB.Player;
using QSB.Player.TransformSync;
using QSB.SectorSync.WorldObjects;
using QSB.Utility;
using System.Linq;
using UnityEngine;
namespace QSB.TransformSync
{
public abstract class SyncObjectTransformSync : PlayerSyncObject
{
public abstract bool IsReady { get; }
protected abstract Transform InitLocalTransform();
protected abstract Transform InitRemoteTransform();
public Transform SyncedTransform { get; private set; }
public QSBSector ReferenceSector { get; set; }
public SectorSync.SectorSync SectorSync { get; private set; }
private const float SmoothTime = 0.1f;
private const float DistanceLeeway = 5f;
private bool _isInitialized;
private Vector3 _positionSmoothVelocity;
private Quaternion _rotationSmoothVelocity;
private bool _isVisible;
private float _previousDistance;
protected override void Start()
{
base.Start();
var lowestBound = QSBPlayerManager.GetSyncObjects<PlayerTransformSync>()
.Where(x => x.NetId.Value <= NetId.Value).OrderBy(x => x.NetId.Value).Last();
NetIdentity.SetRootIdentity(lowestBound.NetIdentity);
SectorSync = gameObject.AddComponent<SectorSync.SectorSync>();
DontDestroyOnLoad(gameObject);
QSBSceneManager.OnSceneLoaded += OnSceneLoaded;
}
protected override void OnDestroy()
{
base.OnDestroy();
if (!HasAuthority && SyncedTransform != null)
{
Destroy(SyncedTransform.gameObject);
}
QSBSceneManager.OnSceneLoaded -= OnSceneLoaded;
if (SectorSync != null)
{
Destroy(SectorSync);
}
}
private void OnSceneLoaded(OWScene scene, bool isInUniverse) =>
_isInitialized = false;
protected void Init()
{
SyncedTransform = HasAuthority ? InitLocalTransform() : InitRemoteTransform();
SetReferenceSector(SectorSync.GetClosestSector(SyncedTransform));
_isInitialized = true;
_isVisible = true;
}
public void OnRenderObject()
{
if (!QSBCore.HasWokenUp || !QSBCore.DebugMode || !QSBCore.ShowLinesInDebug || !IsReady)
{
return;
}
Popcron.Gizmos.Cube(SyncedTransform.position, SyncedTransform.rotation, Vector3.one / 2, Color.green);
Popcron.Gizmos.Cube(transform.position, transform.rotation, Vector3.one / 2, Color.red);
Popcron.Gizmos.Line(SyncedTransform.position, transform.position, Color.magenta);
Popcron.Gizmos.Cube(ReferenceSector.Position, ReferenceSector.Transform.rotation, Vector3.one, Color.cyan);
Popcron.Gizmos.Line(SyncedTransform.position, ReferenceSector.Position, Color.blue);
}
public void Update()
{
if (!_isInitialized && IsReady)
{
Init();
}
else if (_isInitialized && !IsReady)
{
_isInitialized = false;
return;
}
if (!_isInitialized)
{
return;
}
if (SyncedTransform == null)
{
DebugLog.ToConsole($"Warning - SyncedTransform {Player.PlayerId}.{GetType().Name} is null.", MessageType.Warning);
return;
}
UpdateTransform();
}
protected virtual void UpdateTransform()
{
if (HasAuthority) // If this script is attached to the client's own body on the client's side.
{
if (ReferenceSector == null || ReferenceSector.AttachedObject == null)
{
DebugLog.ToConsole($"Error - ReferenceSector has null value for {Player.PlayerId}.{GetType().Name}", MessageType.Error);
return;
}
transform.position = ReferenceSector.Transform.InverseTransformPoint(SyncedTransform.position);
transform.rotation = ReferenceSector.Transform.InverseTransformRotation(SyncedTransform.rotation);
return;
}
// If this script is attached to any other body, eg the representations of other players
if (SyncedTransform.position == Vector3.zero)
{
Hide();
}
else
{
Show();
}
SyncedTransform.localPosition = SmartSmoothDamp(SyncedTransform.localPosition, transform.position);
SyncedTransform.localRotation = QuaternionHelper.SmoothDamp(SyncedTransform.localRotation, transform.rotation, ref _rotationSmoothVelocity, SmoothTime);
}
private Vector3 SmartSmoothDamp(Vector3 currentPosition, Vector3 targetPosition)
{
var distance = Vector3.Distance(currentPosition, targetPosition);
if (distance > _previousDistance + DistanceLeeway)
{
// moved too far! assume sector sync warp / actual warp
_previousDistance = distance;
return targetPosition;
}
_previousDistance = distance;
return Vector3.SmoothDamp(currentPosition, targetPosition, ref _positionSmoothVelocity, SmoothTime);
}
public void SetReferenceSector(QSBSector sector)
{
if (sector == ReferenceSector || sector == default(QSBSector))
{
return;
}
_positionSmoothVelocity = Vector3.zero;
ReferenceSector = sector;
if (!HasAuthority)
{
SyncedTransform.SetParent(sector.Transform, true);
transform.position = sector.Transform.InverseTransformPoint(SyncedTransform.position);
transform.rotation = sector.Transform.InverseTransformRotation(SyncedTransform.rotation);
}
}
private void Show()
{
if (!_isVisible)
{
SyncedTransform.gameObject.Show();
_isVisible = true;
}
}
private void Hide()
{
if (_isVisible)
{
SyncedTransform.gameObject.Hide();
_isVisible = false;
}
}
}
}

View File

@ -1,7 +1,4 @@
using OWML.Utils; using OWML.Utils;
using QSB.Player;
using QSB.Player.TransformSync;
using System.Linq;
using UnityEngine; using UnityEngine;
namespace QSB.Utility namespace QSB.Utility
@ -27,35 +24,12 @@ namespace QSB.Utility
bridgeVolume.AddObjectToVolume(Locator.GetPlayerCameraDetector()); bridgeVolume.AddObjectToVolume(Locator.GetPlayerCameraDetector());
} }
private void DebugWarpToPlayer(int index)
{
var allPlayers = QSBPlayerManager.PlayerList.Where(x => x != QSBPlayerManager.LocalPlayer).ToList();
if (allPlayers.Count <= index)
{
return;
}
var player = allPlayers[index];
var localPlayer = Locator.GetPlayerBody();
localPlayer.WarpToPositionRotation(player.CameraBody.transform.position, player.CameraBody.transform.rotation);
var playerTransformSync = QSBPlayerManager.GetSyncObject<PlayerTransformSync>(player.PlayerId);
var syncedRigidbody = playerTransformSync.ReferenceSector.AttachedObject.GetOWRigidbody();
localPlayer.SetVelocity(syncedRigidbody.GetPointVelocity(player.Body.transform.position));
}
public void Update() public void Update()
{ {
if (!QSBCore.DebugMode) if (!QSBCore.DebugMode)
{ {
return; return;
} }
if (Input.GetKeyDown(KeyCode.Keypad0))
{
DebugWarpToPlayer(0);
}
if (Input.GetKeyDown(KeyCode.Keypad1))
{
DebugWarpToPlayer(1);
}
if (Input.GetKeyDown(KeyCode.Keypad7)) if (Input.GetKeyDown(KeyCode.Keypad7))
{ {
GoToVessel(); GoToVessel();

View File

@ -23,6 +23,9 @@ namespace QSB.Utility
} }
} }
public static Quaternion TransformRotation(this Transform transform, Quaternion localRotation)
=> transform.rotation * localRotation;
public static GameObject InstantiateInactive(this GameObject original) public static GameObject InstantiateInactive(this GameObject original)
{ {
original.SetActive(false); original.SetActive(false);

View File

@ -8,23 +8,26 @@ namespace QuantumUNET.Components
{ {
public class QNetworkTransform : QNetworkBehaviour public class QNetworkTransform : QNetworkBehaviour
{ {
public float SendInterval { get; set; } = 0.1f; public float SendInterval { get; set; } = 0.05f;
public AxisSyncMode SyncRotationAxis { get; set; } = AxisSyncMode.AxisXYZ;
public CompressionSyncMode RotationSyncCompression { get; set; } = CompressionSyncMode.None;
public ClientMoveCallback3D clientMoveCallback3D { get; set; }
public float LastSyncTime { get; private set; } public float LastSyncTime { get; private set; }
private float _lastClientSendTime;
protected Vector3 _prevPosition;
protected Quaternion _prevRotation;
private QNetworkWriter _localTransformWriter;
public void Awake() public void Awake()
{ {
m_PrevPosition = transform.position; _prevPosition = transform.position;
m_PrevRotation = transform.rotation; _prevRotation = transform.rotation;
if (LocalPlayerAuthority) if (LocalPlayerAuthority)
{ {
m_LocalTransformWriter = new QNetworkWriter(); _localTransformWriter = new QNetworkWriter();
} }
} }
public override void OnStartServer() => LastSyncTime = 0f; public override void OnStartServer()
=> LastSyncTime = 0f;
public override bool OnSerialize(QNetworkWriter writer, bool initialState) public override bool OnSerialize(QNetworkWriter writer, bool initialState)
{ {
@ -37,21 +40,10 @@ namespace QuantumUNET.Components
} }
writer.WritePackedUInt32(1U); writer.WritePackedUInt32(1U);
} }
SerializeModeTransform(writer); SerializeTransform(writer);
return true; return true;
} }
private void SerializeModeTransform(QNetworkWriter writer)
{
writer.Write(transform.position);
if (SyncRotationAxis != AxisSyncMode.None)
{
SerializeRotation3D(writer, transform.rotation, SyncRotationAxis, RotationSyncCompression);
}
m_PrevPosition = transform.position;
m_PrevRotation = transform.rotation;
}
public override void OnDeserialize(QNetworkReader reader, bool initialState) public override void OnDeserialize(QNetworkReader reader, bool initialState)
{ {
if (!IsServer || !QNetworkServer.localClientActive) if (!IsServer || !QNetworkServer.localClientActive)
@ -63,116 +55,78 @@ namespace QuantumUNET.Components
return; return;
} }
} }
UnserializeModeTransform(reader, initialState); DeserializeTransform(reader);
LastSyncTime = Time.time; LastSyncTime = Time.time;
} }
} }
private void UnserializeModeTransform(QNetworkReader reader, bool initialState) public virtual void SerializeTransform(QNetworkWriter writer)
{
writer.Write(transform.position);
SerializeRotation(writer, transform.rotation);
_prevPosition = transform.position;
_prevRotation = transform.rotation;
}
public virtual void DeserializeTransform(QNetworkReader reader)
{ {
if (HasAuthority) if (HasAuthority)
{ {
reader.ReadVector3(); reader.ReadVector3();
if (SyncRotationAxis != AxisSyncMode.None) DeserializeRotation(reader);
{ return;
UnserializeRotation3D(reader, SyncRotationAxis, RotationSyncCompression);
}
}
else if (IsServer && clientMoveCallback3D != null)
{
var position = reader.ReadVector3();
var zero = Vector3.zero;
var rotation = Quaternion.identity;
if (SyncRotationAxis != AxisSyncMode.None)
{
rotation = UnserializeRotation3D(reader, SyncRotationAxis, RotationSyncCompression);
}
if (clientMoveCallback3D(ref position, ref zero, ref rotation))
{
transform.position = position;
if (SyncRotationAxis != AxisSyncMode.None)
{
transform.rotation = rotation;
}
}
}
else
{
transform.position = reader.ReadVector3();
if (SyncRotationAxis != AxisSyncMode.None)
{
transform.rotation = UnserializeRotation3D(reader, SyncRotationAxis, RotationSyncCompression);
}
} }
transform.position = reader.ReadVector3();
transform.rotation = DeserializeRotation(reader);
} }
private void FixedUpdate() private void FixedUpdate()
{ {
if (IsServer) if (!IsServer)
{ {
FixedUpdateServer(); return;
}
if (SyncVarDirtyBits != 0U)
{
return;
}
if (!QNetworkServer.active)
{
return;
}
if (GetNetworkSendInterval() != 0f && HasMoved())
{
SetDirtyBit(1U);
} }
} }
private void FixedUpdateServer() public virtual void Update()
{ {
if (SyncVarDirtyBits == 0U) if (!HasAuthority || !LocalPlayerAuthority)
{ {
if (QNetworkServer.active) return;
{ }
if (IsServer)
{ if (QNetworkServer.active)
if (GetNetworkSendInterval() != 0f) {
{ return;
if (HasMoved()) }
{
SetDirtyBit(1U); if (Time.time - _lastClientSendTime > GetNetworkSendInterval())
} {
} SendTransform();
} _lastClientSendTime = Time.time;
}
} }
} }
private void Update() public virtual bool HasMoved()
{ {
if (HasAuthority) var displacementMagnitude = (transform.position - _prevPosition).magnitude;
{ return displacementMagnitude > 1E-05f
if (LocalPlayerAuthority) || Quaternion.Angle(transform.rotation, _prevRotation) > 1E-05f;
{
if (!QNetworkServer.active)
{
if (Time.time - m_LastClientSendTime > GetNetworkSendInterval())
{
SendTransform();
m_LastClientSendTime = Time.time;
}
}
}
}
}
private bool HasMoved()
{
var num = (transform.position - m_PrevPosition).magnitude;
bool result;
if (num > 1E-05f)
{
result = true;
}
else
{
num = Quaternion.Angle(transform.rotation, m_PrevRotation);
if (num > 1E-05f)
{
result = true;
}
else
{
result = num > 1E-05f;
}
}
return result;
} }
[Client] [Client]
@ -180,13 +134,14 @@ namespace QuantumUNET.Components
{ {
if (HasMoved() && QClientScene.readyConnection != null) if (HasMoved() && QClientScene.readyConnection != null)
{ {
m_LocalTransformWriter.StartMessage(6); _localTransformWriter.StartMessage(QMsgType.LocalPlayerTransform);
m_LocalTransformWriter.Write(NetId); _localTransformWriter.Write(NetId);
SerializeModeTransform(m_LocalTransformWriter); SerializeTransform(_localTransformWriter);
m_PrevPosition = transform.position; _prevPosition = transform.position;
m_PrevRotation = transform.rotation; _prevRotation = transform.rotation;
m_LocalTransformWriter.FinishMessage(); _localTransformWriter.FinishMessage();
QClientScene.readyConnection.SendWriter(m_LocalTransformWriter, GetNetworkChannel());
QClientScene.readyConnection.SendWriter(_localTransformWriter, GetNetworkChannel());
} }
} }
@ -197,281 +152,63 @@ namespace QuantumUNET.Components
if (gameObject == null) if (gameObject == null)
{ {
QLog.Warning("Received NetworkTransform data for GameObject that doesn't exist"); QLog.Warning("Received NetworkTransform data for GameObject that doesn't exist");
return;
}
var component = gameObject.GetComponent<QNetworkTransform>();
if (component == null)
{
QLog.Warning("HandleTransform null target");
return;
}
if (!component.LocalPlayerAuthority)
{
QLog.Warning("HandleTransform no localPlayerAuthority");
return;
}
if (netMsg.Connection.ClientOwnedObjects == null)
{
QLog.Warning("HandleTransform object not owned by connection");
return;
}
if (netMsg.Connection.ClientOwnedObjects.Contains(networkInstanceId))
{
component.DeserializeTransform(netMsg.Reader);
component.LastSyncTime = Time.time;
} }
else else
{ {
var component = gameObject.GetComponent<QNetworkTransform>(); QLog.Warning(
if (component == null) $"HandleTransform netId:{networkInstanceId} is not for a valid player");
{
QLog.Warning("HandleTransform null target");
}
else if (!component.LocalPlayerAuthority)
{
QLog.Warning("HandleTransform no localPlayerAuthority");
}
else if (netMsg.Connection.ClientOwnedObjects == null)
{
QLog.Warning("HandleTransform object not owned by connection");
}
else if (netMsg.Connection.ClientOwnedObjects.Contains(networkInstanceId))
{
component.UnserializeModeTransform(netMsg.Reader, false);
component.LastSyncTime = Time.time;
}
else
{
QLog.Warning(
$"HandleTransform netId:{networkInstanceId} is not for a valid player");
}
} }
} }
private static void WriteAngle(QNetworkWriter writer, float angle, CompressionSyncMode compression) public static void SerializeRotation(QNetworkWriter writer, Quaternion rot)
{ {
if (compression != CompressionSyncMode.None) writer.Write(rot.eulerAngles.x);
{ writer.Write(rot.eulerAngles.y);
if (compression != CompressionSyncMode.Low) writer.Write(rot.eulerAngles.z);
{
if (compression == CompressionSyncMode.High)
{
writer.Write((short)angle);
}
}
else
{
writer.Write((short)angle);
}
}
else
{
writer.Write(angle);
}
} }
private static float ReadAngle(QNetworkReader reader, CompressionSyncMode compression) public static Quaternion DeserializeRotation(QNetworkReader reader)
{ {
float result; var rotation = Quaternion.identity;
if (compression != CompressionSyncMode.None) var eulerAngles = Vector3.zero;
{ eulerAngles.Set(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
if (compression != CompressionSyncMode.Low) rotation.eulerAngles = eulerAngles;
{ return rotation;
if (compression != CompressionSyncMode.High)
{
result = 0f;
}
else
{
result = reader.ReadInt16();
}
}
else
{
result = reader.ReadInt16();
}
}
else
{
result = reader.ReadSingle();
}
return result;
} }
public static void SerializeVelocity3D(QNetworkWriter writer, Vector3 velocity, CompressionSyncMode compression) => public override int GetNetworkChannel()
writer.Write(velocity); => 1;
public static void SerializeRotation3D(QNetworkWriter writer, Quaternion rot, AxisSyncMode mode, CompressionSyncMode compression) public override float GetNetworkSendInterval()
{ => SendInterval;
switch (mode)
{
case AxisSyncMode.AxisX:
WriteAngle(writer, rot.eulerAngles.x, compression);
break;
case AxisSyncMode.AxisY: public override void OnStartAuthority()
WriteAngle(writer, rot.eulerAngles.y, compression); => LastSyncTime = 0f;
break;
case AxisSyncMode.AxisZ:
WriteAngle(writer, rot.eulerAngles.z, compression);
break;
case AxisSyncMode.AxisXY:
WriteAngle(writer, rot.eulerAngles.x, compression);
WriteAngle(writer, rot.eulerAngles.y, compression);
break;
case AxisSyncMode.AxisXZ:
WriteAngle(writer, rot.eulerAngles.x, compression);
WriteAngle(writer, rot.eulerAngles.z, compression);
break;
case AxisSyncMode.AxisYZ:
WriteAngle(writer, rot.eulerAngles.y, compression);
WriteAngle(writer, rot.eulerAngles.z, compression);
break;
case AxisSyncMode.AxisXYZ:
WriteAngle(writer, rot.eulerAngles.x, compression);
WriteAngle(writer, rot.eulerAngles.y, compression);
WriteAngle(writer, rot.eulerAngles.z, compression);
break;
}
}
public static void SerializeSpin3D(QNetworkWriter writer, Vector3 angularVelocity, AxisSyncMode mode, CompressionSyncMode compression)
{
switch (mode)
{
case AxisSyncMode.AxisX:
WriteAngle(writer, angularVelocity.x, compression);
break;
case AxisSyncMode.AxisY:
WriteAngle(writer, angularVelocity.y, compression);
break;
case AxisSyncMode.AxisZ:
WriteAngle(writer, angularVelocity.z, compression);
break;
case AxisSyncMode.AxisXY:
WriteAngle(writer, angularVelocity.x, compression);
WriteAngle(writer, angularVelocity.y, compression);
break;
case AxisSyncMode.AxisXZ:
WriteAngle(writer, angularVelocity.x, compression);
WriteAngle(writer, angularVelocity.z, compression);
break;
case AxisSyncMode.AxisYZ:
WriteAngle(writer, angularVelocity.y, compression);
WriteAngle(writer, angularVelocity.z, compression);
break;
case AxisSyncMode.AxisXYZ:
WriteAngle(writer, angularVelocity.x, compression);
WriteAngle(writer, angularVelocity.y, compression);
WriteAngle(writer, angularVelocity.z, compression);
break;
}
}
public static Vector3 UnserializeVelocity3D(QNetworkReader reader, CompressionSyncMode compression) => reader.ReadVector3();
public static Quaternion UnserializeRotation3D(QNetworkReader reader, AxisSyncMode mode, CompressionSyncMode compression)
{
var identity = Quaternion.identity;
var zero = Vector3.zero;
switch (mode)
{
case AxisSyncMode.AxisX:
zero.Set(ReadAngle(reader, compression), 0f, 0f);
identity.eulerAngles = zero;
break;
case AxisSyncMode.AxisY:
zero.Set(0f, ReadAngle(reader, compression), 0f);
identity.eulerAngles = zero;
break;
case AxisSyncMode.AxisZ:
zero.Set(0f, 0f, ReadAngle(reader, compression));
identity.eulerAngles = zero;
break;
case AxisSyncMode.AxisXY:
zero.Set(ReadAngle(reader, compression), ReadAngle(reader, compression), 0f);
identity.eulerAngles = zero;
break;
case AxisSyncMode.AxisXZ:
zero.Set(ReadAngle(reader, compression), 0f, ReadAngle(reader, compression));
identity.eulerAngles = zero;
break;
case AxisSyncMode.AxisYZ:
zero.Set(0f, ReadAngle(reader, compression), ReadAngle(reader, compression));
identity.eulerAngles = zero;
break;
case AxisSyncMode.AxisXYZ:
zero.Set(ReadAngle(reader, compression), ReadAngle(reader, compression), ReadAngle(reader, compression));
identity.eulerAngles = zero;
break;
}
return identity;
}
public static Vector3 UnserializeSpin3D(QNetworkReader reader, AxisSyncMode mode, CompressionSyncMode compression)
{
var zero = Vector3.zero;
switch (mode)
{
case AxisSyncMode.AxisX:
zero.Set(ReadAngle(reader, compression), 0f, 0f);
break;
case AxisSyncMode.AxisY:
zero.Set(0f, ReadAngle(reader, compression), 0f);
break;
case AxisSyncMode.AxisZ:
zero.Set(0f, 0f, ReadAngle(reader, compression));
break;
case AxisSyncMode.AxisXY:
zero.Set(ReadAngle(reader, compression), ReadAngle(reader, compression), 0f);
break;
case AxisSyncMode.AxisXZ:
zero.Set(ReadAngle(reader, compression), 0f, ReadAngle(reader, compression));
break;
case AxisSyncMode.AxisYZ:
zero.Set(0f, ReadAngle(reader, compression), ReadAngle(reader, compression));
break;
case AxisSyncMode.AxisXYZ:
zero.Set(ReadAngle(reader, compression), ReadAngle(reader, compression), ReadAngle(reader, compression));
break;
}
return zero;
}
public override int GetNetworkChannel() => 1;
public override float GetNetworkSendInterval() => SendInterval;
public override void OnStartAuthority() => LastSyncTime = 0f;
private float m_LastClientSendTime;
private Vector3 m_PrevPosition;
private Quaternion m_PrevRotation;
private QNetworkWriter m_LocalTransformWriter;
public enum AxisSyncMode
{
None,
AxisX,
AxisY,
AxisZ,
AxisXY,
AxisXZ,
AxisYZ,
AxisXYZ
}
public enum CompressionSyncMode
{
None,
Low,
High
}
public delegate bool ClientMoveCallback3D(ref Vector3 position, ref Vector3 velocity, ref Quaternion rotation);
} }
} }

View File

@ -11,11 +11,7 @@ namespace QuantumUNET.Components
public Transform Target public Transform Target
{ {
get => m_Target; get => m_Target;
set set => m_Target = value;
{
m_Target = value;
OnValidate();
}
} }
public uint ChildIndex => m_ChildIndex; public uint ChildIndex => m_ChildIndex;
@ -26,18 +22,6 @@ namespace QuantumUNET.Components
set => m_SendInterval = value; set => m_SendInterval = value;
} }
public QNetworkTransform.AxisSyncMode SyncRotationAxis
{
get => m_SyncRotationAxis;
set => m_SyncRotationAxis = value;
}
public QNetworkTransform.CompressionSyncMode RotationSyncCompression
{
get => m_RotationSyncCompression;
set => m_RotationSyncCompression = value;
}
public float MovementThreshold public float MovementThreshold
{ {
get => m_MovementThreshold; get => m_MovementThreshold;
@ -56,89 +40,10 @@ namespace QuantumUNET.Components
set => m_InterpolateMovement = value; set => m_InterpolateMovement = value;
} }
public QNetworkTransform.ClientMoveCallback3D ClientMoveCallback3D
{
get => m_ClientMoveCallback3D;
set => m_ClientMoveCallback3D = value;
}
public float LastSyncTime { get; private set; } public float LastSyncTime { get; private set; }
public Vector3 TargetSyncPosition => m_TargetSyncPosition; public Vector3 TargetSyncPosition => m_TargetSyncPosition;
public Quaternion TargetSyncRotation3D => m_TargetSyncRotation3D; public Quaternion TargetSyncRotation3D => m_TargetSyncRotation3D;
private void OnValidate()
{
if (m_Target != null)
{
var parent = m_Target.parent;
if (parent == null)
{
Debug.LogError("NetworkTransformChild target cannot be the root transform.");
m_Target = null;
return;
}
while (parent.parent != null)
{
parent = parent.parent;
}
m_Root = parent.gameObject.GetComponent<QNetworkTransform>();
if (m_Root == null)
{
Debug.LogError("NetworkTransformChild root must have NetworkTransform");
m_Target = null;
return;
}
}
if (m_Root != null)
{
m_ChildIndex = uint.MaxValue;
var components = m_Root.GetComponents<QNetworkTransformChild>();
var num = 0U;
while (num < (ulong)components.Length)
{
if (components[(int)(UIntPtr)num] == this)
{
m_ChildIndex = num;
break;
}
num += 1U;
}
if (m_ChildIndex == 4294967295U)
{
Debug.LogError("QNetworkTransformChild component must be a child in the same hierarchy");
m_Target = null;
}
}
if (m_SendInterval < 0f)
{
m_SendInterval = 0f;
}
if (m_SyncRotationAxis < QNetworkTransform.AxisSyncMode.None || m_SyncRotationAxis > QNetworkTransform.AxisSyncMode.AxisXYZ)
{
m_SyncRotationAxis = QNetworkTransform.AxisSyncMode.None;
}
if (MovementThreshold < 0f)
{
MovementThreshold = 0f;
}
if (InterpolateRotation < 0f)
{
InterpolateRotation = 0.01f;
}
if (InterpolateRotation > 1f)
{
InterpolateRotation = 1f;
}
if (InterpolateMovement < 0f)
{
InterpolateMovement = 0.01f;
}
if (InterpolateMovement > 1f)
{
InterpolateMovement = 1f;
}
}
private void Awake() private void Awake()
{ {
m_PrevPosition = m_Target.localPosition; m_PrevPosition = m_Target.localPosition;
@ -167,10 +72,7 @@ namespace QuantumUNET.Components
private void SerializeModeTransform(QNetworkWriter writer) private void SerializeModeTransform(QNetworkWriter writer)
{ {
writer.Write(m_Target.localPosition); writer.Write(m_Target.localPosition);
if (m_SyncRotationAxis != QNetworkTransform.AxisSyncMode.None) QNetworkTransform.SerializeRotation(writer, m_Target.localRotation);
{
QNetworkTransform.SerializeRotation3D(writer, m_Target.localRotation, SyncRotationAxis, RotationSyncCompression);
}
m_PrevPosition = m_Target.localPosition; m_PrevPosition = m_Target.localPosition;
m_PrevRotation = m_Target.localRotation; m_PrevRotation = m_Target.localRotation;
} }
@ -197,36 +99,12 @@ namespace QuantumUNET.Components
if (HasAuthority) if (HasAuthority)
{ {
reader.ReadVector3(); reader.ReadVector3();
if (SyncRotationAxis != QNetworkTransform.AxisSyncMode.None) QNetworkTransform.DeserializeRotation(reader);
{
QNetworkTransform.UnserializeRotation3D(reader, SyncRotationAxis, RotationSyncCompression);
}
}
else if (IsServer && m_ClientMoveCallback3D != null)
{
var targetSyncPosition = reader.ReadVector3();
var zero = Vector3.zero;
var targetSyncRotation3D = Quaternion.identity;
if (SyncRotationAxis != QNetworkTransform.AxisSyncMode.None)
{
targetSyncRotation3D = QNetworkTransform.UnserializeRotation3D(reader, SyncRotationAxis, RotationSyncCompression);
}
if (m_ClientMoveCallback3D(ref targetSyncPosition, ref zero, ref targetSyncRotation3D))
{
m_TargetSyncPosition = targetSyncPosition;
if (SyncRotationAxis != QNetworkTransform.AxisSyncMode.None)
{
m_TargetSyncRotation3D = targetSyncRotation3D;
}
}
} }
else else
{ {
m_TargetSyncPosition = reader.ReadVector3(); m_TargetSyncPosition = reader.ReadVector3();
if (SyncRotationAxis != QNetworkTransform.AxisSyncMode.None) m_TargetSyncRotation3D = QNetworkTransform.DeserializeRotation(reader);
{
m_TargetSyncRotation3D = QNetworkTransform.UnserializeRotation3D(reader, SyncRotationAxis, RotationSyncCompression);
}
} }
} }
@ -404,12 +282,9 @@ namespace QuantumUNET.Components
public uint m_ChildIndex; public uint m_ChildIndex;
public QNetworkTransform m_Root; public QNetworkTransform m_Root;
public float m_SendInterval = 0.1f; public float m_SendInterval = 0.1f;
public QNetworkTransform.AxisSyncMode m_SyncRotationAxis = QNetworkTransform.AxisSyncMode.AxisXYZ;
public QNetworkTransform.CompressionSyncMode m_RotationSyncCompression = QNetworkTransform.CompressionSyncMode.None;
public float m_MovementThreshold = 0.001f; public float m_MovementThreshold = 0.001f;
public float m_InterpolateRotation = 0.5f; public float m_InterpolateRotation = 0.5f;
public float m_InterpolateMovement = 0.5f; public float m_InterpolateMovement = 0.5f;
public QNetworkTransform.ClientMoveCallback3D m_ClientMoveCallback3D;
private Vector3 m_TargetSyncPosition; private Vector3 m_TargetSyncPosition;
private Quaternion m_TargetSyncRotation3D; private Quaternion m_TargetSyncRotation3D;
private float m_LastClientSendTime; private float m_LastClientSendTime;

View File

@ -1,5 +1,11 @@
![logo](unknown.png) ![logo](unknown.png)
![GitHub](https://img.shields.io/github/license/misternebula/quantum-space-buddies?style=flat-square)
![GitHub release (latest by date)](https://img.shields.io/github/v/release/misternebula/quantum-space-buddies?style=flat-square)
![GitHub Release Date](https://img.shields.io/github/release-date/misternebula/quantum-space-buddies?label=last%20release&style=flat-square)
![GitHub all releases](https://img.shields.io/github/downloads/misternebula/quantum-space-buddies/total?style=flat-square)
![GitHub last commit (branch)](https://img.shields.io/github/last-commit/misternebula/quantum-space-buddies/dev?label=last%20commit%20to%20dev&style=flat-square)
Quantum Space Buddies (QSB) is a multiplayer mod for Outer Wilds. The mod uses the OWML mod loader and customized UNET code (internally referred to as QNet or QuantumUNET) for networking. Quantum Space Buddies (QSB) is a multiplayer mod for Outer Wilds. The mod uses the OWML mod loader and customized UNET code (internally referred to as QNet or QuantumUNET) for networking.
## License ## License
@ -56,19 +62,36 @@ QSB relies on exact orders of objects found using Resources.FindObjectsOfTypeAll
**NomaiVR compatibility is currently not planned and likely will never happen, due to extensive changes needed to both mods for it to work.** **NomaiVR compatibility is currently not planned and likely will never happen, due to extensive changes needed to both mods for it to work.**
### What is currently synced? ### What is currently synced?
QSB does not operate on a strict server-client relationship. Each player's game is left as a completely seperate game, and individual elemets are synced or patched so they can be controlled though network messages. Right now, the list of synced objects are :
- Geysers | System / Mechanic | Synced? |
- Elevators | :---: | :---: |
- Nomai interface orbs | Anglerfish | No |
- Player animations | Brittle Hollow fragments | No |
- Player tools | Campfires | Yes |
- Player ships | Conversations with NPCs | Yes |
- Player probes | Discovering signals/frequencies | Yes |
- NPC heads in conversations | Eye of the Universe ancient glade | No |
- Ship log | Eye of the Universe instrument hunt | No |
- Discovering frequencies / signals | Eye of the Universe jam session | No |
- Nomai text (Spirals + Computers) | Eye of the Universe quantum lightning | No |
- Picking up/dropping/socketing items | Geysers | Yes |
| Items | Yes |
| Jellyfish | No |
| Marshmallow roasting | Yes |
| Meteors | No |
| Museum statue | Yes |
| NPC animations | Kind of |
| Nomai orbs | Yes |
| Nomai shuttle | Kind of |
| Orbital Probe Cannon (direction) | No |
| Player animation | Kind of |
| Player position | Yes |
| Player tools | Yes |
| Projection pools | Yes |
| Quantum objects | Yes |
| Ship log | Yes |
| Timber Hearth satellite | No |
| Tornadoes | No |
QSB also changes some mechanics of the base game, to better fit a multiplayer experience. These include : QSB also changes some mechanics of the base game, to better fit a multiplayer experience. These include :
- Adding dialogue boxes above NPC and player heads, so other players can "listen in" on conversations. - Adding dialogue boxes above NPC and player heads, so other players can "listen in" on conversations.