From 4addaf2407b4a7c487b7339d6fdad90f18ff04d6 Mon Sep 17 00:00:00 2001 From: Citrinate Date: Sun, 17 Mar 2024 13:35:26 -0400 Subject: [PATCH 1/9] Update ArchiSteamFarm to 6.0.1.2 --- ArchiSteamFarm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ArchiSteamFarm b/ArchiSteamFarm index e4c20df..0d65f51 160000 --- a/ArchiSteamFarm +++ b/ArchiSteamFarm @@ -1 +1 @@ -Subproject commit e4c20df4a896209636078c7acf33fbcaa8ad35bc +Subproject commit 0d65f5174b0ed77892f481be53794e7929d055d5 From 42e94c42894f08c03332f6fe9fc9fa44fa3458bb Mon Sep 17 00:00:00 2001 From: Citrinate Date: Sun, 17 Mar 2024 13:35:37 -0400 Subject: [PATCH 2/9] Remove ASFE features --- CS2Interface/AdapterBridge.cs | 42 ----------------------------------- CS2Interface/CS2Interface.cs | 9 -------- 2 files changed, 51 deletions(-) delete mode 100644 CS2Interface/AdapterBridge.cs diff --git a/CS2Interface/AdapterBridge.cs b/CS2Interface/AdapterBridge.cs deleted file mode 100644 index d80af9c..0000000 --- a/CS2Interface/AdapterBridge.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Reflection; -using ArchiSteamFarm.Core; -using ArchiSteamFarm.Steam; - -// ASFEnhanced Adapter https://github.com/chr233/ASFEnhanceAdapterDemoPlugin - -namespace CS2Interface; -internal static class AdapterBridge { - public static bool InitAdapter(string pluginName, string pluginId, string? cmdPrefix, string? repoName, MethodInfo? cmdHandler) { - try { - var adapterEndpoint = Assembly.Load("ASFEnhance").GetType("ASFEnhance._Adapter_.Endpoint"); - var registerModule = adapterEndpoint?.GetMethod("RegisterModule", BindingFlags.Static | BindingFlags.Public); - var pluinVersion = Assembly.GetExecutingAssembly().GetName().Version; - - if (registerModule != null && adapterEndpoint != null) { - var result = registerModule?.Invoke(null, new object?[] { pluginName, pluginId, cmdPrefix, repoName, pluinVersion, cmdHandler }); - - if (result is string str) { - if (str == pluginName) { - return true; - } else { - ASF.ArchiLogger.LogGenericWarning(str); - } - } - } - } catch (Exception) { - ASF.ArchiLogger.LogGenericDebug("Could not find ASFEnhance plugin"); - } - - return false; - } - - internal static string? Response(Bot bot, EAccess access, ulong steamID, string message, string[] args) { - // ASFEnhance wants to intercept commands meant for this plugin, for the purpose of it's DisabledCmds config setting. - // Seems buggy though: https://github.com/Citrinate/FreePackages/issues/28 - // Therefore I'm feeding it this dummy response function, as ASFEnhance requires that cmdHandler not be null. - // This disables DisabledCmds support, but should not effect PLUGINSUPDATE command support - - return null; - } -} diff --git a/CS2Interface/CS2Interface.cs b/CS2Interface/CS2Interface.cs index 319724d..6a12f2d 100644 --- a/CS2Interface/CS2Interface.cs +++ b/CS2Interface/CS2Interface.cs @@ -8,7 +8,6 @@ using SteamKit2; using System.Collections.Concurrent; using System.Text.Json; -using System.Reflection; namespace CS2Interface { [Export(typeof(IPlugin))] @@ -20,14 +19,6 @@ public sealed class CS2Interface : IASF, IBotModules, IBotSteamClient, IBotComma public Task OnLoaded() { ASF.ArchiLogger.LogGenericInfo("Counter-Strike 2 Interface ASF Plugin by Citrinate"); GameData.Update(); - - // ASFEnhanced Adapter https://github.com/chr233/ASFEnhanceAdapterDemoPlugin - var flag = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; - var handler = typeof(AdapterBridge).GetMethod(nameof(AdapterBridge.Response), flag); - const string pluginId = nameof(CS2Interface); - const string cmdPrefix = "CS2INTERFACE"; - const string repoName = "Citrinate/CS2Interface"; - AdapterBridge.InitAdapter(Name, pluginId, cmdPrefix, repoName, handler); return Task.CompletedTask; } From 1a076734c57fc5323ea7e507632bda3e1f38c88f Mon Sep 17 00:00:00 2001 From: Citrinate Date: Sun, 17 Mar 2024 14:11:21 -0400 Subject: [PATCH 3/9] Add support for ASF plugin updates --- CS2Interface/CS2Interface.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CS2Interface/CS2Interface.cs b/CS2Interface/CS2Interface.cs index 6a12f2d..efdb2fd 100644 --- a/CS2Interface/CS2Interface.cs +++ b/CS2Interface/CS2Interface.cs @@ -11,9 +11,10 @@ namespace CS2Interface { [Export(typeof(IPlugin))] - public sealed class CS2Interface : IASF, IBotModules, IBotSteamClient, IBotCommand2, IBotConnection, IBotCardsFarmerInfo { + public sealed class CS2Interface : IASF, IBotModules, IBotSteamClient, IBotCommand2, IBotConnection, IBotCardsFarmerInfo, IGitHubPluginUpdates { internal static ConcurrentDictionary AutoStart = new(); public string Name => nameof(CS2Interface); + public string RepositoryName => "Citrinate/CS2Interface"; public Version Version => typeof(CS2Interface).Assembly.GetName().Version ?? new Version("0"); public Task OnLoaded() { From cc74b022587a766d2e02c538049e0463c684e00b Mon Sep 17 00:00:00 2001 From: Citrinate Date: Sun, 17 Mar 2024 17:26:23 -0400 Subject: [PATCH 4/9] Version bump --- CS2Interface/CS2Interface.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CS2Interface/CS2Interface.csproj b/CS2Interface/CS2Interface.csproj index a22cf21..94b0e8d 100644 --- a/CS2Interface/CS2Interface.csproj +++ b/CS2Interface/CS2Interface.csproj @@ -2,7 +2,7 @@ Citrinate - 1.0.9 + 1.0.10 enable latest net8.0 From ccd056ba30e361ca9132b0867535edee9ec1b51d Mon Sep 17 00:00:00 2001 From: Citrinate Date: Fri, 22 Mar 2024 15:43:45 -0400 Subject: [PATCH 5/9] Move translatable strings into a resource file --- CS2Interface/CS/Client.cs | 75 ++--- CS2Interface/CS/GameData.cs | 5 +- CS2Interface/CS2Interface.csproj | 13 + CS2Interface/Commands.cs | 13 +- CS2Interface/Data/GameDataItems.cs | 5 +- CS2Interface/Data/ItemData.cs | 3 +- CS2Interface/Handlers/ClientHandler.cs | 49 +-- .../IPC/Api/CS2InterfaceController.cs | 17 +- CS2Interface/Localization/Strings.resx | 300 ++++++++++++++++++ 9 files changed, 398 insertions(+), 82 deletions(-) create mode 100644 CS2Interface/Localization/Strings.resx diff --git a/CS2Interface/CS/Client.cs b/CS2Interface/CS/Client.cs index 7dc14d1..16e610c 100644 --- a/CS2Interface/CS/Client.cs +++ b/CS2Interface/CS/Client.cs @@ -6,6 +6,7 @@ using System.Threading; using System.Threading.Tasks; using ArchiSteamFarm.Steam; +using CS2Interface.Localization; using ProtoBuf; using SteamKit2; using SteamKit2.GC; @@ -38,17 +39,17 @@ internal async Task Run() { } if (ConnectionSemaphore.CurrentCount != 1) { - throw new ClientException(EClientExceptionType.Failed, "CS2 Client is already attempting to run, please wait"); + throw new ClientException(EClientExceptionType.Failed, Strings.ClientAlreadyStarting); } if (FatalError) { - throw new ClientException(EClientExceptionType.FatalError, "CS2 Client experienced a fatal error"); + throw new ClientException(EClientExceptionType.FatalError, Strings.ClientFatalError); } await ConnectionSemaphore.WaitAsync().ConfigureAwait(false); try { if (!await GameData.IsLoaded(0).ConfigureAwait(false)) { - throw new ClientException(EClientExceptionType.Failed, "Failed to load Game Data"); + throw new ClientException(EClientExceptionType.Failed, Strings.GameDataLoadingFailed); } // TODO: Verify that this account owns CS2 @@ -69,10 +70,10 @@ internal async Task Run() { }}; var fetcher = new GCFetcher((uint) EGCBaseClientMsg.k_EMsgGCClientWelcome); - Bot.ArchiLogger.LogGenericDebug("Sending hello message"); + Bot.ArchiLogger.LogGenericDebug(Strings.SendingHello); if (fetcher.Fetch(this, msg, resendMsg: true) == null) { - throw new ClientException(EClientExceptionType.Timeout, "CS2 Client wasn't able to connect to GC"); + throw new ClientException(EClientExceptionType.Timeout, Strings.GCConnectionFailed); } HasGCSession = true; @@ -110,7 +111,7 @@ internal async Task VerifyConnection() { return false; } - Bot.ArchiLogger.LogGenericDebug("Verifying CS2 Client's connection to GC"); + Bot.ArchiLogger.LogGenericDebug(Strings.VerifyingGCConnection); try { await RequestPlayerProfile(Bot.SteamID).ConfigureAwait(false); } catch { @@ -126,7 +127,7 @@ private void OnGCMessage(SteamGameCoordinator.MessageCallback callback) { } #if DEBUG - Bot.ArchiLogger.LogGenericDebug(String.Format("Message Received: {0}", callback.EMsg)); + Bot.ArchiLogger.LogGenericDebug(String.Format("{0}: {1}", Strings.MessageRecieved, callback.EMsg)); #endif OnGCMessageRecieved?.Invoke(callback); @@ -167,7 +168,7 @@ private void OnClientWelcome(IPacketGCMsg packetMsg) { } })); - Bot.ArchiLogger.LogGenericDebug("CS2 inventory loaded"); + Bot.ArchiLogger.LogGenericDebug(Strings.InventoryLoaded); return; } @@ -177,7 +178,7 @@ private void OnClientWelcome(IPacketGCMsg packetMsg) { private void OnFatalLogonError(IPacketGCMsg packetMsg) { var msg = new ClientGCMsgProtobuf(packetMsg); - Bot.ArchiLogger.LogGenericError(String.Format("Fatal CS2 logon error {0}: {1}", msg.Body.errorcode, msg.Body.message)); + Bot.ArchiLogger.LogGenericError(String.Format("{0}: {1}", String.Format(Strings.FatalLogonError, msg.Body.errorcode), msg.Body.message)); FatalError = true; } @@ -253,7 +254,7 @@ private void OnMultiItemUpdated(IPacketGCMsg packetMsg) { internal async Task InspectItem(ulong param_s, ulong param_a, ulong param_d, ulong param_m) { if (!HasGCSession) { - throw new ClientException(EClientExceptionType.Failed, "CS2 Client is not connected to GC"); + throw new ClientException(EClientExceptionType.Failed, Strings.ClientNotConnectedToGC); } await GCSemaphore.WaitAsync().ConfigureAwait(false); @@ -271,11 +272,11 @@ internal async Task In VerifyFunc = response => response.Body.iteminfo.itemid == param_a }; - Bot.ArchiLogger.LogGenericDebug(String.Format("Inspecting item: s {0} a {1} d {2} m {3}", param_s, param_a, param_d, param_m)); + Bot.ArchiLogger.LogGenericDebug(String.Format("{0}: s {1} a {2} d {3} m {4}", Strings.InspectingItem, param_s, param_a, param_d, param_m)); var response = fetcher.Fetch(this, msg); if (response == null) { - throw new ClientException(EClientExceptionType.Timeout, "Request timed out"); + throw new ClientException(EClientExceptionType.Timeout, Strings.RequestTimeout); } return response.Body; @@ -286,12 +287,12 @@ internal async Task In internal async Task RequestPlayerProfile(ulong steam_id) { //uint account_id) { if (!HasGCSession) { - throw new ClientException(EClientExceptionType.Failed, "CS2 Client is not connected"); + throw new ClientException(EClientExceptionType.Failed, Strings.ClientNotConnectedToGC); } SteamID SteamID = new(steam_id); if (!SteamID.IsValid || SteamID.AccountUniverse != EUniverse.Public || SteamID.AccountType != EAccountType.Individual || SteamID.AccountInstance != SteamID.DesktopInstance) { - throw new ClientException(EClientExceptionType.BadRequest, "Invalid Steam ID"); + throw new ClientException(EClientExceptionType.BadRequest, Strings.InvalidSteamID); } await GCSemaphore.WaitAsync().ConfigureAwait(false); @@ -308,11 +309,11 @@ internal async Task RequestPlayerProfile(ulon VerifyFunc = response => response.Body.account_profiles.FirstOrDefault()?.account_id == account_id }; - Bot.ArchiLogger.LogGenericDebug(String.Format("Getting CS2 player profile: {0}", steam_id)); + Bot.ArchiLogger.LogGenericDebug(String.Format("{0}: {1}", Strings.InspectingPlayer, steam_id)); var response = fetcher.Fetch(this, msg); if (response == null) { - throw new ClientException(EClientExceptionType.Timeout, "Request timed out"); + throw new ClientException(EClientExceptionType.Timeout, Strings.RequestTimeout); } return response.Body; @@ -323,21 +324,21 @@ internal async Task RequestPlayerProfile(ulon internal async Task> GetCasketContents(ulong casket_id) { if (!HasGCSession) { - throw new ClientException(EClientExceptionType.Failed, "CS2 Client is not connected to GC"); + throw new ClientException(EClientExceptionType.Failed, Strings.ClientNotConnectedToGC); } if (Inventory == null) { - throw new ClientException(EClientExceptionType.Failed, "Inventory not loaded yet"); + throw new ClientException(EClientExceptionType.Failed, Strings.InventoryNotLoaded); } InventoryItem? casket = Inventory.Values.FirstOrDefault(x => x.ItemInfo.id == casket_id); if (casket == null) { - throw new ClientException(EClientExceptionType.BadRequest, "Storage unit not found in inventory"); + throw new ClientException(EClientExceptionType.BadRequest, Strings.CasketNotFound); } uint? items_count = casket.GetAttribute("items count")?.ToUInt32(); if (items_count == null) { - throw new ClientException(EClientExceptionType.Failed, "Could not determine storage unit item count"); + throw new ClientException(EClientExceptionType.Failed, Strings.CasketContentsUndefined); } if (items_count == 0) { @@ -362,21 +363,21 @@ internal async Task> GetCasketContents(ulong casket_id) { VerifyFunc = response => response.Body.item_id.FirstOrDefault() == casket_id && response.Body.request == (uint) EGCItemCustomizationNotification.k_EGCItemCustomizationNotification_CasketContents }; - Bot.ArchiLogger.LogGenericDebug(String.Format("Opening casket {0}", casket_id)); + Bot.ArchiLogger.LogGenericDebug(String.Format(Strings.OpeningCasket, casket_id)); if (fetcher.Fetch(this, msg) == null) { - throw new ClientException(EClientExceptionType.Timeout, "Request timed out"); + throw new ClientException(EClientExceptionType.Timeout, Strings.RequestTimeout); } // Casket contents can sometimes continue to come in after we've recieved the k_EMsgGCItemCustomizationNotification response DateTime waitTime = DateTime.Now.AddSeconds(30); while (Inventory.Values.Where(x => x.CasketID == casket_id).Count() != items_count && DateTime.Now < waitTime) { - Bot.ArchiLogger.LogGenericDebug(String.Format("Waiting for casket {0} items", casket_id)); + Bot.ArchiLogger.LogGenericDebug(String.Format(Strings.CasketContentsLoading, casket_id)); await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false); } if (Inventory.Values.Where(x => x.CasketID == casket_id).Count() != items_count) { - throw new ClientException(EClientExceptionType.Failed, String.Format("Casket item count mismatch, casket should have {0} items but only found {1}", items_count, Inventory.Values.Where(x => x.CasketID == casket_id).Count())); + throw new ClientException(EClientExceptionType.Failed, String.Format(Strings.CasketItemCountMismatch, items_count, Inventory.Values.Where(x => x.CasketID == casket_id).Count())); } return Inventory.Values.Where(x => x.CasketID == casket_id).ToList(); @@ -387,27 +388,27 @@ internal async Task> GetCasketContents(ulong casket_id) { internal async Task AddItemToCasket(ulong casket_id, ulong item_id) { if (!HasGCSession) { - throw new ClientException(EClientExceptionType.Failed, "CS2 Client is not connected to GC"); + throw new ClientException(EClientExceptionType.Failed, Strings.ClientNotConnectedToGC); } if (Inventory == null) { - throw new ClientException(EClientExceptionType.Failed, "Inventory not loaded yet"); + throw new ClientException(EClientExceptionType.Failed, Strings.InventoryNotLoaded); } if (Inventory.Values.FirstOrDefault(x => x.ItemInfo.id == item_id) == null) { - throw new ClientException(EClientExceptionType.BadRequest, "Item not found in inventory"); + throw new ClientException(EClientExceptionType.BadRequest, Strings.InventoryItemNotFound); } InventoryItem? casket = Inventory.Values.FirstOrDefault(x => x.ItemInfo.id == casket_id); if (casket == null) { - throw new ClientException(EClientExceptionType.BadRequest, "Storage unit not found in inventory"); + throw new ClientException(EClientExceptionType.BadRequest, Strings.CasketNotFound); } uint? items_count = casket.GetAttribute("items count")?.ToUInt32(); if (items_count == null) { - throw new ClientException(EClientExceptionType.Failed, "Could not determine storage unit item count"); + throw new ClientException(EClientExceptionType.Failed, Strings.CasketContentsUndefined); } else if (items_count == 1000) { - throw new ClientException(EClientExceptionType.BadRequest, "Storage unit is full"); + throw new ClientException(EClientExceptionType.BadRequest, Strings.CasketFull); } await GCSemaphore.WaitAsync().ConfigureAwait(false); @@ -437,10 +438,10 @@ internal async Task AddItemToCasket(ulong casket_id, ulong item_id) { } }; - Bot.ArchiLogger.LogGenericDebug(String.Format("Adding item {0} to casket {1}", item_id, casket_id)); + Bot.ArchiLogger.LogGenericDebug(String.Format(Strings.AddingItemToCasket, item_id, casket_id)); if (fetcher.Fetch(this, msg) == null) { - throw new ClientException(EClientExceptionType.Timeout, "Request timed out"); + throw new ClientException(EClientExceptionType.Timeout, Strings.RequestTimeout); } return true; @@ -451,16 +452,16 @@ internal async Task AddItemToCasket(ulong casket_id, ulong item_id) { internal async Task RemoveItemFromCasket(ulong casket_id, ulong item_id) { if (!HasGCSession) { - throw new ClientException(EClientExceptionType.Failed, "CS2 Client is not connected to GC"); + throw new ClientException(EClientExceptionType.Failed, Strings.ClientNotConnectedToGC); } if (Inventory == null) { - throw new ClientException(EClientExceptionType.Failed, "Inventory not loaded yet"); + throw new ClientException(EClientExceptionType.Failed, Strings.InventoryNotLoaded); } InventoryItem? casket = Inventory.Values.FirstOrDefault(x => x.ItemInfo.id == casket_id); if (casket == null) { - throw new ClientException(EClientExceptionType.BadRequest, "Storage unit not found in inventory"); + throw new ClientException(EClientExceptionType.BadRequest, Strings.CasketNotFound); } // Does not verify that the item is actually in the crate, to do that we would need to request the crate contents first @@ -492,10 +493,10 @@ internal async Task RemoveItemFromCasket(ulong casket_id, ulong item_id) { } }; - Bot.ArchiLogger.LogGenericDebug(String.Format("Removing item {0} from casket {1}", item_id, casket_id)); + Bot.ArchiLogger.LogGenericDebug(String.Format(Strings.RemovingItemFromCasket, item_id, casket_id)); if (fetcher.Fetch(this, msg) == null) { - throw new ClientException(EClientExceptionType.Timeout, "Request timed out"); + throw new ClientException(EClientExceptionType.Timeout, Strings.RequestTimeout); } return true; diff --git a/CS2Interface/CS/GameData.cs b/CS2Interface/CS/GameData.cs index da55cfd..2e844d7 100644 --- a/CS2Interface/CS/GameData.cs +++ b/CS2Interface/CS/GameData.cs @@ -3,6 +3,7 @@ using System.Threading; using System.Threading.Tasks; using ArchiSteamFarm.Core; +using CS2Interface.Localization; namespace CS2Interface { internal static class GameData { @@ -21,7 +22,7 @@ internal static void Update(bool forceUpdate = false) { ItemsGameCdn.Updated = false; CsgoEnglish.Updated = false; DoNotUpdateUntil = DateTime.Now.AddMinutes(15); - ASF.ArchiLogger.LogGenericInfo("Refreshing CS2 game data"); + ASF.ArchiLogger.LogGenericInfo(Strings.GameDataRefreshing); } if (!ItemsGame.Updated || !ItemsGameCdn.Updated || !CsgoEnglish.Updated) { @@ -72,7 +73,7 @@ private static async Task DoUpdate() { if (ItemsGame.Updated && ItemsGameCdn.Updated && CsgoEnglish.Updated) { UpdateTimer.Change(Timeout.Infinite, Timeout.Infinite); IsUpdating = false; - ASF.ArchiLogger.LogGenericInfo("CS2 game data loaded"); + ASF.ArchiLogger.LogGenericInfo(Strings.GameDataLoadingSuccess); } } catch (Exception e) { ASF.ArchiLogger.LogGenericException(e); diff --git a/CS2Interface/CS2Interface.csproj b/CS2Interface/CS2Interface.csproj index 94b0e8d..04a11e7 100644 --- a/CS2Interface/CS2Interface.csproj +++ b/CS2Interface/CS2Interface.csproj @@ -7,6 +7,7 @@ latest net8.0 true + PrepareResources;$(CompileDependsOn) @@ -17,4 +18,16 @@ + + + + MSBuild:Compile + Strings.Designer.cs + $(IntermediateOutputPath)\Strings.Designer.cs + CSharp + CS2Interface.Localization + Strings + + + diff --git a/CS2Interface/Commands.cs b/CS2Interface/Commands.cs index dff0efc..46e7ec0 100644 --- a/CS2Interface/Commands.cs +++ b/CS2Interface/Commands.cs @@ -4,7 +4,6 @@ using System.ComponentModel; using ArchiSteamFarm.Core; using System.Collections.Generic; -using ArchiSteamFarm.Localization; using System.Linq; namespace CS2Interface { @@ -61,7 +60,7 @@ internal static class Commands { } if (!bot.IsConnectedAndLoggedOn) { - return FormatBotResponse(bot, Strings.BotNotConnected); + return FormatBotResponse(bot, ArchiSteamFarm.Localization.Strings.BotNotConnected); } (_, string message) = await ClientHandler.ClientHandlers[bot.BotName].Run().ConfigureAwait(false); @@ -77,7 +76,7 @@ internal static class Commands { HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return access >= EAccess.Owner ? FormatStaticResponse(String.Format(Strings.BotNotFound, botNames)) : null; + return access >= EAccess.Owner ? FormatStaticResponse(String.Format(ArchiSteamFarm.Localization.Strings.BotNotFound, botNames)) : null; } IList results = await Utilities.InParallel(bots.Select(bot => ResponseRun(bot, ArchiSteamFarm.Steam.Interaction.Commands.GetProxyAccess(bot, access, steamID)))).ConfigureAwait(false); @@ -93,7 +92,7 @@ internal static class Commands { } if (!bot.IsConnectedAndLoggedOn) { - return FormatBotResponse(bot, Strings.BotNotConnected); + return FormatBotResponse(bot, ArchiSteamFarm.Localization.Strings.BotNotConnected); } string message = ClientHandler.ClientHandlers[bot.BotName].Stop(); @@ -109,7 +108,7 @@ internal static class Commands { HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return access >= EAccess.Owner ? FormatStaticResponse(String.Format(Strings.BotNotFound, botNames)) : null; + return access >= EAccess.Owner ? FormatStaticResponse(String.Format(ArchiSteamFarm.Localization.Strings.BotNotFound, botNames)) : null; } IEnumerable results = bots.Select(bot => ResponseStop(bot, ArchiSteamFarm.Steam.Interaction.Commands.GetProxyAccess(bot, access, steamID))); @@ -125,7 +124,7 @@ internal static class Commands { } if (!bot.IsConnectedAndLoggedOn) { - return FormatBotResponse(bot, Strings.BotNotConnected); + return FormatBotResponse(bot, ArchiSteamFarm.Localization.Strings.BotNotConnected); } (_, string message) = ClientHandler.ClientHandlers[bot.BotName].Status(); @@ -141,7 +140,7 @@ internal static class Commands { HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return access >= EAccess.Owner ? FormatStaticResponse(String.Format(Strings.BotNotFound, botNames)) : null; + return access >= EAccess.Owner ? FormatStaticResponse(String.Format(ArchiSteamFarm.Localization.Strings.BotNotFound, botNames)) : null; } IEnumerable results = bots.Select(bot => ResponseStatus(bot, ArchiSteamFarm.Steam.Interaction.Commands.GetProxyAccess(bot, access, steamID))); diff --git a/CS2Interface/Data/GameDataItems.cs b/CS2Interface/Data/GameDataItems.cs index a8f5211..cc2fbee 100644 --- a/CS2Interface/Data/GameDataItems.cs +++ b/CS2Interface/Data/GameDataItems.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Threading.Tasks; using ArchiSteamFarm.Core; +using CS2Interface.Localization; using SteamKit2; namespace CS2Interface { @@ -14,7 +15,7 @@ internal GameDataItems(string url) : base(url) {} internal async Task Update() { KeyValue? data = await FetchKVResource().ConfigureAwait(false); if (data == null) { - ASF.ArchiLogger.LogGenericError(String.Format("Couldn't load game data from: {0}", Url)); + ASF.ArchiLogger.LogGenericError(String.Format(Strings.GameDataSourceFailed, Url)); return false; } @@ -44,7 +45,7 @@ internal List? this[string? key] { if (def == null) { if (!suppressErrorLogs) { - ASF.ArchiLogger.LogGenericError(String.Format("Couldn't find definition: {0}[{1}]", value, index)); + ASF.ArchiLogger.LogGenericError(String.Format("{0}: {1}[{2}]", Strings.GameDataDefinitionUndefined, value, index)); } return null; diff --git a/CS2Interface/Data/ItemData.cs b/CS2Interface/Data/ItemData.cs index 9dfa04a..0e1de19 100644 --- a/CS2Interface/Data/ItemData.cs +++ b/CS2Interface/Data/ItemData.cs @@ -1,6 +1,7 @@ using System; using System.Text.Json.Serialization; using ArchiSteamFarm.Core; +using CS2Interface.Localization; using SteamKit2; namespace CS2Interface { @@ -73,7 +74,7 @@ private bool MergePrefab(ItemDef itemDef, string? prefab) { } if (!foundValid) { - ASF.ArchiLogger.LogGenericError(String.Format("Couldn't find definition: prefabs[{0}]", prefab)); + ASF.ArchiLogger.LogGenericError(String.Format("{0}: prefabs[{1}]", Strings.GameDataDefinitionUndefined, prefab)); } return foundValid; diff --git a/CS2Interface/Handlers/ClientHandler.cs b/CS2Interface/Handlers/ClientHandler.cs index f67bfe2..bd80f41 100644 --- a/CS2Interface/Handlers/ClientHandler.cs +++ b/CS2Interface/Handlers/ClientHandler.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using ArchiSteamFarm.Steam; +using CS2Interface.Localization; using SteamKit2; namespace CS2Interface { @@ -26,7 +27,7 @@ internal static void AddHandler(Bot bot, CallbackManager callbackManager) { internal async Task<(bool Success, string Message)> Run(int numAttempts = 3) { if (!Bot.IsConnectedAndLoggedOn) { - return (false, "Bot is not connected"); + return (false, ArchiSteamFarm.Localization.Strings.BotNotConnected); } (_, _, EClientStatus status) = await VerifyConnection().ConfigureAwait(false); @@ -34,55 +35,55 @@ internal static void AddHandler(Bot bot, CallbackManager callbackManager) { bool ready = ((status & EClientStatus.Ready) == EClientStatus.Ready); if (connected) { - return (true, "CS2 Interface is already running"); + return (true, Strings.InterfaceAlreadyRunning); } if (!connected && !ready) { - return (false, "CS2 Interface is already attempting to run"); + return (false, Strings.InterfaceAlreadyStarting); } try { await Client.Run().ConfigureAwait(false); if (!await Client.VerifyConnection().ConfigureAwait(false)) { - throw new ClientException(EClientExceptionType.Failed, "CS2 Interface seemed to start, but then didn't"); + throw new ClientException(EClientExceptionType.Failed, Strings.InterfaceStartFailedUnexpectedly); } } catch (ClientException e) { Bot.ArchiLogger.LogGenericError(e.Message); if (numAttempts > 0 && e.Type != EClientExceptionType.FatalError) { await Task.Delay(TimeSpan.FromSeconds(10)).ConfigureAwait(false); - Bot.ArchiLogger.LogGenericError("CS2 Interface failed to start, retrying"); + Bot.ArchiLogger.LogGenericError(Strings.InterfaceStartFailedRetry); return await Run(numAttempts - 1).ConfigureAwait(false); } ForceStop(); Bot.Actions.Resume(); - Bot.ArchiLogger.LogGenericError("CS2 Interface failed to start"); + Bot.ArchiLogger.LogGenericError(Strings.InterfaceStartFailed); - return (false, String.Format("CS2 Interface failed to start: {0}", e.Message)); + return (false, String.Format("{0}: {1}", Strings.InterfaceStartFailed, e.Message)); } - Bot.ArchiLogger.LogGenericInfo("CS2 Interface started"); + Bot.ArchiLogger.LogGenericInfo(Strings.InterfaceStarted); - return (true, "CS2 Interface started"); + return (true, Strings.InterfaceStarted); } internal string Stop() { if (!Bot.IsConnectedAndLoggedOn) { - return "Bot is not connected"; + return ArchiSteamFarm.Localization.Strings.BotNotConnected; } EClientStatus status = Client.Status(); bool connected = ((status & EClientStatus.Connected) == EClientStatus.Connected); if (!connected) { - return "CS2 Interface was not running"; + return Strings.IntefaceNotRunning; } Client.Stop(); Bot.Actions.Resume(); - Bot.ArchiLogger.LogGenericInfo("CS2 Interface stopped"); + Bot.ArchiLogger.LogGenericInfo(Strings.InterfaceStopped); - return "CS2 Interface successfully stopped"; + return Strings.InterfaceStoppedSuccessfully; } internal void ForceStop() { @@ -91,13 +92,13 @@ internal void ForceStop() { if (connected) { Client.Stop(); // Stop even if bot is logged out, to update the client state Bot.Actions.Resume(); - Bot.ArchiLogger.LogGenericInfo("CS2 Interface was forcibly stopped"); + Bot.ArchiLogger.LogGenericInfo(Strings.InterfaceForciblyStopped); } } internal (EClientStatus ClientStatus, string Message) Status() { if (!Bot.IsConnectedAndLoggedOn) { - return (EClientStatus.None, "Bot is not connected"); + return (EClientStatus.None, ArchiSteamFarm.Localization.Strings.BotNotConnected); } EClientStatus status = Client.Status(); @@ -106,17 +107,17 @@ internal void ForceStop() { if (!connected) { if (!ready) { - return (status, "CS2 Interface is connecting"); + return (status, Strings.InterfaceConnecting); } - return (status, "CS2 Interface is not connected"); + return (status, Strings.InterfaceNotConnected); } if (!ready) { - return (status, "CS2 Interface is busy"); + return (status, Strings.InterfaceBusy); } - return (status, "Ready"); + return (status, Strings.Ready); } internal async Task<(bool Connected, string Message, EClientStatus ClientStatus)> VerifyConnection() { @@ -124,18 +125,18 @@ internal void ForceStop() { bool connected = ((status & EClientStatus.Connected) == EClientStatus.Connected); if (!connected) { - return (false, "CS2 Interface is not connected", status); + return (false, Strings.InterfaceNotConnected, status); } connected = await Client.VerifyConnection().ConfigureAwait(false); if (!connected) { ForceStop(); - Bot.ArchiLogger.LogGenericError("CS2 Interface stopped unexpectedly"); + Bot.ArchiLogger.LogGenericError(Strings.InterfaceStoppedUnexpectedly); - return (false, "CS2 Interface stopped unexpectedly", Client.Status()); + return (false, Strings.InterfaceStoppedUnexpectedly, Client.Status()); } - return (true, "CS2 Interface is connected", Client.Status()); + return (true, Strings.InterfaceConnected, Client.Status()); } internal static (Bot?, Client?, string) GetAvailableClient(HashSet bots) { @@ -146,7 +147,7 @@ internal static (Bot?, Client?, string) GetAvailableClient(HashSet bots) { } } - return (null, null, "No bots are available"); + return (null, null, Strings.NoBotsAvailable); } internal (Client?, string) GetClient(EClientStatus desiredStatus = EClientStatus.Connected | EClientStatus.Ready) { diff --git a/CS2Interface/IPC/Api/CS2InterfaceController.cs b/CS2Interface/IPC/Api/CS2InterfaceController.cs index b1e3d47..da0a799 100644 --- a/CS2Interface/IPC/Api/CS2InterfaceController.cs +++ b/CS2Interface/IPC/Api/CS2InterfaceController.cs @@ -7,7 +7,6 @@ using ArchiSteamFarm.Core; using ArchiSteamFarm.IPC.Controllers.Api; using ArchiSteamFarm.IPC.Responses; -using ArchiSteamFarm.Localization; using ArchiSteamFarm.Steam; using Microsoft.AspNetCore.Mvc; using SteamKit2.GC.CSGO.Internal; @@ -28,7 +27,7 @@ public async Task> Start([FromRoute] string botNam HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return BadRequest(new GenericResponse(false, string.Format(Strings.BotNotFound, botNames))); + return BadRequest(new GenericResponse(false, string.Format(ArchiSteamFarm.Localization.Strings.BotNotFound, botNames))); } IList<(bool Success, string Message)> results = await Utilities.InParallel(bots.Select(static bot => ClientHandler.ClientHandlers[bot.BotName].Run())).ConfigureAwait(false); @@ -48,7 +47,7 @@ public ActionResult Stop([FromRoute] string botNames) { HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return BadRequest(new GenericResponse(false, string.Format(Strings.BotNotFound, botNames))); + return BadRequest(new GenericResponse(false, string.Format(ArchiSteamFarm.Localization.Strings.BotNotFound, botNames))); } IEnumerable results = bots.Select(static bot => ClientHandler.ClientHandlers[bot.BotName].Stop()); @@ -92,7 +91,7 @@ public async Task> InspectItem( HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return BadRequest(new GenericResponse(false, string.Format(Strings.BotNotFound, botNames))); + return BadRequest(new GenericResponse(false, string.Format(ArchiSteamFarm.Localization.Strings.BotNotFound, botNames))); } (Bot? bot, Client? client, string status) = ClientHandler.GetAvailableClient(bots); @@ -148,7 +147,7 @@ public async Task> PlayerProfile([FromRoute] strin Bot? bot = Bot.GetBot(botName); if (bot == null) { - return BadRequest(new GenericResponse(false, string.Format(Strings.BotNotFound, botName))); + return BadRequest(new GenericResponse(false, string.Format(ArchiSteamFarm.Localization.Strings.BotNotFound, botName))); } (Client? client, string client_status) = ClientHandler.ClientHandlers[bot.BotName].GetClient(); @@ -186,7 +185,7 @@ public ActionResult Inventory( Bot? bot = Bot.GetBot(botName); if (bot == null) { - return BadRequest(new GenericResponse(false, string.Format(Strings.BotNotFound, botName))); + return BadRequest(new GenericResponse(false, string.Format(ArchiSteamFarm.Localization.Strings.BotNotFound, botName))); } (Client? client, string status) = ClientHandler.ClientHandlers[bot.BotName].GetClient(EClientStatus.Connected); @@ -227,7 +226,7 @@ public async Task> GetCrateContents( Bot? bot = Bot.GetBot(botName); if (bot == null) { - return BadRequest(new GenericResponse(false, string.Format(Strings.BotNotFound, botName))); + return BadRequest(new GenericResponse(false, string.Format(ArchiSteamFarm.Localization.Strings.BotNotFound, botName))); } (Client? client, string client_status) = ClientHandler.ClientHandlers[bot.BotName].GetClient(); @@ -259,7 +258,7 @@ public async Task> StoreItem([FromRoute] string bo Bot? bot = Bot.GetBot(botName); if (bot == null) { - return BadRequest(new GenericResponse(false, string.Format(Strings.BotNotFound, botName))); + return BadRequest(new GenericResponse(false, string.Format(ArchiSteamFarm.Localization.Strings.BotNotFound, botName))); } (Client? client, string client_status) = ClientHandler.ClientHandlers[bot.BotName].GetClient(); @@ -288,7 +287,7 @@ public async Task> RetrieveItem([FromRoute] string Bot? bot = Bot.GetBot(botName); if (bot == null) { - return BadRequest(new GenericResponse(false, string.Format(Strings.BotNotFound, botName))); + return BadRequest(new GenericResponse(false, string.Format(ArchiSteamFarm.Localization.Strings.BotNotFound, botName))); } (Client? client, string client_status) = ClientHandler.ClientHandlers[bot.BotName].GetClient(); diff --git a/CS2Interface/Localization/Strings.resx b/CS2Interface/Localization/Strings.resx new file mode 100644 index 0000000..3f26e89 --- /dev/null +++ b/CS2Interface/Localization/Strings.resx @@ -0,0 +1,300 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + CS2 Client is already attempting to run, please wait + + + + CS2 Client experienced a fatal error + + + + Failed to load Game Data + + + + Sending hello message + + + + CS2 Client wasn't able to connect to GC + + + + Verifying CS2 Client's connection to GC + + + + Message Received + + + + CS2 inventory loaded + + + + Fatal CS2 logon error {0} + {0} will be replaced by a number + + + CS2 Client is not connected to GC + + + + Inspecting item + + + + Request timed out + + + + Invalid Steam ID + + + + Getting CS2 player profile + + + + Inventory not loaded yet + + + + Storage unit not found in inventory + + + + Could not determine storage unit item count + + + + Opening storage unit {0} + {0} will be replaced by a number + + + Waiting for storage unit {0} items + {0} will be replaced by a number + + + Storage unit item count mismatch, storage unit should have {0} items but only found {1} + {0} will be replaced by a number, {1} will be replaced by a number + + + Item not found in inventory + + + + Storage unit is full + + + + Adding item {0} to storage unit {1} + {0} will be replaced by a number, {1} will be replaced by a number + + + Removing item {0} from storage unit {1} + {0} will be replaced by a number, {1} will be replaced by a number + + + CS2 game data loaded + + + + Refreshing CS2 game data + + + + Couldn't load game data from: {0} + {0} will be replaced by a url + + + Couldn't find definition + + + + CS2 Interface is already running + + + + CS2 Interface is already attempting to run + + + + CS2 Interface seemed to start, but then didn't + + + + CS2 Interface failed to start, retrying + + + + CS2 Interface failed to start + + + + CS2 Interface started + + + + CS2 Interface was not running + + + + CS2 Interface stopped + + + + CS2 Interface successfully stopped + + + + CS2 Interface was forcibly stopped + + + + CS2 Interface is connecting + + + + CS2 Interface is not connected + + + + CS2 Interface is busy + + + + Ready + + + + CS2 Interface stopped unexpectedly + + + + CS2 Interface is connected + + + + No bots are available + + + \ No newline at end of file From d6061d14e3d1980e56c4957b90512d24546bf249 Mon Sep 17 00:00:00 2001 From: Citrinate Date: Sat, 30 Mar 2024 13:47:01 -0400 Subject: [PATCH 6/9] Disable auto-start after a stop command is issued --- CS2Interface/Handlers/ClientHandler.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CS2Interface/Handlers/ClientHandler.cs b/CS2Interface/Handlers/ClientHandler.cs index bd80f41..aa2107f 100644 --- a/CS2Interface/Handlers/ClientHandler.cs +++ b/CS2Interface/Handlers/ClientHandler.cs @@ -69,6 +69,8 @@ internal static void AddHandler(Bot bot, CallbackManager callbackManager) { } internal string Stop() { + // The user has decided to stop the interface + if (!Bot.IsConnectedAndLoggedOn) { return ArchiSteamFarm.Localization.Strings.BotNotConnected; } @@ -80,6 +82,7 @@ internal string Stop() { } Client.Stop(); + CS2Interface.AutoStart[Bot.BotName] = false; Bot.Actions.Resume(); Bot.ArchiLogger.LogGenericInfo(Strings.InterfaceStopped); @@ -87,6 +90,8 @@ internal string Stop() { } internal void ForceStop() { + // The interface has decided to stop itself + EClientStatus status = Client.Status(); bool connected = ((status & EClientStatus.Connected) == EClientStatus.Connected); if (connected) { From 138834fe99fc7b0953a577df63e7089a7303c7f2 Mon Sep 17 00:00:00 2001 From: Citrinate Date: Sat, 30 Mar 2024 15:04:16 -0400 Subject: [PATCH 7/9] Always try to autostart after farming stopped --- CS2Interface/CS2Interface.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/CS2Interface/CS2Interface.cs b/CS2Interface/CS2Interface.cs index efdb2fd..10306c9 100644 --- a/CS2Interface/CS2Interface.cs +++ b/CS2Interface/CS2Interface.cs @@ -89,9 +89,7 @@ public async Task OnBotLoggedOn(Bot bot) { } public async Task OnBotFarmingFinished(Bot bot, bool farmedSomething) { - if (farmedSomething) { - await TryAutoStart(bot).ConfigureAwait(false); - } + await TryAutoStart(bot).ConfigureAwait(false); } public Task OnBotFarmingStarted(Bot bot) { @@ -100,10 +98,8 @@ public Task OnBotFarmingStarted(Bot bot) { return Task.FromResult(0); } - public Task OnBotFarmingStopped(Bot bot) { - ClientHandler.ClientHandlers[bot.BotName].ForceStop(); - - return Task.FromResult(0); + public async Task OnBotFarmingStopped(Bot bot) { + await TryAutoStart(bot).ConfigureAwait(false); } } } From 37d69212a17ba198130cb1f5e70a8c5cd434b78d Mon Sep 17 00:00:00 2001 From: Citrinate Date: Tue, 2 Apr 2024 15:54:03 -0400 Subject: [PATCH 8/9] Moving more strings --- CS2Interface/CS/GameData.cs | 50 ++++++------- CS2Interface/Localization/Strings.resx | 100 +++++++++++++++++++++++++ 2 files changed, 125 insertions(+), 25 deletions(-) diff --git a/CS2Interface/CS/GameData.cs b/CS2Interface/CS/GameData.cs index 2e844d7..7d7f020 100644 --- a/CS2Interface/CS/GameData.cs +++ b/CS2Interface/CS/GameData.cs @@ -95,31 +95,31 @@ private static async Task DoUpdate() { internal static string? GetOriginName(uint origin) { // https://raw.githubusercontent.com/SteamDatabase/SteamTracking/b5cba7a22ab899d6d423380cff21cec707b7c947/ItemSchema/CounterStrikeGlobalOffensive.json return origin switch { - 0 => "Timed Drop", - 1 => "Achievement", - 2 => "Purchased", - 3 => "Traded", - 4 => "Crafted", - 5 => "Store Promotion", - 6 => "Gifted", - 7 => "Support Granted", - 8 => "Found in Crate", - 9 => "Earned", - 10 => "Third-Party Promotion", - 11 => "Wrapped Gift", - 12 => "Halloween Drop", - 13 => "Steam Purchase", - 14 => "Foreign Item", - 15 => "CD Key", - 16 => "Collection Reward", - 17 => "Preview Item", - 18 => "Steam Workshop Contribution", - 19 => "Periodic Score Reward", - 20 => "Recycling", - 21 => "Tournament Drop", - 22 => "Stock Item", - 23 => "Quest Reward", - 24 => "Level Up Reward", + 0 => Strings.ItemOrigin0, // Timed Drop + 1 => Strings.ItemOrigin1, // Achievement + 2 => Strings.ItemOrigin2, // Purchased + 3 => Strings.ItemOrigin3, // Traded + 4 => Strings.ItemOrigin4, // Crafted + 5 => Strings.ItemOrigin5, // Store Promotion + 6 => Strings.ItemOrigin6, // Gifted + 7 => Strings.ItemOrigin7, // Support Granted + 8 => Strings.ItemOrigin8, // Found in Crate + 9 => Strings.ItemOrigin9, // Earned + 10 => Strings.ItemOrigin10, // "Third-Party Promotion + 11 => Strings.ItemOrigin11, // "Wrapped Gift + 12 => Strings.ItemOrigin12, // "Halloween Drop + 13 => Strings.ItemOrigin13, // "Steam Purchase + 14 => Strings.ItemOrigin14, // "Foreign Item + 15 => Strings.ItemOrigin15, // "CD Key + 16 => Strings.ItemOrigin16, // "Collection Reward + 17 => Strings.ItemOrigin17, // "Preview Item + 18 => Strings.ItemOrigin18, // "Steam Workshop Contribution + 19 => Strings.ItemOrigin19, // "Periodic Score Reward + 20 => Strings.ItemOrigin20, // "Recycling + 21 => Strings.ItemOrigin21, // "Tournament Drop + 22 => Strings.ItemOrigin22, // "Stock Item + 23 => Strings.ItemOrigin23, // "Quest Reward + 24 => Strings.ItemOrigin24, // "Level Up Reward _ => null }; } diff --git a/CS2Interface/Localization/Strings.resx b/CS2Interface/Localization/Strings.resx index 3f26e89..646ceaa 100644 --- a/CS2Interface/Localization/Strings.resx +++ b/CS2Interface/Localization/Strings.resx @@ -297,4 +297,104 @@ No bots are available + + Timed Drop + + + + Achievement + + + + Purchased + + + + Traded + + + + Crafted + + + + Store Promotion + + + + Gifted + + + + Support Granted + + + + Found in Crate + + + + Earned + + + + Third-Party Promotion + + + + Wrapped Gift + + + + Halloween Drop + + + + Steam Purchase + + + + Foreign Item + + + + CD Key + + + + Collection Reward + + + + Preview Item + + + + Steam Workshop Contribution + + + + Periodic Score Reward + + + + Recycling + + + + Tournament Drop + + + + Stock Item + + + + Quest Reward + + + + Level Up Reward + + \ No newline at end of file From 1eab5a5d2552ec52bc2ba2644a82b18dc8e910db Mon Sep 17 00:00:00 2001 From: Citrinate Date: Tue, 2 Apr 2024 16:15:51 -0400 Subject: [PATCH 9/9] Update ArchiSteamFarm to 6.0.1.23 --- ArchiSteamFarm | 2 +- CS2Interface/CS2Interface.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ArchiSteamFarm b/ArchiSteamFarm index 0d65f51..e02e597 160000 --- a/ArchiSteamFarm +++ b/ArchiSteamFarm @@ -1 +1 @@ -Subproject commit 0d65f5174b0ed77892f481be53794e7929d055d5 +Subproject commit e02e59710208a5b0cded24e019f2162fe0649997 diff --git a/CS2Interface/CS2Interface.csproj b/CS2Interface/CS2Interface.csproj index 04a11e7..ff1f020 100644 --- a/CS2Interface/CS2Interface.csproj +++ b/CS2Interface/CS2Interface.csproj @@ -2,7 +2,7 @@ Citrinate - 1.0.10 + 1.0.10.0 enable latest net8.0