Implement and use persistent ServerList

Initial idea was suggested back in #66, but recently made possible in clean and nice way, thanks to @azuisleet and @Netshroud
This commit is contained in:
JustArchi
2016-07-08 09:02:53 +02:00
parent ce4733547c
commit 7e9844edab
7 changed files with 203 additions and 25 deletions

View File

@@ -125,9 +125,12 @@
<Compile Include="CardsFarmer.cs" />
<Compile Include="Debugging.cs" />
<Compile Include="GlobalConfig.cs" />
<Compile Include="IPAddressConverter.cs" />
<Compile Include="IPEndPointConverter.cs" />
<Compile Include="JSON\GitHub.cs" />
<Compile Include="JSON\Steam.cs" />
<Compile Include="Logging.cs" />
<Compile Include="JsonStorageServerListProvider.cs" />
<Compile Include="MobileAuthenticator.cs" />
<Compile Include="Runtime.cs" />
<Compile Include="ObsoleteSteamGuardAccount.cs" />

View File

@@ -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) {

View File

@@ -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<JsonConverter> {
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<GlobalDatabase>(File.ReadAllText(filePath));
globalDatabase = JsonConvert.DeserializeObject<GlobalDatabase>(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;

View File

@@ -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<string>());
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
IPAddress ip = (IPAddress) value;
writer.WriteValue(ip.ToString());
}
}
}

View File

@@ -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<IPAddress>(serializer);
ushort port = jo["Port"].Value<ushort>();
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();
}
}
}

View File

@@ -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<IPEndPoint> Servers = new HashSet<IPEndPoint>();
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<IEnumerable<IPEndPoint>> FetchServerListAsync() => Task.FromResult(Servers.Select(endpoint => endpoint));
public Task UpdateServerListAsync(IEnumerable<IPEndPoint> 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);
}
}
}

View File

@@ -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;