gg.easy.airship 0.1.2164 → 0.1.2166
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/Runtime/Code/Analytics/ClientFolderUploader.cs +62 -31
- package/Runtime/Code/Bundles/AssetBridge.cs +1 -1
- package/Runtime/Code/Bundles/SystemRoot.cs +20 -15
- package/Runtime/Code/Luau/LuauCoreReflection.cs +1 -1
- package/Runtime/Code/Luau/ReflectionList.cs +1 -0
- package/Runtime/Code/LuauAPI/FrameTimingManagerAPI.cs +22 -2
- package/Runtime/Code/Player/PlayerInfo.cs +2 -2
- package/Runtime/Code/Player/PlayerManagerBridge.cs +1 -1
- package/Runtime/Code/Quality/QualityManager.cs +143 -0
- package/Runtime/Code/Quality/QualityManager.cs.meta +3 -0
- package/Runtime/Code/Quality.meta +3 -0
- package/Runtime/Code/Steam/SteamLuauAPI.cs +34 -31
- package/Runtime/Code/VoiceChat/AirshipUniVoiceNetwork.cs +7 -0
- package/Runtime/Code/VoxelWorld/Resources/SampleDerivative.hlsl +7 -0
- package/Runtime/Code/VoxelWorld/Resources/SampleDerivative.hlsl.meta +7 -0
- package/Runtime/Code/VoxelWorld/Resources/VoxelWorldAtlasURP.shadergraph +1442 -161
- package/Runtime/Code/VoxelWorld/Resources/VoxelWorldMatURP.mat +2 -2
- package/Runtime/Code/VoxelWorld/TexturePacker.cs +31 -24
- package/Runtime/Code/VoxelWorld/VoxelBlocks.cs +4 -4
- package/Runtime/Scenes/CoreScene.unity +380 -290
- package/ThirdParty/Graphy - Ultimate Stats Monitor/Runtime/Timings/G_TimingsGraph.cs +7 -6
- package/package.json +1 -2
- package/ThirdParty/TextMesh Pro/Shaders/TMP_SDF-HDRP LIT.shadergraph +0 -12074
- package/ThirdParty/TextMesh Pro/Shaders/TMP_SDF-HDRP LIT.shadergraph.meta +0 -10
- package/ThirdParty/TextMesh Pro/Shaders/TMP_SDF-HDRP UNLIT.shadergraph +0 -11759
- package/ThirdParty/TextMesh Pro/Shaders/TMP_SDF-HDRP UNLIT.shadergraph.meta +0 -10
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
using System;
|
|
2
|
+
using System.Collections.Generic;
|
|
2
3
|
using System.IO;
|
|
3
4
|
using System.IO.Compression;
|
|
4
5
|
using System.Threading.Tasks;
|
|
@@ -57,46 +58,71 @@ namespace Code.Analytics {
|
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
var playerLogFile = Path.Combine(path, "Player.log");
|
|
61
|
+
var tempCurPlayerLogFile = Path.Combine(path, "Player-cur.log");
|
|
60
62
|
var playerPrevLogFile = Path.Combine(path, "Player-prev.log");
|
|
61
63
|
var editorLogFile = Path.Combine(path, "Editor.log");
|
|
64
|
+
var tempCurEditorLogFile = Path.Combine(path, "Editor-cur.log");
|
|
62
65
|
var editorPrevLogFile = Path.Combine(path, "Editor-prev.log");
|
|
63
66
|
|
|
64
|
-
|
|
65
|
-
try {
|
|
66
|
-
using (var archive = ZipFile.Open(zipPath, ZipArchiveMode.Create)) {
|
|
67
|
-
var fileExists = false;
|
|
68
|
-
if (File.Exists(playerLogFile)) {
|
|
69
|
-
fileExists = true;
|
|
70
|
-
Debug.Log("[ClientFolderUploader] Adding Player.log");
|
|
71
|
-
archive.CreateEntryFromFile(playerLogFile, "Player.log");
|
|
72
|
-
}
|
|
67
|
+
var logMessages = new List<string>();
|
|
73
68
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
69
|
+
Action printDebugLines = () => {
|
|
70
|
+
foreach (var message in logMessages) {
|
|
71
|
+
Debug.Log(message);
|
|
72
|
+
}
|
|
73
|
+
logMessages.Clear();
|
|
74
|
+
};
|
|
79
75
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
archive.CreateEntryFromFile(editorLogFile, "Editor.log");
|
|
84
|
-
}
|
|
76
|
+
var cleanupFiles = new List<string>() {
|
|
77
|
+
zipPath,
|
|
78
|
+
};
|
|
85
79
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
80
|
+
Debug.Log("[ClientFolderUploader] Creating zip archive");
|
|
81
|
+
try {
|
|
82
|
+
if (File.Exists(zipPath)) {
|
|
83
|
+
File.Delete(zipPath);
|
|
90
84
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
85
|
+
using (var archive = ZipFile.Open(zipPath, ZipArchiveMode.Create)) {
|
|
86
|
+
var fileExists = false;
|
|
87
|
+
if (File.Exists(playerLogFile)) {
|
|
88
|
+
fileExists = true;
|
|
89
|
+
File.Copy(playerLogFile, tempCurPlayerLogFile, true);
|
|
90
|
+
logMessages.Add("[ClientFolderUploader] Adding Player.log");
|
|
91
|
+
archive.CreateEntryFromFile(tempCurPlayerLogFile, "Player.log");
|
|
92
|
+
cleanupFiles.Add(tempCurPlayerLogFile);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (File.Exists(playerPrevLogFile)) {
|
|
96
|
+
fileExists = true;
|
|
97
|
+
logMessages.Add("[ClientFolderUploader] Adding Player-prev.log");
|
|
98
|
+
archive.CreateEntryFromFile(playerPrevLogFile, "Player-prev.log");
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (File.Exists(editorLogFile)) {
|
|
102
|
+
fileExists = true;
|
|
103
|
+
File.Copy(editorLogFile, tempCurEditorLogFile, true);
|
|
104
|
+
logMessages.Add("[ClientFolderUploader] Adding Editor.log");
|
|
105
|
+
archive.CreateEntryFromFile(tempCurEditorLogFile, "Editor.log");
|
|
106
|
+
cleanupFiles.Add(tempCurEditorLogFile);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (File.Exists(editorPrevLogFile)) {
|
|
110
|
+
fileExists = true;
|
|
111
|
+
logMessages.Add("[ClientFolderUploader] Adding Editor-prev.log");
|
|
112
|
+
archive.CreateEntryFromFile(editorPrevLogFile, "Editor-prev.log");
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (!fileExists) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
94
118
|
}
|
|
119
|
+
} catch (Exception ex) {
|
|
120
|
+
printDebugLines();
|
|
121
|
+
Debug.LogError($"[ClientFolderUploader] Zip creation failed: {ex.Message}");
|
|
122
|
+
throw;
|
|
95
123
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
throw;
|
|
99
|
-
}
|
|
124
|
+
|
|
125
|
+
printDebugLines();
|
|
100
126
|
|
|
101
127
|
var contentType = "application/zip";
|
|
102
128
|
var contentLength = new FileInfo(zipPath).Length;
|
|
@@ -135,7 +161,12 @@ namespace Code.Analytics {
|
|
|
135
161
|
},
|
|
136
162
|
}, "");
|
|
137
163
|
|
|
138
|
-
|
|
164
|
+
Debug.Log("[ClientFolderUploader] Deleting temporary files");
|
|
165
|
+
foreach (var file in cleanupFiles) {
|
|
166
|
+
if (File.Exists(file)) {
|
|
167
|
+
File.Delete(file);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
139
170
|
} catch (Exception ex) {
|
|
140
171
|
Debug.LogError($"[ClientFolderUploader] Error during upload process: {ex.Message}");
|
|
141
172
|
throw;
|
|
@@ -259,7 +259,7 @@ public class AssetBridge : IAssetBridge
|
|
|
259
259
|
#endif
|
|
260
260
|
|
|
261
261
|
if (printErrorOnFail) {
|
|
262
|
-
Debug.LogError("
|
|
262
|
+
Debug.LogError($"Failed to load asset at path \"{path}\". Are you sure it exists?");
|
|
263
263
|
}
|
|
264
264
|
return null;
|
|
265
265
|
}
|
|
@@ -35,6 +35,7 @@ public class SystemRoot : Singleton<SystemRoot> {
|
|
|
35
35
|
public List<AssetBundleCreateRequest> extraBundleLoadRequests = new();
|
|
36
36
|
public static bool startedLoadingExtraBundle = false;
|
|
37
37
|
public static bool preWarmedCoreShaders = false;
|
|
38
|
+
private static bool disableCoreMaterialsBundle = false;
|
|
38
39
|
public AssetBundle coreMaterialsAssetBundle;
|
|
39
40
|
private bool loadInProgress = false;
|
|
40
41
|
|
|
@@ -105,16 +106,18 @@ public class SystemRoot : Singleton<SystemRoot> {
|
|
|
105
106
|
Debug.Log($"Loaded CoreMaterials bundle in {st.ElapsedMilliseconds} ms.");
|
|
106
107
|
}
|
|
107
108
|
#else
|
|
108
|
-
if (
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
109
|
+
if (!disableCoreMaterialsBundle) {
|
|
110
|
+
if (File.Exists(path)) {
|
|
111
|
+
Debug.Log($"Loading CoreMaterials... ({path})");
|
|
112
|
+
var st = Stopwatch.StartNew();
|
|
113
|
+
var ao = AssetBundle.LoadFromFileAsync(path);
|
|
114
|
+
this.extraBundleLoadRequests.Add(ao);
|
|
115
|
+
await ao;
|
|
116
|
+
this.coreMaterialsAssetBundle = ao.assetBundle;
|
|
117
|
+
Debug.Log($"Loaded CoreMaterials bundle in {st.ElapsedMilliseconds} ms.");
|
|
118
|
+
} else {
|
|
119
|
+
Debug.LogWarning($"CoreMaterials path not found ({path})");
|
|
120
|
+
}
|
|
118
121
|
}
|
|
119
122
|
#endif
|
|
120
123
|
}
|
|
@@ -342,11 +345,13 @@ public class SystemRoot : Singleton<SystemRoot> {
|
|
|
342
345
|
yield return this.WaitAll(loadLists[0].ToArray());
|
|
343
346
|
Debug.Log("Loading game...");
|
|
344
347
|
yield return this.WaitAll(loadLists[1].ToArray());
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
348
|
+
|
|
349
|
+
if (!disableCoreMaterialsBundle) {
|
|
350
|
+
foreach (var ao in this.extraBundleLoadRequests) {
|
|
351
|
+
if (!ao.isDone) {
|
|
352
|
+
Debug.Log("Waiting for shipped asset bundles to load...");
|
|
353
|
+
yield return ao;
|
|
354
|
+
}
|
|
350
355
|
}
|
|
351
356
|
}
|
|
352
357
|
|
|
@@ -420,7 +420,7 @@ public partial class LuauCore : MonoBehaviour
|
|
|
420
420
|
LuauPlugin.LuauPushValueToThread(thread, (int)PODTYPE.POD_DOUBLE, new IntPtr(value: &value), 0);
|
|
421
421
|
}
|
|
422
422
|
|
|
423
|
-
|
|
423
|
+
public static bool WriteArrayToThread(IntPtr thread, IEnumerable array, Type t, int knownSize = 0) {
|
|
424
424
|
LuauPluginRaw.NewTable(thread, knownSize);
|
|
425
425
|
|
|
426
426
|
var i = 0;
|
|
@@ -187,6 +187,7 @@ namespace Luau {
|
|
|
187
187
|
[typeof(MeshFilter)] = LuauContextAll,
|
|
188
188
|
[typeof(Sprite)] = LuauContextAll,
|
|
189
189
|
[typeof(DecalProjector)] = LuauContextAll,
|
|
190
|
+
[typeof(LODGroup)] = LuauContextAll,
|
|
190
191
|
//Spline
|
|
191
192
|
[typeof(Spline)] = LuauContextAll,
|
|
192
193
|
[typeof(BezierCurve)] = LuauContextAll,
|
|
@@ -3,8 +3,28 @@ using UnityEngine;
|
|
|
3
3
|
|
|
4
4
|
[LuauAPI]
|
|
5
5
|
public class FrameTimingManagerAPI : BaseLuaAPIClass {
|
|
6
|
-
public override Type GetAPIType()
|
|
7
|
-
{
|
|
6
|
+
public override Type GetAPIType() {
|
|
8
7
|
return typeof(UnityEngine.FrameTimingManager);
|
|
9
8
|
}
|
|
9
|
+
|
|
10
|
+
public override int OverrideStaticMethod(LuauContext context, IntPtr thread, string methodName, int numParameters,
|
|
11
|
+
ArraySegment<int> parameterDataPODTypes, ArraySegment<IntPtr> parameterDataPtrs,
|
|
12
|
+
ArraySegment<int> parameterDataSizes) {
|
|
13
|
+
if (methodName is "GetLatestTimings") {
|
|
14
|
+
if (numParameters != 1) throw new ArgumentException("GetLatestTimings expects 1 parameter.");
|
|
15
|
+
|
|
16
|
+
var numTimings = (uint) LuauCore.GetParameterAsInt(0, numParameters, parameterDataPODTypes, parameterDataPtrs,
|
|
17
|
+
parameterDataSizes);
|
|
18
|
+
|
|
19
|
+
// Arbitrary limit to avoid huge allocations / requests
|
|
20
|
+
if (numTimings > 1000) throw new ArgumentException("Requested too many frames (>1000) from GetLatestTimings.");
|
|
21
|
+
|
|
22
|
+
// This function will alloc -- we could look to in the future support a non-alloc setup
|
|
23
|
+
var timings = new FrameTiming[numTimings];
|
|
24
|
+
var fetchedResults = FrameTimingManager.GetLatestTimings(numTimings, timings);
|
|
25
|
+
LuauCore.WriteArrayToThread(thread, timings, typeof(FrameTiming), (int) fetchedResults);
|
|
26
|
+
return 1;
|
|
27
|
+
}
|
|
28
|
+
return -1;
|
|
29
|
+
}
|
|
10
30
|
}
|
|
@@ -44,7 +44,7 @@ public class PlayerInfo : NetworkBehaviour {
|
|
|
44
44
|
|
|
45
45
|
private void InitVoiceChat() {
|
|
46
46
|
var voiceChatGO = new GameObject(
|
|
47
|
-
$"{this.username}
|
|
47
|
+
$"{this.username}_VoiceChatAudioSource");
|
|
48
48
|
this.voiceChatAudioSource = voiceChatGO.AddComponent<AudioSource>();
|
|
49
49
|
voiceChatGO.transform.SetParent(this.transform);
|
|
50
50
|
}
|
|
@@ -57,7 +57,7 @@ public class PlayerInfo : NetworkBehaviour {
|
|
|
57
57
|
public override void OnStartClient() {
|
|
58
58
|
base.OnStartClient();
|
|
59
59
|
|
|
60
|
-
if (isClient) {
|
|
60
|
+
if (isClient && !RunCore.IsServer()) {
|
|
61
61
|
this.InitVoiceChat();
|
|
62
62
|
}
|
|
63
63
|
}
|
|
@@ -189,7 +189,7 @@ namespace Code.Player {
|
|
|
189
189
|
}
|
|
190
190
|
|
|
191
191
|
/**
|
|
192
|
-
*
|
|
192
|
+
* Server side logic for when a new client joins.
|
|
193
193
|
*/
|
|
194
194
|
private async void NetworkServer_OnConnected(NetworkConnectionToClient conn) {
|
|
195
195
|
if (playerPrefab == null) {
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using System.Collections.Generic;
|
|
3
|
+
using UnityEngine;
|
|
4
|
+
|
|
5
|
+
namespace Code.Quality {
|
|
6
|
+
// We should redo this to be a single core number for quality
|
|
7
|
+
// NOTE: must match FrameHealth in Airship.d.ts
|
|
8
|
+
public enum FrameHealth {
|
|
9
|
+
Ok = 0,
|
|
10
|
+
Unhealthy = 1,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
public struct QualityReport {
|
|
14
|
+
public double gpuAvg;
|
|
15
|
+
public double cpuMainAvg;
|
|
16
|
+
public double cpuRenderAvg;
|
|
17
|
+
|
|
18
|
+
public int numFrames;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
[LuauAPI(LuauContext.Game)]
|
|
22
|
+
public class QualityManager : Singleton<QualityManager> {
|
|
23
|
+
public static event Action<object, object> OnQualityCheck;
|
|
24
|
+
|
|
25
|
+
private const int SampleCount = 500;
|
|
26
|
+
/// <summary>
|
|
27
|
+
/// Time after starting that we'll run a quality check on the client. This
|
|
28
|
+
/// will be sent to game clients to allow them to configure quality based on
|
|
29
|
+
/// performance.
|
|
30
|
+
/// </summary>
|
|
31
|
+
private const float QualityCheckTimeSec = 15;
|
|
32
|
+
/// <summary>
|
|
33
|
+
/// We use this to determine GPU/CPU bound.
|
|
34
|
+
/// </summary>
|
|
35
|
+
private const int FrameTimingCount = 50;
|
|
36
|
+
private short[] _fps = new short[SampleCount];
|
|
37
|
+
private FrameTiming[] _frameTimings = new FrameTiming[FrameTimingCount];
|
|
38
|
+
private int currentQualityLevel = 5;
|
|
39
|
+
|
|
40
|
+
/// <summary>
|
|
41
|
+
/// FPS array is circular, this is the front index
|
|
42
|
+
/// </summary>
|
|
43
|
+
private int _fpsFront;
|
|
44
|
+
/// <summary>
|
|
45
|
+
/// True once we've first loaded all SampleCount samples into _fps
|
|
46
|
+
/// </summary>
|
|
47
|
+
private bool _fpsSamplesLoaded;
|
|
48
|
+
private float _nextQualityCheck;
|
|
49
|
+
|
|
50
|
+
/// <summary>
|
|
51
|
+
/// For now we only run the quality check once after 15 seconds.
|
|
52
|
+
/// </summary>
|
|
53
|
+
private bool _hasRunQualityCheck;
|
|
54
|
+
|
|
55
|
+
private void Awake() {
|
|
56
|
+
_nextQualityCheck = Time.unscaledTime + QualityCheckTimeSec;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private void Update() {
|
|
60
|
+
if (_hasRunQualityCheck) return;
|
|
61
|
+
|
|
62
|
+
FrameTimingManager.CaptureFrameTimings();
|
|
63
|
+
|
|
64
|
+
if (!_fpsSamplesLoaded && _fpsFront == SampleCount - 1) _fpsSamplesLoaded = true;
|
|
65
|
+
_fps[_fpsFront++ % SampleCount] = (short) (1d / Time.unscaledDeltaTime);
|
|
66
|
+
|
|
67
|
+
// Should we do a quality check?
|
|
68
|
+
if (Time.time > _nextQualityCheck) {
|
|
69
|
+
_hasRunQualityCheck = true;
|
|
70
|
+
DoQualityCheck();
|
|
71
|
+
_nextQualityCheck = Time.time + QualityCheckTimeSec;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
private void DoQualityCheck() {
|
|
76
|
+
var targetFrameRate = Application.targetFrameRate;
|
|
77
|
+
if (targetFrameRate < 0 || targetFrameRate > Screen.currentResolution.refreshRateRatio.value)
|
|
78
|
+
targetFrameRate = (int) (1.0 / Screen.currentResolution.refreshRateRatio.value);
|
|
79
|
+
|
|
80
|
+
var currentFivePercent = GetPercentFps(0.05f);
|
|
81
|
+
|
|
82
|
+
var frameHealth = FrameHealth.Ok;
|
|
83
|
+
var avgFrameTimings = GetRecentAverageFrameTimings();
|
|
84
|
+
|
|
85
|
+
// If our 5% is lower than 80% of target we should drop quality
|
|
86
|
+
if (currentFivePercent < 0.80 * targetFrameRate) {
|
|
87
|
+
frameHealth = FrameHealth.Unhealthy;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
OnQualityCheck?.Invoke(frameHealth, avgFrameTimings);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private QualityReport GetRecentAverageFrameTimings() {
|
|
94
|
+
if (!FrameTimingManager.IsFeatureEnabled()) return default;
|
|
95
|
+
|
|
96
|
+
var numLatestTimings = FrameTimingManager.GetLatestTimings(FrameTimingCount, _frameTimings);
|
|
97
|
+
if (numLatestTimings <= 0) return default;
|
|
98
|
+
|
|
99
|
+
var result = new QualityReport();
|
|
100
|
+
for (var i = 0; i < numLatestTimings; i++) {
|
|
101
|
+
result.cpuMainAvg += _frameTimings[i].cpuMainThreadFrameTime;
|
|
102
|
+
result.cpuRenderAvg += _frameTimings[i].cpuRenderThreadFrameTime;
|
|
103
|
+
result.gpuAvg += _frameTimings[i].gpuFrameTime;
|
|
104
|
+
}
|
|
105
|
+
result.cpuMainAvg /= numLatestTimings;
|
|
106
|
+
result.cpuRenderAvg /= numLatestTimings;
|
|
107
|
+
result.gpuAvg /= numLatestTimings;
|
|
108
|
+
result.numFrames = (int) numLatestTimings;
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/// <summary>
|
|
113
|
+
/// Gets the slowest percent of frames and averages them.
|
|
114
|
+
/// </summary>
|
|
115
|
+
private double GetPercentFps(float percent) {
|
|
116
|
+
var percentSampleCount = (int) Mathf.Ceil(SampleCount * percent);
|
|
117
|
+
|
|
118
|
+
// Load the first N samples and keep samples sorted
|
|
119
|
+
var samples = new List<short>(_fps.AsSpan(0, percentSampleCount).ToArray());
|
|
120
|
+
samples.Sort();
|
|
121
|
+
|
|
122
|
+
for (var i = percentSampleCount; i < _fps.Length; i++) {
|
|
123
|
+
// If we're slower than the best FPS in samples, insert
|
|
124
|
+
var largestFpsSample = samples[^1];
|
|
125
|
+
var sample = _fps[i];
|
|
126
|
+
if (sample < largestFpsSample) {
|
|
127
|
+
var index = samples.BinarySearch(sample);
|
|
128
|
+
// Will return bitwise compliment if larger than list
|
|
129
|
+
if (index < 0) index = ~index;
|
|
130
|
+
|
|
131
|
+
samples.Insert(index, sample);
|
|
132
|
+
samples.RemoveAt(samples.Count - 1);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
var fpsTotal = 0d;
|
|
137
|
+
foreach (var sample in samples) {
|
|
138
|
+
fpsTotal += sample;
|
|
139
|
+
}
|
|
140
|
+
return fpsTotal / samples.Count;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -177,42 +177,45 @@ public class SteamLuauAPI : Singleton<SteamLuauAPI> {
|
|
|
177
177
|
return success;
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
-
public static AirshipSteamFriendInfo[] GetSteamFriends() {
|
|
181
|
-
|
|
180
|
+
public static async Task<AirshipSteamFriendInfo[]> GetSteamFriends() {
|
|
181
|
+
Assert.IsTrue(SteamManager.Initialized, "Can't fetch friends: steam is not initialized.");
|
|
182
182
|
|
|
183
183
|
#if !STEAMWORKS_NET
|
|
184
|
-
return Array.Empty<AirshipSteamFriendInfo>();
|
|
184
|
+
return Task.FromResult(Array.Empty<AirshipSteamFriendInfo>());
|
|
185
185
|
#else
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
var
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
186
|
+
return await Task.Run(() => {
|
|
187
|
+
var friendCount = SteamFriends.GetFriendCount(EFriendFlags.k_EFriendFlagImmediate);
|
|
188
|
+
var friendInfos = new AirshipSteamFriendInfo[friendCount];
|
|
189
|
+
for (var i = 0; i < friendCount; i++) {
|
|
190
|
+
var friendId = SteamFriends.GetFriendByIndex(i, EFriendFlags.k_EFriendFlagImmediate);
|
|
191
|
+
var friendName = SteamFriends.GetFriendPersonaName(friendId);
|
|
192
|
+
var personaState = SteamFriends.GetFriendPersonaState(friendId);
|
|
193
|
+
SteamFriends.GetFriendGamePlayed(friendId, out var friendGameInfo);
|
|
194
|
+
|
|
195
|
+
var friendInfoStruct = new AirshipSteamFriendInfo {
|
|
196
|
+
steamId = friendId.m_SteamID,
|
|
197
|
+
steamName = friendName
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
// Is friend playing Airship?
|
|
201
|
+
if (friendGameInfo.m_gameID.m_GameID == SteamUtils.GetAppID().m_AppId) {
|
|
202
|
+
friendInfoStruct.playingAirship = true;
|
|
203
|
+
} else if (friendGameInfo.m_gameID.IsValid()) {
|
|
204
|
+
friendInfoStruct.playingOtherGame = true;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Check if online
|
|
208
|
+
if (personaState != EPersonaState.k_EPersonaStateOffline) {
|
|
209
|
+
friendInfoStruct.online = true;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
friendInfos[i] = friendInfoStruct;
|
|
204
213
|
}
|
|
205
214
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
friendInfos[i] = friendInfoStruct;
|
|
212
|
-
}
|
|
213
|
-
return friendInfos;
|
|
214
|
-
#endif
|
|
215
|
-
}
|
|
215
|
+
return friendInfos;
|
|
216
|
+
});
|
|
217
|
+
#endif
|
|
218
|
+
}
|
|
216
219
|
|
|
217
220
|
/// <summary>
|
|
218
221
|
/// Can return null.
|
|
@@ -54,6 +54,7 @@ namespace Code.VoiceChat {
|
|
|
54
54
|
public ChatroomAgent agent;
|
|
55
55
|
|
|
56
56
|
private uint audioNonce = 0;
|
|
57
|
+
private bool deafened = false;
|
|
57
58
|
|
|
58
59
|
|
|
59
60
|
private void OnDisable() {
|
|
@@ -327,6 +328,8 @@ namespace Code.VoiceChat {
|
|
|
327
328
|
}
|
|
328
329
|
|
|
329
330
|
private void EmitAudioInScene(short senderPeerId, byte[] bytes) {
|
|
331
|
+
if (this.deafened) return;
|
|
332
|
+
|
|
330
333
|
var segment = FromByteArray<ChatroomAudioSegment>(bytes);
|
|
331
334
|
|
|
332
335
|
this.PreProcessAudio(segment);
|
|
@@ -384,6 +387,10 @@ namespace Code.VoiceChat {
|
|
|
384
387
|
}
|
|
385
388
|
}
|
|
386
389
|
|
|
390
|
+
public void SetDeafened(bool deafened) {
|
|
391
|
+
this.deafened = deafened;
|
|
392
|
+
}
|
|
393
|
+
|
|
387
394
|
/// <summary>
|
|
388
395
|
/// Returns the UniVoice peer Id corresponding to a previously
|
|
389
396
|
/// registered Mirror connection Id
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
void SampleWithDerivatives_float(UnityTexture2D tex, UnitySamplerState samplerState, float2 uv, float2 dx, float2 dy, out float4 color) {
|
|
2
|
+
// The lower our derivative scale the further out we'll see
|
|
3
|
+
// high quality mips. Because we use a texture atlas that can bleed this needs
|
|
4
|
+
// to be relatively low.
|
|
5
|
+
float derivativeScale = 0.05;
|
|
6
|
+
color = tex.SampleGrad(samplerState, uv, dx * derivativeScale, dy * derivativeScale);
|
|
7
|
+
}
|