diff --git a/Assets/Prefabs/UI/SongView.prefab b/Assets/Prefabs/UI/SongView.prefab
index 04405ff6e..324967e2a 100644
--- a/Assets/Prefabs/UI/SongView.prefab
+++ b/Assets/Prefabs/UI/SongView.prefab
@@ -88,7 +88,7 @@ GameObject:
- component: {fileID: 7898971487434874996}
- component: {fileID: 1200385484507932754}
m_Layer: 5
- m_Name: Length Text
+ m_Name: Score Text
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
@@ -760,7 +760,7 @@ MonoBehaviour:
m_EditorClassIdentifier:
songName: {fileID: 6846856528720448031}
artist: {fileID: 7006697502306556741}
- lengthText: {fileID: 1200385484507932754}
+ scoreText: {fileID: 1200385484507932754}
--- !u!225 &215129106566158253
CanvasGroup:
m_ObjectHideFlags: 0
diff --git a/Assets/Scenes/MenuScene.unity b/Assets/Scenes/MenuScene.unity
index 1f9a05b95..e9e521747 100644
--- a/Assets/Scenes/MenuScene.unity
+++ b/Assets/Scenes/MenuScene.unity
@@ -3811,7 +3811,7 @@ RectTransform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 1, y: 1}
m_AnchorMax: {x: 1, y: 1}
- m_AnchoredPosition: {x: -7.9999695, y: -30.500004}
+ m_AnchoredPosition: {x: -7.9999084, y: -52.5}
m_SizeDelta: {x: 125, y: 11}
m_Pivot: {x: 0.9999996, y: 1}
--- !u!114 &1886399724
@@ -4116,7 +4116,7 @@ GameObject:
- component: {fileID: 1986583193}
- component: {fileID: 1986583192}
m_Layer: 5
- m_Name: Length Text
+ m_Name: Score Text
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
@@ -4140,7 +4140,7 @@ RectTransform:
m_AnchorMin: {x: 1, y: 1}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: -7.9999695, y: -12.5}
- m_SizeDelta: {x: 125, y: 29}
+ m_SizeDelta: {x: 125, y: 20}
m_Pivot: {x: 0.9999996, y: 1}
--- !u!114 &1986583192
MonoBehaviour:
@@ -4182,7 +4182,7 @@ MonoBehaviour:
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
- m_text: N/A
+ m_text: 1:23
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: ae170e91fd29a90479e906ddffb1d8ee, type: 2}
m_sharedMaterial: {fileID: 2100000, guid: 97a4ac31562f98641a2b7822989f607a, type: 2}
@@ -4292,6 +4292,7 @@ RectTransform:
m_Children:
- {fileID: 1844302445}
- {fileID: 1986583191}
+ - {fileID: 2086297276}
- {fileID: 1886399723}
- {fileID: 1330867043}
- {fileID: 80151784}
@@ -4358,13 +4359,170 @@ MonoBehaviour:
m_EditorClassIdentifier:
songName: {fileID: 149797927}
artist: {fileID: 1290299072}
- lengthText: {fileID: 1986583193}
+ scoreText: {fileID: 1986583193}
+ lengthText: {fileID: 2086297278}
supportText: {fileID: 1886399725}
albumCover: {fileID: 1330867046}
albumCoverAlt: {fileID: 1876917083}
difficultyContainer: {fileID: 332568989}
difficultyView: {fileID: 397774209812536635, guid: 53438a07c6e5ec5409d5bde8514761ca,
type: 3}
+--- !u!1 &2086297275
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 2086297276}
+ - component: {fileID: 2086297279}
+ - component: {fileID: 2086297278}
+ - component: {fileID: 2086297277}
+ m_Layer: 5
+ m_Name: Length Text
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &2086297276
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2086297275}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 2044165073}
+ m_RootOrder: -1
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 1, y: 1}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: -7.9999084, y: -32.5}
+ m_SizeDelta: {x: 125, y: 20}
+ m_Pivot: {x: 0.9999996, y: 1}
+--- !u!114 &2086297277
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2086297275}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 306cc8c2b49d7114eaa3623786fc2126, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_IgnoreLayout: 0
+ m_MinWidth: -1
+ m_MinHeight: -1
+ m_PreferredWidth: 0
+ m_PreferredHeight: -1
+ m_FlexibleWidth: -1
+ m_FlexibleHeight: -1
+ m_LayoutPriority: 1
+--- !u!114 &2086297278
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2086297275}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 1, g: 1, b: 1, a: 1}
+ m_RaycastTarget: 1
+ m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
+ m_Maskable: 1
+ m_OnCullStateChanged:
+ m_PersistentCalls:
+ m_Calls: []
+ m_text: N/A
+ m_isRightToLeft: 0
+ m_fontAsset: {fileID: 11400000, guid: ae170e91fd29a90479e906ddffb1d8ee, type: 2}
+ m_sharedMaterial: {fileID: 2100000, guid: 97a4ac31562f98641a2b7822989f607a, type: 2}
+ m_fontSharedMaterials: []
+ m_fontMaterial: {fileID: 0}
+ m_fontMaterials: []
+ m_fontColor32:
+ serializedVersion: 2
+ rgba: 4294967295
+ m_fontColor: {r: 1, g: 1, b: 1, a: 1}
+ m_enableVertexGradient: 0
+ m_colorMode: 3
+ m_fontColorGradient:
+ topLeft: {r: 1, g: 1, b: 1, a: 1}
+ topRight: {r: 1, g: 1, b: 1, a: 1}
+ bottomLeft: {r: 1, g: 1, b: 1, a: 1}
+ bottomRight: {r: 1, g: 1, b: 1, a: 1}
+ m_fontColorGradientPreset: {fileID: 0}
+ m_spriteAsset: {fileID: 0}
+ m_tintAllSprites: 0
+ m_StyleSheet: {fileID: 0}
+ m_TextStyleHashCode: -1183493901
+ m_overrideHtmlColors: 0
+ m_faceColor:
+ serializedVersion: 2
+ rgba: 4294967295
+ m_fontSize: 16
+ m_fontSizeBase: 16
+ m_fontWeight: 400
+ m_enableAutoSizing: 0
+ m_fontSizeMin: 18
+ m_fontSizeMax: 72
+ m_fontStyle: 0
+ m_HorizontalAlignment: 4
+ m_VerticalAlignment: 256
+ m_textAlignment: 65535
+ m_characterSpacing: 0
+ m_wordSpacing: 0
+ m_lineSpacing: 0
+ m_lineSpacingMax: 0
+ m_paragraphSpacing: 0
+ m_charWidthMaxAdj: 0
+ m_enableWordWrapping: 0
+ m_wordWrappingRatios: 0.4
+ m_overflowMode: 1
+ m_linkedTextComponent: {fileID: 0}
+ parentLinkedComponent: {fileID: 0}
+ m_enableKerning: 1
+ m_enableExtraPadding: 0
+ checkPaddingRequired: 0
+ m_isRichText: 1
+ m_parseCtrlCharacters: 1
+ m_isOrthographic: 1
+ m_isCullingEnabled: 0
+ m_horizontalMapping: 0
+ m_verticalMapping: 0
+ m_uvLineOffset: 0
+ m_geometrySortingOrder: 0
+ m_IsTextObjectScaleStatic: 0
+ m_VertexBufferAutoSizeReduction: 0
+ m_useMaxVisibleDescender: 1
+ m_pageToDisplay: 1
+ m_margin: {x: 0, y: 0, z: 0, w: 0}
+ m_isUsingLegacyAnimationComponent: 0
+ m_isVolumetricText: 0
+ m_hasFontAssetChanged: 0
+ m_baseMaterial: {fileID: 0}
+ m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
+--- !u!222 &2086297279
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2086297275}
+ m_CullTransparentMesh: 1
--- !u!1 &2115213575
GameObject:
m_ObjectHideFlags: 0
diff --git a/Assets/Script/Data/SongInfo.cs b/Assets/Script/Data/SongInfo.cs
index d296c70d0..d58dd0b86 100644
--- a/Assets/Script/Data/SongInfo.cs
+++ b/Assets/Script/Data/SongInfo.cs
@@ -27,6 +27,11 @@ public class SongInfo {
[JsonConverter(typeof(DirectoryInfoConverter))]
public DirectoryInfo folder;
+ ///
+ /// For remote mode only.
+ ///
+ public DirectoryInfo realFolderRemote;
+
public bool BassPedal2xExpertPlus {
private set;
get;
@@ -163,5 +168,9 @@ public SongInfo(DirectoryInfo folder) {
partDifficulties = new(DEFAULT_DIFFS);
}
+
+ public SongInfo Duplicate() {
+ return (SongInfo) MemberwiseClone();
+ }
}
}
\ No newline at end of file
diff --git a/Assets/Script/Data/SongScore.cs b/Assets/Script/Data/SongScore.cs
new file mode 100644
index 000000000..a5905e760
--- /dev/null
+++ b/Assets/Script/Data/SongScore.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Newtonsoft.Json;
+
+namespace YARG.Data {
+ [JsonObject(MemberSerialization.Fields)]
+ public class SongScore {
+ public DateTime lastPlayed;
+ public int timesPlayed;
+
+ public float TotalHighestPercent => highestPercent.Max(i => i.Value);
+ public Dictionary highestPercent;
+ }
+}
\ No newline at end of file
diff --git a/Assets/Script/Data/SongScore.cs.meta b/Assets/Script/Data/SongScore.cs.meta
new file mode 100644
index 000000000..5a14dc73a
--- /dev/null
+++ b/Assets/Script/Data/SongScore.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 1b5aea9ad45657b428a02e718545b516
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Script/PlayMode/AbstractTrack.cs b/Assets/Script/PlayMode/AbstractTrack.cs
index 19e50ec3c..ed47dadd7 100644
--- a/Assets/Script/PlayMode/AbstractTrack.cs
+++ b/Assets/Script/PlayMode/AbstractTrack.cs
@@ -109,6 +109,8 @@ private void Start() {
player.inputStrategy.StarpowerEvent += StarpowerAction;
Play.Instance.BeatEvent += BeatAction;
+ player.lastScore = null;
+
GameUI.Instance.AddTrackImage(trackCamera.targetTexture);
// Adjust hit window
diff --git a/Assets/Script/PlayMode/DrumsTrack.cs b/Assets/Script/PlayMode/DrumsTrack.cs
index a0118a0fb..251d143a9 100644
--- a/Assets/Script/PlayMode/DrumsTrack.cs
+++ b/Assets/Script/PlayMode/DrumsTrack.cs
@@ -57,7 +57,7 @@ protected override void OnDestroy() {
// Set score
player.lastScore = new PlayerManager.Score {
- percentage = (float) notesHit / Chart.Count,
+ percentage = notesHit == 0 ? 1f : (float) notesHit / Chart.Count,
notesHit = notesHit,
notesMissed = Chart.Count - notesHit
};
diff --git a/Assets/Script/PlayMode/FiveFretTrack.cs b/Assets/Script/PlayMode/FiveFretTrack.cs
index 9c5ac7b21..a90f5617d 100644
--- a/Assets/Script/PlayMode/FiveFretTrack.cs
+++ b/Assets/Script/PlayMode/FiveFretTrack.cs
@@ -63,7 +63,7 @@ protected override void OnDestroy() {
// Set score
player.lastScore = new PlayerManager.Score {
- percentage = (float) notesHit / Chart.Count,
+ percentage = notesHit == 0 ? 1f : (float) notesHit / Chart.Count,
notesHit = notesHit,
notesMissed = Chart.Count - notesHit
};
diff --git a/Assets/Script/PlayMode/RealGuitarTrack.cs b/Assets/Script/PlayMode/RealGuitarTrack.cs
index e36cf93bc..f6664e15c 100644
--- a/Assets/Script/PlayMode/RealGuitarTrack.cs
+++ b/Assets/Script/PlayMode/RealGuitarTrack.cs
@@ -70,7 +70,7 @@ protected override void OnDestroy() {
// Set score
player.lastScore = new PlayerManager.Score {
- percentage = (float) notesHit / Chart.Count,
+ percentage = notesHit == 0 ? 1f : (float) notesHit / Chart.Count,
notesHit = notesHit,
notesMissed = Chart.Count - notesHit
};
diff --git a/Assets/Script/ScoreManager.cs b/Assets/Script/ScoreManager.cs
new file mode 100644
index 000000000..5a2a04410
--- /dev/null
+++ b/Assets/Script/ScoreManager.cs
@@ -0,0 +1,102 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Threading;
+using Newtonsoft.Json;
+using YARG.Data;
+
+namespace YARG {
+ public static class ScoreManager {
+ ///
+ /// The location of the local or remote score file (depending on whether we are connected to a server).
+ ///
+ public static FileInfo ScoreFile {
+ get {
+ if (GameManager.client != null) {
+ return GameManager.client.remoteScore;
+ }
+
+ return new(Path.Combine(SongLibrary.songFolder.ToString(), "yarg_score.json"));
+ }
+ }
+
+ private static Dictionary scores = null;
+
+ ///
+ /// Should be called before you access any scores.
+ ///
+ public static void FetchScores() {
+ if (scores != null) {
+ return;
+ }
+
+ // Read from score file OR create new
+ if (ScoreFile.Exists) {
+ string json = File.ReadAllText(ScoreFile.ToString());
+ scores = JsonConvert.DeserializeObject>(json);
+ } else {
+ scores = new();
+
+ // Create a dummy score file if one doesn't exist.
+ File.WriteAllText(ScoreFile.ToString(), "{}");
+ }
+ }
+
+ public static void PushScore(SongInfo song, SongScore score) {
+ string path = song.folder.ToString();
+ if (GameManager.client != null) {
+ path = song.realFolderRemote.ToString();
+ }
+
+ if (!scores.TryGetValue(path, out var oldScore)) {
+ // If the score info doesn't exist, just add the new one.
+ scores.Add(path, score);
+ } else {
+ // Otherwise, MERGE!
+ oldScore.lastPlayed = score.lastPlayed;
+ oldScore.timesPlayed += score.timesPlayed;
+
+ // Merge high scores
+ foreach (var kvp in score.highestPercent) {
+ if (oldScore.highestPercent.TryGetValue(kvp.Key, out var old)) {
+ if (old < kvp.Value) {
+ oldScore.highestPercent[kvp.Key] = kvp.Value;
+ }
+ } else {
+ oldScore.highestPercent.Add(kvp.Key, kvp.Value);
+ }
+ }
+ }
+
+ // Save ASAP!
+ SaveScore();
+ }
+
+ public static SongScore GetScore(SongInfo song) {
+ string path = song.folder.ToString();
+ if (song.realFolderRemote != null && GameManager.client != null) {
+ path = song.realFolderRemote.ToString();
+ }
+
+ if (scores.TryGetValue(path, out var o)) {
+ return o;
+ }
+
+ return null;
+ }
+
+ public static void SaveScore() {
+ var scoreCopy = new Dictionary(scores);
+
+ // Prevent game lag by saving on another thread
+ ThreadPool.QueueUserWorkItem(_ => {
+ string json = JsonConvert.SerializeObject(scores, Formatting.Indented);
+ File.WriteAllText(ScoreFile.ToString(), json);
+
+ // If remote, write scores on server
+ if (GameManager.client != null) {
+ GameManager.client.WriteScores();
+ }
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/Script/ScoreManager.cs.meta b/Assets/Script/ScoreManager.cs.meta
new file mode 100644
index 000000000..c9338b183
--- /dev/null
+++ b/Assets/Script/ScoreManager.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 1737174bd22f1cb4cb8f01303c5e69ec
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Script/Server/Client.cs b/Assets/Script/Server/Client.cs
index 21b7e1fef..c6f1b2101 100644
--- a/Assets/Script/Server/Client.cs
+++ b/Assets/Script/Server/Client.cs
@@ -14,6 +14,7 @@ public class Client {
public event SignalAction SignalEvent;
public FileInfo remoteCache;
+ public FileInfo remoteScore;
public string remotePath;
public string AlbumCoversPath => Path.Combine(remotePath, "_album_covers");
@@ -51,12 +52,15 @@ public void Start(string ip) {
private void ClientThread() {
var stream = client.GetStream();
- // Request cache
- Send(stream, "ReqCache");
-
// Read cache from server
+ Send(stream, "ReqCache");
remoteCache = new(Path.Combine(remotePath, "yarg_cache.json"));
- ReadFile(stream, remoteCache);
+ Utils.ReadFile(stream, remoteCache);
+
+ // Read score from server
+ Send(stream, "ReqScore");
+ remoteScore = new(Path.Combine(remotePath, "yarg_score.json"));
+ Utils.ReadFile(stream, remoteScore);
// Wait until request
while (true) {
@@ -66,7 +70,7 @@ private void ClientThread() {
if (request.StartsWith("ReqSong,")) {
// Read zipped song from server
string zipPath = Path.Combine(remotePath, "download.zip");
- ReadFile(stream, new(zipPath));
+ Utils.ReadFile(stream, new(zipPath));
// When done, unzip file
string folderName = Utils.Hash(request[8..]);
@@ -81,10 +85,32 @@ private void ClientThread() {
// Read album.png from server
string hash = Utils.Hash(request[14..]);
string pngPath = Path.Combine(AlbumCoversPath, $"{hash}.png");
- ReadFile(stream, new(pngPath));
+ Utils.ReadFile(stream, new(pngPath));
// Send signal
signals.Enqueue($"AlbumCoverDone,{hash}");
+ } else if (request == "WriteScores") {
+ // TODO: This sucks, but I'm too lazy
+ // Wait for proceed on the stream
+ while (true) {
+ if (stream.DataAvailable) {
+ // Get data from client
+ byte[] bytes = new byte[1024];
+ int size = stream.Read(bytes, 0, bytes.Length);
+
+ // Get request
+ var str = Encoding.UTF8.GetString(bytes, 0, size);
+
+ if (str == "ProceedWriteScores") {
+ SendFile(stream, ScoreManager.ScoreFile);
+ }
+
+ break;
+ }
+
+ // Prevent CPU burn
+ Thread.Sleep(10);
+ }
}
}
@@ -99,34 +125,14 @@ private void Send(NetworkStream stream, string str) {
stream.Flush();
}
- private void ReadFile(NetworkStream stream, FileInfo output) {
- const int BUF_SIZE = 81920;
-
- // Wait until data is available
- while (!stream.DataAvailable) {
- Thread.Sleep(100);
- }
-
- // Get file size
- var buffer = new byte[sizeof(long)];
- stream.Read(buffer, 0, sizeof(long));
- long size = BitConverter.ToInt64(buffer);
+ private void SendFile(NetworkStream stream, FileInfo file) {
+ using var fs = file.OpenRead();
- // If the size is zero, the file did not exist on server
- if (size <= 0) {
- return;
- }
+ // Send file size
+ stream.Write(BitConverter.GetBytes(fs.Length));
- // Copy data to disk
- // We can't use CopyTo on a infinite stream (like NetworkStream)
- long totalRead = 0;
- var fileBuf = new byte[BUF_SIZE];
- using var fs = output.OpenWrite();
- while (totalRead < size) {
- int bytesRead = stream.Read(fileBuf, 0, BUF_SIZE);
- fs.Write(fileBuf, 0, bytesRead);
- totalRead += bytesRead;
- }
+ // Send file itself
+ fs.CopyTo(stream);
}
public void Stop() {
@@ -183,5 +189,9 @@ public void RequestAlbumCover(string path) {
// Otherwise, we have to request it
requests.Enqueue($"ReqAlbumCover,{path}");
}
+
+ public void WriteScores() {
+ requests.Enqueue("WriteScores");
+ }
}
}
\ No newline at end of file
diff --git a/Assets/Script/Server/Host.cs b/Assets/Script/Server/Host.cs
index 17c665d37..7cc664a20 100644
--- a/Assets/Script/Server/Host.cs
+++ b/Assets/Script/Server/Host.cs
@@ -4,8 +4,10 @@
using System.IO.Compression;
using System.Net;
using System.Net.Sockets;
+using System.Text;
using System.Threading;
using UnityEngine;
+using YARG.Util;
namespace YARG.Server {
public partial class Host : MonoBehaviour {
@@ -18,8 +20,9 @@ private void Start() {
GameManager.Instance.LowQualityMode = true;
Application.targetFrameRate = 5;
- // Fetch songs first so we have a cache file to send
+ // Fetch songs and scores first so we have a cache file to send
SongLibrary.FetchSongs();
+ ScoreManager.FetchScores();
// Create the TcpListener
server = new TcpListener(IPAddress.Any, 6145);
@@ -60,7 +63,7 @@ private void ServerThread(TcpClient client) {
int size = stream.Read(bytes, 0, bytes.Length);
// Get request
- var str = System.Text.Encoding.UTF8.GetString(bytes, 0, size);
+ var str = Encoding.UTF8.GetString(bytes, 0, size);
Log($"Received: `{str}`.");
// Do something
@@ -68,6 +71,8 @@ private void ServerThread(TcpClient client) {
break;
} else if (str == "ReqCache") {
SendFile(stream, SongLibrary.CacheFile);
+ } else if (str == "ReqScore") {
+ SendFile(stream, ScoreManager.ScoreFile);
} else if (str.StartsWith("ReqSong,")) {
// Get the folder
string path = str[8..];
@@ -106,6 +111,20 @@ private void ServerThread(TcpClient client) {
} else {
SendNoFile(stream);
}
+ } else if (str == "WriteScores") {
+ Send(stream, "ProceedWriteScores");
+
+ // TODO: This sucks, but I'm too lazy
+ // Wait for file on the stream
+ while (true) {
+ if (stream.DataAvailable) {
+ Utils.ReadFile(stream, ScoreManager.ScoreFile);
+ break;
+ }
+
+ // Prevent CPU burn
+ Thread.Sleep(10);
+ }
}
} else {
// Prevent CPU burn
@@ -136,6 +155,12 @@ private void SendNoFile(NetworkStream stream) {
stream.Write(BitConverter.GetBytes(0));
}
+ private void Send(NetworkStream stream, string str) {
+ var send = Encoding.UTF8.GetBytes(str);
+ stream.Write(send, 0, send.Length);
+ stream.Flush();
+ }
+
private void OnDestroy() {
foreach (var thread in threads) {
thread.Abort();
diff --git a/Assets/Script/UI/MainMenu.cs b/Assets/Script/UI/MainMenu.cs
index 49c364f85..4d631ec69 100644
--- a/Assets/Script/UI/MainMenu.cs
+++ b/Assets/Script/UI/MainMenu.cs
@@ -1,9 +1,10 @@
+using System;
+using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.UIElements;
using YARG.Data;
using YARG.PlayMode;
-using YARG.Serialization;
using YARG.Util;
namespace YARG.UI {
@@ -66,9 +67,12 @@ private void OnDisable() {
private void SignalRecieved(string signal) {
if (signal.StartsWith("DownloadDone,")) {
- Play.song = SongIni.CompleteSongInfo(new SongInfo(
- new(Path.Combine(GameManager.client.remotePath, signal[13..]))
- ));
+ Play.song = chosenSong.Duplicate();
+
+ // Replace song folder
+ Play.song.realFolderRemote = Play.song.folder;
+ Play.song.folder = new(Path.Combine(GameManager.client.remotePath, signal[13..]));
+
GameManager.Instance.LoadScene(SceneIndex.PLAY);
}
}
@@ -174,7 +178,7 @@ private void SetupPreSong() {
3 => "drums",
4 => "realGuitar",
5 => "realBass",
- _ => throw new System.Exception("Unreachable.")
+ _ => throw new Exception("Unreachable.")
};
player.chosenDifficulty = difficultyChoice.value;
@@ -187,6 +191,45 @@ private void SetupPreSong() {
private void SetupPostSong() {
var root = postSongDocument.rootVisualElement;
+ // Create a score to push
+
+ var songScore = new SongScore {
+ lastPlayed = DateTime.Now,
+ timesPlayed = 1,
+ highestPercent = new()
+ };
+ var oldScore = ScoreManager.GetScore(Play.song);
+
+ HashSet highScores = new();
+ foreach (var player in PlayerManager.players) {
+ if (player.inputStrategy.botMode) {
+ continue;
+ }
+
+ if (!player.lastScore.HasValue) {
+ continue;
+ }
+
+ var lastScore = player.lastScore.GetValueOrDefault();
+
+ // Skip if the chart has no notes (will be viewed as 100%)
+ if (lastScore.notesHit == 0) {
+ continue;
+ }
+
+ // Override or add percentage
+ if (oldScore == null ||
+ !oldScore.highestPercent.TryGetValue(player.chosenInstrument, out var oldHighest) ||
+ lastScore.percentage > oldHighest) {
+
+ songScore.highestPercent[player.chosenInstrument] = lastScore.percentage;
+ highScores.Add(player);
+ }
+ }
+
+ // Push!
+ ScoreManager.PushScore(Play.song, songScore);
+
// Setup score label
var label = root.Q