mirror of
https://github.com/misternebula/quantum-space-buddies.git
synced 2025-02-21 09:39:56 +00:00
add new transform syncs
This commit is contained in:
parent
235632200d
commit
120b430cd1
@ -1,4 +1,4 @@
|
||||
using QSB.Syncs.TransformSync;
|
||||
using QSB.Syncs.Unsectored.Transforms;
|
||||
using QSB.Utility;
|
||||
using QSB.WorldSync;
|
||||
using System.Collections.Generic;
|
||||
@ -6,7 +6,7 @@ using UnityEngine;
|
||||
|
||||
namespace QSB.OrbSync.TransformSync
|
||||
{
|
||||
internal class NomaiOrbTransformSync : UnparentedBaseTransformSync
|
||||
internal class NomaiOrbTransformSync : UnsectoredTransformSync
|
||||
{
|
||||
public static List<NomaiOrbTransformSync> OrbTransformSyncs = new List<NomaiOrbTransformSync>();
|
||||
|
||||
|
@ -4,7 +4,7 @@ using QSB.Events;
|
||||
using QSB.Instruments;
|
||||
using QSB.RoastingSync;
|
||||
using QSB.SectorSync;
|
||||
using QSB.Syncs.TransformSync;
|
||||
using QSB.Syncs.Sectored.Transforms;
|
||||
using QSB.Tools;
|
||||
using QSB.Utility;
|
||||
using QSB.WorldSync;
|
||||
|
@ -1,7 +1,7 @@
|
||||
using OWML.Common;
|
||||
using OWML.Utils;
|
||||
using QSB.SectorSync;
|
||||
using QSB.Syncs.TransformSync;
|
||||
using QSB.Syncs.Sectored.Transforms;
|
||||
using QSB.Tools;
|
||||
using QSB.Tools.ProbeLauncherTool;
|
||||
using QSB.Utility;
|
||||
|
@ -278,9 +278,13 @@
|
||||
<Compile Include="ShipSync\Events\RepairTickMessage.cs" />
|
||||
<Compile Include="ShipSync\WorldObjects\QSBShipComponent.cs" />
|
||||
<Compile Include="ShipSync\WorldObjects\QSBShipHull.cs" />
|
||||
<Compile Include="Syncs\RigidbodySync\BaseRigidbodySync.cs" />
|
||||
<Compile Include="Syncs\RigidbodySync\SectoredRigidbodySync.cs" />
|
||||
<Compile Include="Syncs\Sectored\BaseSectoredSync.cs" />
|
||||
<Compile Include="Syncs\Sectored\Rigidbodies\SectoredRigidbodySync.cs" />
|
||||
<Compile Include="Syncs\Sectored\Transforms\SectoredTransformSync.cs" />
|
||||
<Compile Include="Syncs\SyncBase.cs" />
|
||||
<Compile Include="Syncs\Unsectored\BaseUnsectoredSync.cs" />
|
||||
<Compile Include="Syncs\Unsectored\Rigidbodies\UnsectoredRigidbodySync.cs" />
|
||||
<Compile Include="Syncs\Unsectored\Transforms\UnsectoredTransformSync.cs" />
|
||||
<Compile Include="TimeSync\FastForwardReason.cs" />
|
||||
<Compile Include="TimeSync\Patches\TimePatches.cs" />
|
||||
<Compile Include="TimeSync\PauseReason.cs" />
|
||||
@ -308,10 +312,7 @@
|
||||
<Compile Include="StatueSync\Events\StartStatueMessage.cs" />
|
||||
<Compile Include="StatueSync\Patches\StatuePatches.cs" />
|
||||
<Compile Include="StatueSync\StatueManager.cs" />
|
||||
<Compile Include="Syncs\TransformSync\BaseTransformSync.cs" />
|
||||
<Compile Include="Syncs\IntermediaryTransform.cs" />
|
||||
<Compile Include="Syncs\TransformSync\SectoredTransformSync.cs" />
|
||||
<Compile Include="Syncs\TransformSync\UnparentedBaseTransformSync.cs" />
|
||||
<Compile Include="TranslationSync\Events\SetAsTranslatedEvent.cs" />
|
||||
<Compile Include="TranslationSync\Events\SetAsTranslatedMessage.cs" />
|
||||
<Compile Include="TranslationSync\NomaiTextType.cs" />
|
||||
|
@ -1,6 +1,7 @@
|
||||
using OWML.Common;
|
||||
using QSB.SectorSync.WorldObjects;
|
||||
using QSB.Syncs;
|
||||
using QSB.Syncs.Sectored;
|
||||
using QSB.Utility;
|
||||
using QSB.WorldSync;
|
||||
using System.Collections.Generic;
|
||||
@ -18,8 +19,7 @@ namespace QSB.SectorSync
|
||||
private void OnEnable() => RepeatingManager.Repeatings.Add(this);
|
||||
private void OnDisable() => RepeatingManager.Repeatings.Remove(this);
|
||||
|
||||
public List<SyncBase> SectoredTransformSyncs = new List<SyncBase>();
|
||||
public List<SyncBase> SectoredRigidbodySyncs = new List<SyncBase>();
|
||||
public List<BaseSectoredSync> SectoredSyncs = new List<BaseSectoredSync>();
|
||||
|
||||
public void Invoke()
|
||||
{
|
||||
@ -28,7 +28,7 @@ namespace QSB.SectorSync
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var sync in SectoredTransformSyncs)
|
||||
foreach (var sync in SectoredSyncs)
|
||||
{
|
||||
if (sync.AttachedObject == null)
|
||||
{
|
||||
@ -39,22 +39,7 @@ namespace QSB.SectorSync
|
||||
&& sync.AttachedObject.gameObject.activeInHierarchy
|
||||
&& sync.IsReady)
|
||||
{
|
||||
CheckTransformSyncSector(sync as ISectoredSync<Transform>);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var sync in SectoredRigidbodySyncs)
|
||||
{
|
||||
if (sync.AttachedObject == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sync.HasAuthority
|
||||
&& sync.AttachedObject.gameObject.activeInHierarchy
|
||||
&& sync.IsReady)
|
||||
{
|
||||
CheckTransformSyncSector(sync as ISectoredSync<OWRigidbody>);
|
||||
CheckTransformSyncSector(sync);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -89,10 +74,9 @@ namespace QSB.SectorSync
|
||||
IsReady = QSBWorldSync.GetWorldObjects<QSBSector>().Any();
|
||||
}
|
||||
|
||||
private void CheckTransformSyncSector<T>(ISectoredSync<T> transformSync)
|
||||
where T : Component
|
||||
private void CheckTransformSyncSector(BaseSectoredSync transformSync)
|
||||
{
|
||||
var attachedObject = (transformSync as SyncBase).AttachedObject;
|
||||
var attachedObject = transformSync.AttachedObject;
|
||||
var closestSector = transformSync.SectorSync.GetClosestSector(attachedObject.transform);
|
||||
if (closestSector == default(QSBSector))
|
||||
{
|
||||
|
@ -1,6 +1,6 @@
|
||||
using QSB.Player;
|
||||
using QSB.SectorSync;
|
||||
using QSB.Syncs.RigidbodySync;
|
||||
using QSB.Syncs.Sectored.Rigidbodies;
|
||||
using QSB.Utility;
|
||||
using QSB.WorldSync;
|
||||
using UnityEngine;
|
||||
@ -20,9 +20,6 @@ namespace QSB.ShipSync.TransformSync
|
||||
LocalInstance = this;
|
||||
}
|
||||
|
||||
protected override Component InitLocalTransform() => throw new System.NotImplementedException();
|
||||
protected override Component InitRemoteTransform() => throw new System.NotImplementedException();
|
||||
|
||||
protected override OWRigidbody GetRigidbody()
|
||||
{
|
||||
QSBCore.UnityEvents.RunWhen(() => WorldObjectManager.AllReady, () => SectorSync.Init(Locator.GetShipDetector().GetComponent<SectorDetector>(), this));
|
||||
|
226
QSB/Syncs/Sectored/BaseSectoredSync.cs
Normal file
226
QSB/Syncs/Sectored/BaseSectoredSync.cs
Normal file
@ -0,0 +1,226 @@
|
||||
using QSB.SectorSync.WorldObjects;
|
||||
using QSB.SectorSync;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using QSB.Player.TransformSync;
|
||||
using UnityEngine;
|
||||
using OWML.Common;
|
||||
using QSB.Utility;
|
||||
using QSB.Player;
|
||||
using QSB.WorldSync;
|
||||
using QuantumUNET.Transport;
|
||||
|
||||
namespace QSB.Syncs.Sectored
|
||||
{
|
||||
public abstract class BaseSectoredSync : SyncBase
|
||||
{
|
||||
public override bool IgnoreDisabledAttachedObject => false;
|
||||
public override bool IgnoreNullReferenceTransform => true;
|
||||
|
||||
public QSBSector ReferenceSector { get; set; }
|
||||
public SectorSync.SectorSync SectorSync { get; private set; }
|
||||
public abstract TargetType Type { get; }
|
||||
|
||||
private int _sectorIdWaitingSlot = int.MinValue;
|
||||
|
||||
public override void Start()
|
||||
{
|
||||
SectorSync = gameObject.AddComponent<SectorSync.SectorSync>();
|
||||
QSBSectorManager.Instance.SectoredSyncs.Add(this);
|
||||
base.Start();
|
||||
}
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
QSBSectorManager.Instance.SectoredSyncs.Remove(this);
|
||||
if (SectorSync != null)
|
||||
{
|
||||
Destroy(SectorSync);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Init()
|
||||
{
|
||||
base.Init();
|
||||
if (!QSBSectorManager.Instance.IsReady)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!HasAuthority)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QSBCore.UnityEvents.RunWhen(() => SectorSync.IsReady, InitSector);
|
||||
}
|
||||
|
||||
private void InitSector()
|
||||
{
|
||||
DebugLog.DebugWrite($"[BaseSectoredSync] {_logName} InitSector");
|
||||
var closestSector = SectorSync.GetClosestSector(AttachedObject.transform);
|
||||
if (closestSector != null)
|
||||
{
|
||||
SetReferenceSector(closestSector);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugLog.ToConsole($"Warning - {_logName}'s initial sector was null.", OWML.Common.MessageType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
if (_sectorIdWaitingSlot == int.MinValue)
|
||||
{
|
||||
base.Update();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!WorldObjectManager.AllReady)
|
||||
{
|
||||
base.Update();
|
||||
return;
|
||||
}
|
||||
|
||||
var sector = _sectorIdWaitingSlot == -1
|
||||
? null
|
||||
: QSBWorldSync.GetWorldFromId<QSBSector>(_sectorIdWaitingSlot);
|
||||
|
||||
if (sector != ReferenceSector)
|
||||
{
|
||||
if (sector == null)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - {PlayerId}.{GetType().Name} got sector of ID -1.", OWML.Common.MessageType.Error);
|
||||
base.Update();
|
||||
return;
|
||||
}
|
||||
|
||||
SetReferenceSector(sector);
|
||||
}
|
||||
|
||||
_sectorIdWaitingSlot = int.MinValue;
|
||||
|
||||
base.Update();
|
||||
}
|
||||
|
||||
public override void SerializeTransform(QNetworkWriter writer, bool initialState)
|
||||
{
|
||||
if (_intermediaryTransform == null)
|
||||
{
|
||||
_intermediaryTransform = new IntermediaryTransform(transform);
|
||||
}
|
||||
|
||||
if (!QSBPlayerManager.PlayerExists(PlayerId))
|
||||
{
|
||||
writer.Write(-1);
|
||||
}
|
||||
else if (!Player.PlayerStates.IsReady)
|
||||
{
|
||||
writer.Write(-1);
|
||||
}
|
||||
else if (ReferenceSector != null)
|
||||
{
|
||||
writer.Write(ReferenceSector.ObjectId);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_isInitialized)
|
||||
{
|
||||
DebugLog.ToConsole($"Warning - ReferenceSector of {PlayerId}.{GetType().Name} is null.", OWML.Common.MessageType.Warning);
|
||||
}
|
||||
|
||||
writer.Write(-1);
|
||||
}
|
||||
}
|
||||
|
||||
public override void DeserializeTransform(QNetworkReader reader, bool initialState)
|
||||
{
|
||||
int sectorId;
|
||||
if (!QSBCore.WorldObjectsReady)
|
||||
{
|
||||
sectorId = reader.ReadInt32();
|
||||
DebugLog.DebugWrite($" - {_logName} ReadInt32 {sectorId}");
|
||||
if (initialState && sectorId != -1)
|
||||
{
|
||||
DebugLog.DebugWrite($"{_logName} set waiting sector id:{sectorId}");
|
||||
_sectorIdWaitingSlot = sectorId;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
sectorId = reader.ReadInt32();
|
||||
DebugLog.DebugWrite($" - {_logName} ReadInt32 {sectorId}");
|
||||
var sector = sectorId == -1
|
||||
? null
|
||||
: QSBWorldSync.GetWorldFromId<QSBSector>(sectorId);
|
||||
|
||||
if (sector != ReferenceSector)
|
||||
{
|
||||
if (sector == null)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - {PlayerId}.{GetType().Name} got sector of ID -1.", OWML.Common.MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
SetReferenceSector(sector);
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool UpdateTransform()
|
||||
{
|
||||
var referenceNull = ReferenceTransform == null || ReferenceSector == null || _intermediaryTransform.GetReferenceTransform() == null;
|
||||
var sectorManagerReady = QSBSectorManager.Instance.IsReady;
|
||||
|
||||
if (!sectorManagerReady)
|
||||
{
|
||||
if (referenceNull && HasAuthority)
|
||||
{
|
||||
DebugLog.ToConsole($"Warning - Reference was null, but sector manager wasn't ready. " +
|
||||
$"Transform:{ReferenceTransform == null}, Sector:{ReferenceSector == null}, Intermediary:{_intermediaryTransform.GetReferenceTransform() == null}",
|
||||
OWML.Common.MessageType.Warning);
|
||||
}
|
||||
|
||||
DebugLog.DebugWrite($"[BaseSectoredSync] {_logName} : Sector Manager not ready.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!HasAuthority)
|
||||
{
|
||||
DebugLog.DebugWrite($"[BaseSectoredSync] {_logName} : Does not have authority.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (referenceNull)
|
||||
{
|
||||
var closestSector = SectorSync.GetClosestSector(AttachedObject.transform);
|
||||
if (closestSector != null)
|
||||
{
|
||||
SetReferenceTransform(closestSector.Transform);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (SectorSync.IsReady)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - No closest sector found to {PlayerId}.{GetType().Name}!", OWML.Common.MessageType.Error);
|
||||
}
|
||||
|
||||
DebugLog.DebugWrite($"[BaseSectoredSync] {_logName} : No sector found.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void SetReferenceSector(QSBSector sector)
|
||||
{
|
||||
DebugLog.DebugWrite($"[BaseSectoredSync] {_logName} SetReferenceSector {sector.Name}");
|
||||
ReferenceSector = sector;
|
||||
SetReferenceTransform(sector?.Transform);
|
||||
}
|
||||
}
|
||||
}
|
100
QSB/Syncs/Sectored/Transforms/SectoredTransformSync.cs
Normal file
100
QSB/Syncs/Sectored/Transforms/SectoredTransformSync.cs
Normal file
@ -0,0 +1,100 @@
|
||||
using OWML.Common;
|
||||
using QSB.Utility;
|
||||
using QuantumUNET.Transport;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.Syncs.Sectored.Transforms
|
||||
{
|
||||
public abstract class SectoredTransformSync : BaseSectoredSync
|
||||
{
|
||||
public override bool ShouldReparentAttachedObject => true;
|
||||
|
||||
protected abstract Component InitLocalTransform();
|
||||
protected abstract Component InitRemoteTransform();
|
||||
|
||||
protected override Component SetAttachedObject()
|
||||
=> HasAuthority ? InitLocalTransform() : InitRemoteTransform();
|
||||
|
||||
public override void SerializeTransform(QNetworkWriter writer, bool initialState)
|
||||
{
|
||||
base.SerializeTransform(writer, initialState);
|
||||
|
||||
var worldPos = _intermediaryTransform.GetPosition();
|
||||
var worldRot = _intermediaryTransform.GetRotation();
|
||||
writer.Write(worldPos);
|
||||
SerializeRotation(writer, worldRot);
|
||||
_prevPosition = worldPos;
|
||||
_prevRotation = worldRot;
|
||||
}
|
||||
|
||||
public override void DeserializeTransform(QNetworkReader reader, bool initialState)
|
||||
{
|
||||
base.DeserializeTransform(reader, initialState);
|
||||
|
||||
if (!QSBCore.WorldObjectsReady)
|
||||
{
|
||||
reader.ReadVector3();
|
||||
DeserializeRotation(reader);
|
||||
return;
|
||||
}
|
||||
|
||||
var pos = reader.ReadVector3();
|
||||
var rot = DeserializeRotation(reader);
|
||||
|
||||
if (HasAuthority)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_intermediaryTransform == null)
|
||||
{
|
||||
_intermediaryTransform = new IntermediaryTransform(transform);
|
||||
}
|
||||
|
||||
_intermediaryTransform.SetPosition(pos);
|
||||
_intermediaryTransform.SetRotation(rot);
|
||||
|
||||
if (_intermediaryTransform.GetPosition() == Vector3.zero)
|
||||
{
|
||||
DebugLog.ToConsole($"Warning - {_logName} at (0,0,0)! - Given position was {pos}", MessageType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool UpdateTransform()
|
||||
{
|
||||
if (!base.UpdateTransform())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (HasAuthority)
|
||||
{
|
||||
_intermediaryTransform.EncodePosition(AttachedObject.transform.position);
|
||||
_intermediaryTransform.EncodeRotation(AttachedObject.transform.rotation);
|
||||
return true;
|
||||
}
|
||||
|
||||
var targetPos = _intermediaryTransform.GetTargetPosition_ParentedToReference();
|
||||
var targetRot = _intermediaryTransform.GetTargetRotation_ParentedToReference();
|
||||
if (targetPos != Vector3.zero && _intermediaryTransform.GetTargetPosition_Unparented() != Vector3.zero)
|
||||
{
|
||||
if (UseInterpolation)
|
||||
{
|
||||
AttachedObject.transform.localPosition = SmartSmoothDamp(AttachedObject.transform.localPosition, targetPos);
|
||||
AttachedObject.transform.localRotation = QuaternionHelper.SmoothDamp(AttachedObject.transform.localRotation, targetRot, ref _rotationSmoothVelocity, SmoothTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
AttachedObject.transform.localPosition = targetPos;
|
||||
AttachedObject.transform.localRotation = targetRot;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
89
QSB/Syncs/Unsectored/Transforms/UnsectoredTransformSync.cs
Normal file
89
QSB/Syncs/Unsectored/Transforms/UnsectoredTransformSync.cs
Normal file
@ -0,0 +1,89 @@
|
||||
using OWML.Common;
|
||||
using QSB.Utility;
|
||||
using QuantumUNET.Transport;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.Syncs.Unsectored.Transforms
|
||||
{
|
||||
public abstract class UnsectoredTransformSync : BaseUnsectoredSync
|
||||
{
|
||||
protected abstract Component InitLocalTransform();
|
||||
protected abstract Component InitRemoteTransform();
|
||||
|
||||
protected override Component SetAttachedObject()
|
||||
=> HasAuthority ? InitLocalTransform() : InitRemoteTransform();
|
||||
|
||||
public override void SerializeTransform(QNetworkWriter writer, bool initialState)
|
||||
{
|
||||
base.SerializeTransform(writer, initialState);
|
||||
|
||||
var worldPos = _intermediaryTransform.GetPosition();
|
||||
var worldRot = _intermediaryTransform.GetRotation();
|
||||
writer.Write(worldPos);
|
||||
SerializeRotation(writer, worldRot);
|
||||
_prevPosition = worldPos;
|
||||
_prevRotation = worldRot;
|
||||
}
|
||||
|
||||
public override void DeserializeTransform(QNetworkReader reader, bool initialState)
|
||||
{
|
||||
base.DeserializeTransform(reader, initialState);
|
||||
|
||||
if (!QSBCore.WorldObjectsReady)
|
||||
{
|
||||
reader.ReadVector3();
|
||||
DeserializeRotation(reader);
|
||||
return;
|
||||
}
|
||||
|
||||
var pos = reader.ReadVector3();
|
||||
var rot = DeserializeRotation(reader);
|
||||
|
||||
if (HasAuthority)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_intermediaryTransform == null)
|
||||
{
|
||||
_intermediaryTransform = new IntermediaryTransform(transform);
|
||||
}
|
||||
|
||||
_intermediaryTransform.SetPosition(pos);
|
||||
_intermediaryTransform.SetRotation(rot);
|
||||
|
||||
if (_intermediaryTransform.GetPosition() == Vector3.zero)
|
||||
{
|
||||
DebugLog.ToConsole($"Warning - {_logName} at (0,0,0)! - Given position was {pos}", MessageType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool UpdateTransform()
|
||||
{
|
||||
if (HasAuthority)
|
||||
{
|
||||
_intermediaryTransform.EncodePosition(AttachedObject.transform.position);
|
||||
_intermediaryTransform.EncodeRotation(AttachedObject.transform.rotation);
|
||||
return true;
|
||||
}
|
||||
|
||||
var targetPos = _intermediaryTransform.GetTargetPosition_ParentedToReference();
|
||||
var targetRot = _intermediaryTransform.GetTargetRotation_ParentedToReference();
|
||||
if (targetPos != Vector3.zero && _intermediaryTransform.GetTargetPosition_Unparented() != Vector3.zero)
|
||||
{
|
||||
if (UseInterpolation)
|
||||
{
|
||||
AttachedObject.transform.localPosition = SmartSmoothDamp(AttachedObject.transform.localPosition, targetPos);
|
||||
AttachedObject.transform.localRotation = QuaternionHelper.SmoothDamp(AttachedObject.transform.localRotation, targetRot, ref _rotationSmoothVelocity, SmoothTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
AttachedObject.transform.localPosition = targetPos;
|
||||
AttachedObject.transform.localRotation = targetRot;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user