gg.easy.airship 0.1.2136 → 0.1.2138
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Editor/Artifacts/AirshipLocalArtifactDatabase.cs +5 -1
- package/Editor/Artifacts/AirshipPrefabUtility.cs +9 -10
- package/Editor/TypescriptServices/Compiler/TypescriptCompilationService.cs +27 -3
- package/Editor/TypescriptServices/TypescriptServices.cs +1 -1
- package/Runtime/Code/Authentication/EasyAuthenticator.cs +5 -2
- package/Runtime/Code/Authentication/TransferData.cs +1 -0
- package/Runtime/Code/Luau/AirshipScript.cs +2 -0
- package/Runtime/Code/Luau/BinaryBlob.cs +4 -258
- package/Runtime/Code/LuauAPI/Bridge.cs +4 -0
- package/Runtime/Code/LuauAPI/ResourcesAPI.cs +1 -1
- package/Runtime/Code/Network/AirshipNetworkManager.cs +0 -2
- package/Runtime/Code/{RemoteConsole → Network}/ServerConsole.cs +159 -154
- package/Runtime/Code/{RemoteConsole → Network}/ServerConsole.cs.meta +2 -2
- package/Runtime/Code/Network/Simulation/AirshipSimulationManager.cs +13 -15
- package/Runtime/Code/Network/StateSystem/AirshipNetworkedStateManager.cs +11 -15
- package/Runtime/Code/Player/Character/MovementSystems/Character/CharacterMovement.cs +153 -16
- package/Runtime/Code/Player/Character/MovementSystems/Character/Structures/CharacterSnapshotData.cs +88 -19
- package/Runtime/Code/Player/PlayerInfo.cs +6 -2
- package/Runtime/Code/Player/PlayerManagerBridge.cs +3 -3
- package/Runtime/Code/Player/UserData.cs +1 -0
- package/Runtime/Code/TSCodeGen/TypeGenerator.cs +2 -1
- package/Runtime/Code/VoxelWorld/Airship.VoxelWorld.asmdef +2 -1
- package/Runtime/Code/VoxelWorld/VoxelCompressUtil.cs +26 -3
- package/Runtime/Code/VoxelWorld/VoxelWorld.cs +0 -17
- package/Runtime/Code/VoxelWorld/VoxelWorldChunk.cs +1 -1
- package/Runtime/Code/VoxelWorld/WorldSaveFile.cs +8 -6
- package/Runtime/Code/Zstd/Zstd.cs +125 -47
- package/Runtime/Code/Zstd/ZstdCompressStream.cs +147 -0
- package/Runtime/Code/Zstd/ZstdCompressStream.cs.meta +3 -0
- package/Runtime/Code/Zstd/ZstdDecompressStream.cs +196 -0
- package/Runtime/Code/Zstd/ZstdDecompressStream.cs.meta +3 -0
- package/Runtime/Code/Zstd/ZstdNative.cs +5 -5
- package/Runtime/Plugins/Android/libLuauPlugin.so +0 -0
- package/Runtime/Plugins/Linux/libLuauPlugin.so +0 -0
- package/Runtime/Plugins/Mac/LuauPlugin.bundle/Contents/Info.plist +1 -1
- package/Runtime/Plugins/Mac/LuauPlugin.bundle/Contents/MacOS/LuauPlugin +0 -0
- package/Runtime/Plugins/Windows/x64/LuauPlugin.dll +0 -0
- package/Runtime/Plugins/Windows/x64/LuauPlugin.pdb +0 -0
- package/Runtime/Plugins/iOS/LuauPluginIos.a +0 -0
- package/package.json +1 -1
- package/Runtime/Code/RemoteConsole/Airship.RemoteConsole.asmdef +0 -26
- package/Runtime/Code/RemoteConsole/Airship.RemoteConsole.asmdef.meta +0 -3
- package/Runtime/Code/RemoteConsole.meta +0 -3
|
@@ -1,155 +1,160 @@
|
|
|
1
|
-
using System;
|
|
2
|
-
using System.Collections.Concurrent;
|
|
3
|
-
using System.Collections.Generic;
|
|
4
|
-
using System.IO;
|
|
5
|
-
using System.Threading.Tasks;
|
|
6
|
-
using Airship.DevConsole;
|
|
7
|
-
using
|
|
8
|
-
using
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
public string
|
|
14
|
-
public
|
|
15
|
-
public
|
|
16
|
-
public
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
private
|
|
27
|
-
private
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
private string
|
|
31
|
-
private
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
private
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
///
|
|
61
|
-
///
|
|
62
|
-
///
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
DevConsole.console.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
NetworkClient.
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
if (File.Exists(
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
writer
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
writer
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
1
|
+
using System;
|
|
2
|
+
using System.Collections.Concurrent;
|
|
3
|
+
using System.Collections.Generic;
|
|
4
|
+
using System.IO;
|
|
5
|
+
using System.Threading.Tasks;
|
|
6
|
+
using Airship.DevConsole;
|
|
7
|
+
using Code.Player;
|
|
8
|
+
using Mirror;
|
|
9
|
+
using UnityEngine;
|
|
10
|
+
|
|
11
|
+
namespace Code.RemoteConsole {
|
|
12
|
+
struct ServerConsoleBroadcast : NetworkMessage {
|
|
13
|
+
public string message;
|
|
14
|
+
public string stackTrace;
|
|
15
|
+
public LogType logType;
|
|
16
|
+
public string time;
|
|
17
|
+
public bool startup;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
struct RequestServerConsoleStartupLogs : NetworkMessage {
|
|
21
|
+
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
[LuauAPI]
|
|
25
|
+
public class ServerConsole : MonoBehaviour {
|
|
26
|
+
private List<ServerConsoleBroadcast> startupMessages = new(100);
|
|
27
|
+
private const int maxStartupMessages = 100;
|
|
28
|
+
private static readonly ConcurrentQueue<string> logQueue = new();
|
|
29
|
+
|
|
30
|
+
private string logPath;
|
|
31
|
+
private string prevLogPath;
|
|
32
|
+
private StreamWriter writer;
|
|
33
|
+
|
|
34
|
+
private bool shuttingDown = false;
|
|
35
|
+
private Task writeTask;
|
|
36
|
+
|
|
37
|
+
private void Awake() {
|
|
38
|
+
writeTask = Task.Run(ProcessQueue);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private async Task ProcessQueue() {
|
|
42
|
+
while (!shuttingDown) {
|
|
43
|
+
while (logQueue.TryDequeue(out string msg)) {
|
|
44
|
+
try {
|
|
45
|
+
await writer.WriteLineAsync(msg);
|
|
46
|
+
} catch (Exception e) {
|
|
47
|
+
Debug.LogError("ServerLogger write failed: " + e);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
await Task.Delay(1); // tune as needed
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private void OnDestroy() {
|
|
56
|
+
shuttingDown = true;
|
|
57
|
+
// writeTask?.Wait();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/// <summary>
|
|
61
|
+
/// Called on client when client receives a remote log from server.
|
|
62
|
+
/// </summary>
|
|
63
|
+
/// <param name="args"></param>
|
|
64
|
+
private void OnServerConsoleBroadcast(ServerConsoleBroadcast args) {
|
|
65
|
+
if (!DevConsole.console.loggingEnabled) return;
|
|
66
|
+
DevConsole.console.OnLogMessageReceived(args.message, args.stackTrace, args.logType, LogContext.Server, args.time);
|
|
67
|
+
|
|
68
|
+
string timeStamped = $"[{DateTime.Now:HH:mm:ss}] {args.message}";
|
|
69
|
+
logQueue.Enqueue(timeStamped);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
public void OnStartServer() {
|
|
73
|
+
if (!RunCore.IsClient()) {
|
|
74
|
+
Application.logMessageReceived += LogCallback;
|
|
75
|
+
}
|
|
76
|
+
NetworkServer.RegisterHandler<RequestServerConsoleStartupLogs>((conn, data) => {
|
|
77
|
+
foreach (var startupMessage in startupMessages) {
|
|
78
|
+
conn.Send(startupMessage);
|
|
79
|
+
}
|
|
80
|
+
}, false);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
public void OnClientConnectedToServer() {
|
|
84
|
+
if (!RunCore.IsServer()) {
|
|
85
|
+
NetworkClient.RegisterHandler<ServerConsoleBroadcast>(OnServerConsoleBroadcast, false);
|
|
86
|
+
NetworkClient.Send(new RequestServerConsoleStartupLogs());
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Setup logs
|
|
90
|
+
if (RunCore.IsClient()) {
|
|
91
|
+
string logDir = Path.GetDirectoryName(Application.consoleLogPath);
|
|
92
|
+
logPath = Path.Combine(logDir, "Server.log");
|
|
93
|
+
prevLogPath = Path.Combine(logDir, "Server-prev.log");
|
|
94
|
+
|
|
95
|
+
// Rotate logs
|
|
96
|
+
try {
|
|
97
|
+
if (File.Exists(prevLogPath)) File.Delete(prevLogPath);
|
|
98
|
+
if (File.Exists(logPath)) File.Move(logPath, prevLogPath);
|
|
99
|
+
} catch (Exception e) {
|
|
100
|
+
Debug.LogError("Failed rotating server logs: " + e);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (writer != null) {
|
|
104
|
+
writer.Close();
|
|
105
|
+
}
|
|
106
|
+
writer = new StreamWriter(logPath, false); // overwrite existing
|
|
107
|
+
writer.AutoFlush = true;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
public void OnStopClient() {
|
|
112
|
+
NetworkClient.UnregisterHandler<ServerConsoleBroadcast>();
|
|
113
|
+
if (writer != null) {
|
|
114
|
+
writer.Close();
|
|
115
|
+
writer = null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
void LogCallback(string message, string stackTrace, LogType logType) {
|
|
120
|
+
SendServerLogMessage(message, logType, stackTrace);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
private void OnDisable() {
|
|
124
|
+
if (RunCore.IsServer()) {
|
|
125
|
+
Application.logMessageReceived -= LogCallback;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
private void SendServerLogMessage(string message, LogType logType = LogType.Log, string stackTrace = "") {
|
|
130
|
+
if (RunCore.IsServer() && !RunCore.IsEditor()) {
|
|
131
|
+
var time = DateTime.Now.ToString("HH:mm:ss");
|
|
132
|
+
if (this.startupMessages.Count < maxStartupMessages) {
|
|
133
|
+
this.startupMessages.Add(new ServerConsoleBroadcast() {
|
|
134
|
+
message = message,
|
|
135
|
+
logType = logType,
|
|
136
|
+
startup = true,
|
|
137
|
+
time = time,
|
|
138
|
+
stackTrace = stackTrace,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (NetworkServer.active) {
|
|
143
|
+
var packet = new ServerConsoleBroadcast() {
|
|
144
|
+
message = message,
|
|
145
|
+
logType = logType,
|
|
146
|
+
startup = false,
|
|
147
|
+
stackTrace = stackTrace,
|
|
148
|
+
time = time,
|
|
149
|
+
};
|
|
150
|
+
// bool sendToReadyOnly = Application.isEditor;
|
|
151
|
+
foreach (var player in PlayerManagerBridge.Instance.players) {
|
|
152
|
+
if (!string.IsNullOrEmpty(player.orgRoleName)) {
|
|
153
|
+
player.connectionToClient.Send(packet, Channels.Reliable);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
155
160
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
fileFormatVersion: 2
|
|
2
|
-
guid: 8d4b051ef8784e61bc03cc11c9eef05b
|
|
1
|
+
fileFormatVersion: 2
|
|
2
|
+
guid: 8d4b051ef8784e61bc03cc11c9eef05b
|
|
3
3
|
timeCreated: 1677178638
|
|
@@ -102,12 +102,10 @@ namespace Code.Network.Simulation
|
|
|
102
102
|
*/
|
|
103
103
|
public event Action<int, double, double> OnLagCompensationCheck;
|
|
104
104
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
public event Action<double, bool> OnPerformTick;
|
|
110
|
-
|
|
105
|
+
/// <summary>
|
|
106
|
+
/// This action tells all watching components that they need to perform a tick.
|
|
107
|
+
/// A Physics.Simulate() call will be made after PerformTick completes.
|
|
108
|
+
/// </summary>
|
|
111
109
|
public event Action<object, object> OnTick;
|
|
112
110
|
|
|
113
111
|
/**
|
|
@@ -184,7 +182,6 @@ namespace Code.Network.Simulation
|
|
|
184
182
|
if (resimBackTo != time) this.PerformResimulation(resimBackTo);
|
|
185
183
|
|
|
186
184
|
// Perform the standard tick behavior
|
|
187
|
-
OnPerformTick?.Invoke(time, false);
|
|
188
185
|
OnTick?.Invoke(time, false);
|
|
189
186
|
// Debug.Log("Simulate call. Main Tick: " + NetworkTime.time);
|
|
190
187
|
Physics.Simulate(Time.fixedDeltaTime);
|
|
@@ -310,13 +307,14 @@ namespace Code.Network.Simulation
|
|
|
310
307
|
this.ScheduleResimulation((resim => resim(time)));
|
|
311
308
|
}
|
|
312
309
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
310
|
+
/// <summary>
|
|
311
|
+
/// Requests a simulation based on the provided time. Requesting a simulation will roll back the physics
|
|
312
|
+
/// world to the snapshot just before or at the base time provided. Calling the returned tick function
|
|
313
|
+
/// will advance the simulation and re-simulate the calls to <see cref="OnTick"/>, Physics.Simulate,
|
|
314
|
+
/// and <see cref="OnCaptureSnapshot"/>
|
|
315
|
+
///
|
|
316
|
+
/// This function is used internally to implement the scheduled resimulations.
|
|
317
|
+
/// </summary>
|
|
320
318
|
private void PerformResimulation(double baseTime)
|
|
321
319
|
{
|
|
322
320
|
Debug.Log($"T:{Time.unscaledTimeAsDouble} Resimulating from {baseTime}");
|
|
@@ -343,7 +341,7 @@ namespace Code.Network.Simulation
|
|
|
343
341
|
|
|
344
342
|
while (tickIndex < this.tickTimes.Count)
|
|
345
343
|
{
|
|
346
|
-
|
|
344
|
+
OnTick?.Invoke(this.tickTimes[tickIndex], true);
|
|
347
345
|
// Debug.Log("Simulate call. Replay Tick: " + this.tickTimes[tickIndex]);
|
|
348
346
|
Physics.Simulate(Time.fixedDeltaTime);
|
|
349
347
|
OnCaptureSnapshot?.Invoke(this.tickTimes[tickIndex], true);
|
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
using System;
|
|
2
2
|
using System.Collections.Generic;
|
|
3
|
-
using System.Linq;
|
|
4
3
|
using Code.Network.Simulation;
|
|
5
4
|
using Code.Network.StateSystem.Structures;
|
|
6
5
|
using Code.Player.Character.Net;
|
|
7
6
|
using Mirror;
|
|
8
|
-
using RSG.Promises;
|
|
9
|
-
using Unity.Mathematics;
|
|
10
|
-
using Unity.VisualScripting;
|
|
11
7
|
using UnityEngine;
|
|
12
|
-
using UnityEngine.Serialization;
|
|
13
8
|
|
|
14
9
|
namespace Code.Network.StateSystem
|
|
15
10
|
{
|
|
@@ -92,7 +87,7 @@ namespace Code.Network.StateSystem
|
|
|
92
87
|
// Observer history stores authoritative state and uses the server's times. This data can be interpolated
|
|
93
88
|
// with NetworkTime.time. It is converted into state history on the local clients physics timeline
|
|
94
89
|
// in the observer snapshot function.
|
|
95
|
-
|
|
90
|
+
public History<State> observerHistory;
|
|
96
91
|
private double lastReceivedSnapshotTime = 0;
|
|
97
92
|
|
|
98
93
|
// Client interpolation fields
|
|
@@ -165,7 +160,7 @@ namespace Code.Network.StateSystem
|
|
|
165
160
|
private void Awake()
|
|
166
161
|
{
|
|
167
162
|
AirshipSimulationManager.Instance.ActivateSimulationManager();
|
|
168
|
-
AirshipSimulationManager.Instance.
|
|
163
|
+
AirshipSimulationManager.Instance.OnTick += this.OnTick;
|
|
169
164
|
AirshipSimulationManager.Instance.OnSetSnapshot += this.OnSetSnapshot;
|
|
170
165
|
AirshipSimulationManager.Instance.OnCaptureSnapshot += this.OnCaptureSnapshot;
|
|
171
166
|
AirshipSimulationManager.Instance.OnLagCompensationCheck += this.OnLagCompensationCheck;
|
|
@@ -195,13 +190,13 @@ namespace Code.Network.StateSystem
|
|
|
195
190
|
|
|
196
191
|
public void OnDestroy()
|
|
197
192
|
{
|
|
198
|
-
AirshipSimulationManager.Instance.
|
|
193
|
+
AirshipSimulationManager.Instance.OnTick -= this.OnTick;
|
|
199
194
|
AirshipSimulationManager.Instance.OnSetSnapshot -= this.OnSetSnapshot;
|
|
200
195
|
AirshipSimulationManager.Instance.OnCaptureSnapshot -= this.OnCaptureSnapshot;
|
|
201
196
|
AirshipSimulationManager.Instance.OnLagCompensationCheck -= this.OnLagCompensationCheck;
|
|
202
197
|
}
|
|
203
198
|
|
|
204
|
-
private void
|
|
199
|
+
private void SendNetworkMessages()
|
|
205
200
|
{
|
|
206
201
|
if (isClient && isServer)
|
|
207
202
|
{
|
|
@@ -256,8 +251,7 @@ namespace Code.Network.StateSystem
|
|
|
256
251
|
}
|
|
257
252
|
|
|
258
253
|
// We are operating as a server
|
|
259
|
-
if (isServer &&
|
|
260
|
-
AccurateInterval.Elapsed(NetworkTime.localTime, NetworkClient.sendInterval, ref lastServerSend))
|
|
254
|
+
if (isServer && AccurateInterval.Elapsed(NetworkTime.localTime, NetworkClient.sendInterval, ref lastServerSend))
|
|
261
255
|
{
|
|
262
256
|
// No matter what mode the server is operating in, we send our latest state to clients.
|
|
263
257
|
// If we have no state yet, don't send
|
|
@@ -276,14 +270,17 @@ namespace Code.Network.StateSystem
|
|
|
276
270
|
{
|
|
277
271
|
this.Interpolate();
|
|
278
272
|
}
|
|
273
|
+
|
|
274
|
+
SendNetworkMessages();
|
|
279
275
|
}
|
|
280
276
|
|
|
281
277
|
#endregion
|
|
282
278
|
|
|
283
279
|
#region Top Level Event Functions
|
|
284
280
|
|
|
285
|
-
private void
|
|
286
|
-
|
|
281
|
+
private void OnTick(object timeObj, object replayObj) {
|
|
282
|
+
if (timeObj is not double time || replayObj is not bool replay) return;
|
|
283
|
+
|
|
287
284
|
// We are in shared mode
|
|
288
285
|
if (isServer && isClient)
|
|
289
286
|
{
|
|
@@ -976,8 +973,7 @@ namespace Code.Network.StateSystem
|
|
|
976
973
|
}
|
|
977
974
|
|
|
978
975
|
lastReceivedSnapshotTime = state.time;
|
|
979
|
-
|
|
980
|
-
// Debug.Log("Client receive snapshot" + state);
|
|
976
|
+
|
|
981
977
|
// The client is a non-authoritative owner and should update
|
|
982
978
|
// their local state with the authoritative state from the server.
|
|
983
979
|
if (isOwned && serverAuth)
|