diff --git a/ArchiSteamFarm/ArchiSteamFarm.csproj b/ArchiSteamFarm/ArchiSteamFarm.csproj index 2d8670907..ef5025374 100644 --- a/ArchiSteamFarm/ArchiSteamFarm.csproj +++ b/ArchiSteamFarm/ArchiSteamFarm.csproj @@ -125,9 +125,12 @@ + + + diff --git a/ArchiSteamFarm/Bot.cs b/ArchiSteamFarm/Bot.cs index 7a393d68f..19ffbb4cd 100755 --- a/ArchiSteamFarm/Bot.cs +++ b/ArchiSteamFarm/Bot.cs @@ -35,6 +35,7 @@ using System.Threading.Tasks; using System.Text; using System.Text.RegularExpressions; using ArchiSteamFarm.JSON; +using SteamKit2.Discovery; namespace ArchiSteamFarm { internal sealed class Bot : IDisposable { @@ -76,24 +77,14 @@ namespace ArchiSteamFarm { private bool FirstTradeSent, InvalidPassword, SkipFirstShutdown; private string AuthCode, TwoFactorCode; - internal static async Task RefreshCMs(uint cellID) { - bool initialized = false; - for (byte i = 0; (i < WebBrowser.MaxRetries) && !initialized; i++) { - try { - Logging.LogGenericInfo("Refreshing list of CMs..."); - await SteamDirectory.Initialize(cellID).ConfigureAwait(false); - initialized = true; - } catch (Exception e) { - Logging.LogGenericException(e); - await Task.Delay(1000).ConfigureAwait(false); - } + internal static void InitializeCMs(uint cellID, IServerListProvider serverListProvider) { + if (serverListProvider == null) { + Logging.LogNullError(nameof(serverListProvider)); + return; } - if (initialized) { - Logging.LogGenericInfo("Success!"); - } else { - Logging.LogGenericWarning("Failed to initialize list of CMs after " + WebBrowser.MaxRetries + " tries, ASF will use built-in SK2 list, it may take a while to connect"); - } + CMClient.Servers.CellID = cellID; + CMClient.Servers.ServerListProvider = serverListProvider; } private static bool IsOwner(ulong steamID) { diff --git a/ArchiSteamFarm/GlobalDatabase.cs b/ArchiSteamFarm/GlobalDatabase.cs index b9e1aaa55..29d1c4309 100644 --- a/ArchiSteamFarm/GlobalDatabase.cs +++ b/ArchiSteamFarm/GlobalDatabase.cs @@ -24,18 +24,29 @@ using Newtonsoft.Json; using System; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Threading; namespace ArchiSteamFarm { internal sealed class GlobalDatabase { + private static readonly JsonSerializerSettings CustomSerializerSettings = new JsonSerializerSettings { + Converters = new List { + new IPAddressConverter(), + new IPEndPointConverter() + } + }; + + [JsonProperty(Required = Required.DisallowNull)] + private uint _CellID; + internal uint CellID { get { return _CellID; } set { - if (_CellID == value) { + if ((value == 0) || (_CellID == value)) { return; } @@ -45,7 +56,8 @@ namespace ArchiSteamFarm { } [JsonProperty(Required = Required.DisallowNull)] - private uint _CellID; + [SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Local")] + internal JsonStorageServerListProvider ServerListProvider { get; private set; } private readonly object FileLock = new object(); @@ -64,7 +76,7 @@ namespace ArchiSteamFarm { GlobalDatabase globalDatabase; try { - globalDatabase = JsonConvert.DeserializeObject(File.ReadAllText(filePath)); + globalDatabase = JsonConvert.DeserializeObject(File.ReadAllText(filePath), CustomSerializerSettings); } catch (Exception e) { Logging.LogGenericException(e); return null; @@ -76,11 +88,13 @@ namespace ArchiSteamFarm { } globalDatabase.FilePath = filePath; + globalDatabase.ServerListProvider.GlobalDatabase = globalDatabase; + return globalDatabase; } // This constructor is used when creating new database - private GlobalDatabase(string filePath) { + private GlobalDatabase(string filePath) : this() { if (string.IsNullOrEmpty(filePath)) { throw new ArgumentNullException(nameof(filePath)); } @@ -91,10 +105,12 @@ namespace ArchiSteamFarm { // This constructor is used only by deserializer [SuppressMessage("ReSharper", "UnusedMember.Local")] - private GlobalDatabase() { } + private GlobalDatabase() { + ServerListProvider = new JsonStorageServerListProvider(this); + } - private void Save() { - string json = JsonConvert.SerializeObject(this); + internal void Save() { + string json = JsonConvert.SerializeObject(this, CustomSerializerSettings); if (string.IsNullOrEmpty(json)) { Logging.LogNullError(nameof(json)); return; diff --git a/ArchiSteamFarm/IPAddressConverter.cs b/ArchiSteamFarm/IPAddressConverter.cs new file mode 100644 index 000000000..553ee0080 --- /dev/null +++ b/ArchiSteamFarm/IPAddressConverter.cs @@ -0,0 +1,44 @@ +/* + _ _ _ ____ _ _____ + / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___ + / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \ + / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | | +/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_| + + Copyright 2015-2016 Łukasz "JustArchi" Domeradzki + Contact: JustArchi@JustArchi.net + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +using System; +using System.Net; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace ArchiSteamFarm { + internal sealed class IPAddressConverter : JsonConverter { + public override bool CanConvert(Type objectType) => objectType == typeof(IPAddress); + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { + JToken token = JToken.Load(reader); + return IPAddress.Parse(token.Value()); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { + IPAddress ip = (IPAddress) value; + writer.WriteValue(ip.ToString()); + } + } +} diff --git a/ArchiSteamFarm/IPEndPointConverter.cs b/ArchiSteamFarm/IPEndPointConverter.cs new file mode 100644 index 000000000..041e57681 --- /dev/null +++ b/ArchiSteamFarm/IPEndPointConverter.cs @@ -0,0 +1,51 @@ +/* + _ _ _ ____ _ _____ + / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___ + / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \ + / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | | +/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_| + + Copyright 2015-2016 Łukasz "JustArchi" Domeradzki + Contact: JustArchi@JustArchi.net + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +using System; +using System.Net; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace ArchiSteamFarm { + internal sealed class IPEndPointConverter : JsonConverter { + public override bool CanConvert(Type objectType) => objectType == typeof(IPEndPoint); + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { + JObject jo = JObject.Load(reader); + IPAddress address = jo["Address"].ToObject(serializer); + ushort port = jo["Port"].Value(); + return new IPEndPoint(address, port); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { + IPEndPoint ep = (IPEndPoint) value; + writer.WriteStartObject(); + writer.WritePropertyName("Address"); + serializer.Serialize(writer, ep.Address); + writer.WritePropertyName("Port"); + writer.WriteValue(ep.Port); + writer.WriteEndObject(); + } + } +} diff --git a/ArchiSteamFarm/JsonStorageServerListProvider.cs b/ArchiSteamFarm/JsonStorageServerListProvider.cs new file mode 100644 index 000000000..6914964fc --- /dev/null +++ b/ArchiSteamFarm/JsonStorageServerListProvider.cs @@ -0,0 +1,72 @@ +/* + _ _ _ ____ _ _____ + / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___ + / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \ + / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | | +/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_| + + Copyright 2015-2016 Łukasz "JustArchi" Domeradzki + Contact: JustArchi@JustArchi.net + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using Newtonsoft.Json; +using SteamKit2.Discovery; + +namespace ArchiSteamFarm { + internal sealed class JsonStorageServerListProvider : IServerListProvider { + [JsonProperty(Required = Required.DisallowNull)] + [SuppressMessage("ReSharper", "FieldCanBeMadeReadOnly.Local")] + private HashSet Servers = new HashSet(); + + internal GlobalDatabase GlobalDatabase { private get; set; } + + internal JsonStorageServerListProvider(GlobalDatabase globalDatabase) { + if (globalDatabase == null) { + throw new ArgumentNullException(nameof(globalDatabase)); + } + + GlobalDatabase = globalDatabase; + } + + // This constructor is used only by deserializer + [SuppressMessage("ReSharper", "UnusedMember.Local")] + private JsonStorageServerListProvider() { } + + public Task> FetchServerListAsync() => Task.FromResult(Servers.Select(endpoint => endpoint)); + + public Task UpdateServerListAsync(IEnumerable endpoints) { + if (endpoints == null) { + Logging.LogNullError(nameof(endpoints)); + return Task.Delay(0); + } + + Servers.Clear(); + foreach (IPEndPoint endpoint in endpoints) { + Servers.Add(endpoint); + } + + GlobalDatabase.Save(); + + return Task.Delay(0); + } + } +} diff --git a/ArchiSteamFarm/Program.cs b/ArchiSteamFarm/Program.cs index 9f94624ea..cbe766f3d 100644 --- a/ArchiSteamFarm/Program.cs +++ b/ArchiSteamFarm/Program.cs @@ -462,7 +462,6 @@ namespace ArchiSteamFarm { Logging.LogGenericInfo("ASF V" + Version); Directory.SetCurrentDirectory(ExecutableDirectory); - InitServices(); // Allow loading configs from source tree if it's a debug build if (Debugging.IsDebugBuild) { @@ -481,6 +480,8 @@ namespace ArchiSteamFarm { } } + InitServices(); + // If debugging is on, we prepare debug directory prior to running if (GlobalConfig.Debug) { if (Directory.Exists(DebugDirectory)) { @@ -515,7 +516,7 @@ namespace ArchiSteamFarm { CheckForUpdate().Wait(); // Before attempting to connect, initialize our list of CMs - Bot.RefreshCMs(GlobalDatabase.CellID).Wait(); + Bot.InitializeCMs(GlobalDatabase.CellID, GlobalDatabase.ServerListProvider); bool isRunning = false;