diff --git a/ValorantCC/MainWindow.xaml b/ValorantCC/MainWindow.xaml index f912fdd..4b5a810 100644 --- a/ValorantCC/MainWindow.xaml +++ b/ValorantCC/MainWindow.xaml @@ -170,6 +170,7 @@ + diff --git a/ValorantCC/MainWindow.xaml.cs b/ValorantCC/MainWindow.xaml.cs index c6adef7..af12c90 100644 --- a/ValorantCC/MainWindow.xaml.cs +++ b/ValorantCC/MainWindow.xaml.cs @@ -13,6 +13,7 @@ using Utilities; using ValorantCC.src; using ValorantCC.SubWindow; +using Newtonsoft.Json; namespace ValorantCC { @@ -26,7 +27,8 @@ public partial class MainWindow : MetroWindow public API ValCCAPI; public int SelectedIndex; public bool LoggedIn; - + private string _sharecode; + private FetchResponse? sharedProfileResp; public MainWindow() { // Create logging dir @@ -69,7 +71,7 @@ private async void btnSave_Click(object sender, RoutedEventArgs e) return; } - private void profiles_SelectionChanged(object sender, SelectionChangedEventArgs e) + private async void profiles_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (profiles.SelectedIndex == -1) return; @@ -91,6 +93,30 @@ private void profiles_SelectionChanged(object sender, SelectionChangedEventArgs ads_color.SelectedColor = Color.FromRgb(SelectedProfile.aDS.Color.R, SelectedProfile.aDS.Color.G, SelectedProfile.aDS.Color.B); ads_outline_color.SelectedColor = Color.FromRgb(SelectedProfile.aDS.OutlineColor.R, SelectedProfile.aDS.OutlineColor.G, SelectedProfile.aDS.OutlineColor.B); sniper_dot_color.SelectedColor = Color.FromRgb(SelectedProfile.Sniper.CenterDotColor.R, SelectedProfile.Sniper.CenterDotColor.G, SelectedProfile.Sniper.CenterDotColor.B); + if (!sharedProfileResp.HasValue) + { + if (ValCCAPI != null) sharedProfileResp = await ValCCAPI.ObtainSelfSaved(); + } + else + { + var respData = sharedProfileResp.Value; + if (respData.success && respData.data != null) + { + var data = respData.data.FirstOrDefault(); + _sharecode = data.shareCode; + CrosshairProfile fromDb = JsonConvert.DeserializeObject(data.settings); + if (SelectedProfile.ProfileName == fromDb.ProfileName) + { + chkbxShareable.IsChecked = data.shareable; + btnCopyShareCode.Visibility = Visibility.Visible; + } + else + { + chkbxShareable.IsChecked = false; + btnCopyShareCode.Visibility = Visibility.Collapsed; + } + } + } Crosshair_load(); } private void StackPanel_MouseDown(object sender, MouseButtonEventArgs e) @@ -321,7 +347,13 @@ private async void btnShare_Click(object sender, RoutedEventArgs e) ValCCAPI.Shareable = (bool)chkbxShareable.IsChecked; ValCCAPI.profile = SelectedProfile; SetCallResponse response = await ValCCAPI.Set(); - String sharecode = response.data.shareCode; + if (!response.success) + { + Utilities.Utils.Log($"Unable to share crosshair: {response.message}"); + Utilities.Utils.MessageText("Unable to share crosshair: Internal Error/Conflict", Brushes.Red); + return; + } + String sharecode = response.message; Clipboard.SetText(sharecode); MessageWindow.Show($"Your sharecode is: \"{sharecode}\" and is copied.\nIf you want this profile accessible across the community,\nPlease be sure that you have the 'shareable' checkbox checked.", "Profile shared!"); @@ -330,6 +362,8 @@ private async void btnShare_Click(object sender, RoutedEventArgs e) private async void spinner_Loaded(object sender, RoutedEventArgs e) { + Trace.WriteLine(ValorantCC.src.Community.SCGen.GenerateShareCode("64f8b630-64c6-5838-9daa-916f8bbf5587")); + Trace.WriteLine(ValorantCC.src.Community.SCGen.GenerateShareCode("b19f4928-e1ed-5f63-a9d0-8a7fd202e3b2")); Updater.CustomLogger = Utilities.Utils.Log; Updater.OriginalFileName = "ValorantCC"; Updater.LogInterfix = " | "; @@ -351,5 +385,11 @@ private async void spinner_Loaded(object sender, RoutedEventArgs e) update.ShowDialog(); } } + + private void btnCopyShareCode_Click(object sender, RoutedEventArgs e) + { + Clipboard.SetText(_sharecode); + Utilities.Utils.MessageText("Your sharecode has been copied!", Brushes.Lime); + } } } diff --git a/ValorantCC/SubWindow/ProfilesWindow.xaml.cs b/ValorantCC/SubWindow/ProfilesWindow.xaml.cs index 8d293a1..e663898 100644 --- a/ValorantCC/SubWindow/ProfilesWindow.xaml.cs +++ b/ValorantCC/SubWindow/ProfilesWindow.xaml.cs @@ -74,16 +74,20 @@ private async Task InitialFetch(String sharecode = null) if (!string.IsNullOrWhiteSpace(sharecode)) { Utilities.Utils.Log($"Fetching profile with code: {sharecode}"); - ValCCApi.Action = 3; FetchResponse fetchResponse = await ValCCApi.Fetch(sharecode); if (!fetchResponse.success) return false; + if (fetchResponse.data.Count < 1) + { + fetchErrorTxt.Text = "User with corresponding sharecode was not found."; + btnSearchCode.IsEnabled = true; + return false; + } Shareables = fetchResponse.data; } else { Utilities.Utils.Log($"Fetching profiles from server"); - ValCCApi.Action = 2; - FetchResponse fetchResponse = await ValCCApi.Fetch(sharecode); + FetchResponse fetchResponse = await ValCCApi.Fetch(); if (!fetchResponse.success) return false; Shareables = fetchResponse.data; } diff --git a/ValorantCC/ValorantCC.csproj b/ValorantCC/ValorantCC.csproj index e04c93e..a0a921b 100644 --- a/ValorantCC/ValorantCC.csproj +++ b/ValorantCC/ValorantCC.csproj @@ -75,11 +75,11 @@ - + - + diff --git a/ValorantCC/src/API.cs b/ValorantCC/src/API.cs index 4572613..24a3bea 100644 --- a/ValorantCC/src/API.cs +++ b/ValorantCC/src/API.cs @@ -4,28 +4,20 @@ using System.Collections.Generic; using System.Threading.Tasks; using Utilities; - +using ValorantCC.src.Community; namespace ValorantCC { public struct SetCallResponse { public bool success { get; set; } - public SetResponse data { get; set; } - } - - public struct SetResponse - { - public string settings { get; set; } - public string displayName { get; set; } - public string gameTag { get; set; } - public string shareCode { get; set; } - public bool shareable { get; set; } + public string message { get; set; } } public struct FetchResponse { public bool success { get; set; } public List data { get; set; } + public string message { get; set; } } public struct ShareableProfile @@ -36,77 +28,111 @@ public struct ShareableProfile public String displayName { get; set; } } - public partial class PostPayload + public partial class Payload { - public String subject { get; set; } public String settings { get; set; } public bool shareable { get; set; } - public String sharecode { get; set; } = null; - public int action { get; set; } + public String sharecode { get; set; } } public class API { private static AuthTokens AuthTokens; - private static RestClient client = new RestClient("https://valorantcc.000webhostapp.com/api.php"); + private static RestClient client = new RestClient("https://vtools-next.vercel.app/api"); public CrosshairProfile profile; - public int Action; public bool Shareable; public API(AuthTokens Tokens, CrosshairProfile TargetProfile, int ActionInt, bool isShareable) { AuthTokens = Tokens; profile = TargetProfile; - Action = ActionInt; Shareable = isShareable; } public async Task Fetch(String sharecode = null) { - PostPayload payload = new PostPayload() + RestRequest request; + if (sharecode != null) + request = new RestRequest($"/sharecode/{sharecode}", Method.Get); + else + request = new RestRequest("/profiles", Method.Get); + + RestResponse response = await client.ExecuteAsync(request); + if (!response.IsSuccessful && response.StatusCode != System.Net.HttpStatusCode.NotFound) { - subject = AuthTokens.Subject, - settings = JsonConvert.SerializeObject(profile), - shareable = Shareable, - action = Action - }; - if (sharecode != null) + Utils.Log(response.Content.ToString()); + return new FetchResponse() { success = false }; + } + if (response.StatusCode == System.Net.HttpStatusCode.NotFound) { - payload.sharecode = sharecode; - payload.action = Action; + return new FetchResponse() { success = true, data = new List()}; } - RestRequest request = new RestRequest() { Method = Method.Post }; - request.AddJsonBody(payload); + + return new FetchResponse() + { + success = true, + data = JsonConvert.DeserializeObject>(response.Content) + }; + } + + public async Task ObtainSelfSaved() + { + RestRequest request = new RestRequest($"/profiles/{AuthTokens.Subject}/fetch", Method.Get); + RestResponse response = await client.ExecuteAsync(request); - if (!response.IsSuccessful) + if (!response.IsSuccessful && response.StatusCode != System.Net.HttpStatusCode.NotFound) { - Utilities.Utils.Log(response.Content.ToString()); - return new FetchResponse() { success = false }; + Utils.Log(response.Content.ToString()); + return new FetchResponse() { success = false, message = response.Content.ToString() }; } - + if (response.StatusCode == System.Net.HttpStatusCode.NotFound) + return new FetchResponse() { success = true, message = response.Content.ToString() }; - return JsonConvert.DeserializeObject(response.Content); + return new FetchResponse() + { + success = true, + data = new List() { JsonConvert.DeserializeObject(response.Content) } + }; } public async Task Set() { - PostPayload payload = new PostPayload() + Payload payload = new Payload() { - subject = AuthTokens.Subject, settings = JsonConvert.SerializeObject(profile), shareable = Shareable, - action = 1 + sharecode = SCGen.GenerateShareCode(AuthTokens.Subject) }; - RestRequest request = new RestRequest() { Method = Method.Post }; - request.RequestFormat = DataFormat.Json; + RestRequest request = new RestRequest($"/profiles/{AuthTokens.Subject}/set", Method.Put) { RequestFormat = DataFormat.Json }; request.AddJsonBody(payload); request.AddOrUpdateHeader("Authorization", $"Bearer {AuthTokens.AccessToken}"); // Pass to server so nobody can set somebody's saved profile. RestResponse response = await client.ExecuteAsync(request); - if (!response.IsSuccessful) + if (!response.IsSuccessful) { - Utilities.Utils.Log(response.Content.ToString()); - return new SetCallResponse() { success = false }; + Utils.Log(response.Content.ToString()); + int retry = 0; + while (response.StatusCode == System.Net.HttpStatusCode.Conflict) + { + await Task.Delay(500); + if (response.StatusCode != System.Net.HttpStatusCode.Conflict) break; + // Retry until 5 retries or removed the conflict. + payload.sharecode = SCGen.GenerateShareCode(AuthTokens.Subject); + request = new RestRequest($"/profiles/{AuthTokens.Subject}/set", Method.Put) { RequestFormat = DataFormat.Json }; + request.AddJsonBody(payload); + request.AddOrUpdateHeader("Authorization", $"Bearer {AuthTokens.AccessToken}"); + response = await client.ExecuteAsync(request); + retry++; + Utils.Log($"Conflict removal retry: {retry}: {response.Content}"); + if (retry >= 5) break; + } + if (response.StatusCode != System.Net.HttpStatusCode.Conflict) + { + Utils.Log(response.Content.ToString()); + return new SetCallResponse() { success = false, message = response.Content.ToString() }; + } + Utils.Log($"Failure to remove the conflict. Sharecode: {payload.sharecode} | {response.Content}"); + return new SetCallResponse() { success = false, message = response.Content.ToString() }; } - return JsonConvert.DeserializeObject(response.Content); + return new SetCallResponse() { success = true, message = payload.sharecode }; } } diff --git a/ValorantCC/src/Community/SCGen.cs b/ValorantCC/src/Community/SCGen.cs new file mode 100644 index 0000000..9711c58 --- /dev/null +++ b/ValorantCC/src/Community/SCGen.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Numerics; + +namespace ValorantCC.src.Community +{ + public class SCGen + { + private static Random rand = new Random(); + public static string GenerateShareCode(string input) + { + input = input.Replace("-", String.Empty); + + return new string( + Enumerable.Repeat(input, 12) + .Select(s => s[rand.Next(s.Length)]) + .ToArray()).ToUpper(); + } + } +}