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;