mirror of
https://github.com/ppy/osu.git
synced 2024-12-13 04:32:57 +08:00
Add basic online API support.
This commit is contained in:
parent
6a588301ba
commit
8870935a4b
@ -1 +1 @@
|
||||
Subproject commit 3bbfe0137546497e767f7863cda66efc42b1686c
|
||||
Subproject commit 79572e2da7d5f0467f6fd6ad277cfbade2e21b79
|
@ -2,6 +2,7 @@
|
||||
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Game.Online.API;
|
||||
|
||||
namespace osu.Game.Configuration
|
||||
{
|
||||
@ -12,6 +13,10 @@ namespace osu.Game.Configuration
|
||||
Set(OsuConfig.Width, 1366);
|
||||
Set(OsuConfig.Height, 768);
|
||||
Set(OsuConfig.MouseSensitivity, 1.0);
|
||||
|
||||
Set(OsuConfig.Username, string.Empty);
|
||||
Set(OsuConfig.Password, string.Empty);
|
||||
Set(OsuConfig.Token, string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,5 +25,8 @@ namespace osu.Game.Configuration
|
||||
Width,
|
||||
Height,
|
||||
MouseSensitivity,
|
||||
Username,
|
||||
Password,
|
||||
Token
|
||||
}
|
||||
}
|
||||
|
277
osu.Game/Online/API/APIAccess.cs
Normal file
277
osu.Game/Online/API/APIAccess.cs
Normal file
@ -0,0 +1,277 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Online.API.Requests;
|
||||
|
||||
namespace osu.Game.Online.API
|
||||
{
|
||||
internal class APIAccess
|
||||
{
|
||||
private OAuth authentication;
|
||||
|
||||
internal string Endpoint = @"https://new.ppy.sh";
|
||||
const string ClientId = @"daNBnfdv7SppRVc61z0XuOI13y6Hroiz";
|
||||
const string ClientSecret = @"d6fgZuZeQ0eSXkEj5igdqQX6ztdtS6Ow";
|
||||
|
||||
ConcurrentQueue<APIRequest> queue = new ConcurrentQueue<APIRequest>();
|
||||
|
||||
public string Username;
|
||||
|
||||
private SecurePassword password;
|
||||
|
||||
public string Password
|
||||
{
|
||||
set
|
||||
{
|
||||
password = string.IsNullOrEmpty(value) ? null : new SecurePassword(value);
|
||||
}
|
||||
}
|
||||
|
||||
public string Token
|
||||
{
|
||||
get { return authentication.Token?.ToString(); }
|
||||
|
||||
set
|
||||
{
|
||||
|
||||
if (string.IsNullOrEmpty(value))
|
||||
authentication.Token = null;
|
||||
else
|
||||
authentication.Token = OAuthToken.Parse(value);
|
||||
}
|
||||
}
|
||||
|
||||
protected bool HasLogin => Token != null || (!string.IsNullOrEmpty(Username) && password != null);
|
||||
|
||||
private Thread thread;
|
||||
|
||||
Logger log;
|
||||
|
||||
internal APIAccess()
|
||||
{
|
||||
authentication = new OAuth(ClientId, ClientSecret, Endpoint);
|
||||
log = Logger.GetLogger(LoggingTarget.Network);
|
||||
|
||||
thread = new Thread(run) { IsBackground = true };
|
||||
thread.Start();
|
||||
}
|
||||
|
||||
internal string AccessToken => authentication.RequestAccessToken();
|
||||
|
||||
/// <summary>
|
||||
/// Number of consecutive requests which failed due to network issues.
|
||||
/// </summary>
|
||||
int failureCount = 0;
|
||||
|
||||
private void run()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
switch (State)
|
||||
{
|
||||
case APIState.Failing:
|
||||
//todo: replace this with a ping request.
|
||||
log.Add($@"In a failing state, waiting a bit before we try again...");
|
||||
Thread.Sleep(5000);
|
||||
if (queue.Count == 0)
|
||||
{
|
||||
log.Add($@"Queueing a ping request");
|
||||
Queue(new ListChannelsRequest() { Timeout = 5000 });
|
||||
}
|
||||
break;
|
||||
case APIState.Offline:
|
||||
//work to restore a connection...
|
||||
if (!HasLogin)
|
||||
{
|
||||
//OsuGame.Scheduler.Add(() => { OsuGame.ShowLogin(); });
|
||||
|
||||
State = APIState.Offline;
|
||||
Thread.Sleep(500);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (State < APIState.Connecting)
|
||||
State = APIState.Connecting;
|
||||
|
||||
if (!authentication.HasValidAccessToken && !authentication.AuthenticateWithLogin(Username, password.Get(Representation.Raw)))
|
||||
{
|
||||
//todo: this fails even on network-related issues. we should probably handle those differently.
|
||||
//NotificationManager.ShowMessage("Login failed!");
|
||||
log.Add(@"Login failed!");
|
||||
ClearCredentials();
|
||||
continue;
|
||||
}
|
||||
|
||||
//we're connected!
|
||||
State = APIState.Online;
|
||||
failureCount = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
//hard bail if we can't get a valid access token.
|
||||
if (authentication.RequestAccessToken() == null)
|
||||
{
|
||||
State = APIState.Offline;
|
||||
continue;
|
||||
}
|
||||
|
||||
//process the request queue.
|
||||
APIRequest req;
|
||||
while (queue.TryPeek(out req))
|
||||
{
|
||||
if (handleRequest(req))
|
||||
{
|
||||
//we have succeeded, so let's unqueue.
|
||||
queue.TryDequeue(out req);
|
||||
}
|
||||
}
|
||||
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearCredentials()
|
||||
{
|
||||
Username = null;
|
||||
password = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle a single API request.
|
||||
/// </summary>
|
||||
/// <param name="req">The request.</param>
|
||||
/// <returns>true if we should remove this request from the queue.</returns>
|
||||
private bool handleRequest(APIRequest req)
|
||||
{
|
||||
try
|
||||
{
|
||||
req.Perform(this);
|
||||
|
||||
State = APIState.Online;
|
||||
failureCount = 0;
|
||||
return true;
|
||||
}
|
||||
catch (WebException we)
|
||||
{
|
||||
HttpStatusCode statusCode = (we.Response as HttpWebResponse)?.StatusCode ?? HttpStatusCode.RequestTimeout;
|
||||
|
||||
switch (statusCode)
|
||||
{
|
||||
case HttpStatusCode.Unauthorized:
|
||||
State = APIState.Offline;
|
||||
return true;
|
||||
case HttpStatusCode.RequestTimeout:
|
||||
failureCount++;
|
||||
log.Add($@"API failure count is now {failureCount}");
|
||||
|
||||
if (failureCount < 3)
|
||||
//we might try again at an api level.
|
||||
return false;
|
||||
|
||||
State = APIState.Failing;
|
||||
return true;
|
||||
}
|
||||
|
||||
req.Fail(we);
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e is TimeoutException)
|
||||
log.Add(@"API level timeout exception was hit");
|
||||
|
||||
req.Fail(e);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private APIState state;
|
||||
public APIState State
|
||||
{
|
||||
get { return state; }
|
||||
set
|
||||
{
|
||||
APIState oldState = state;
|
||||
APIState newState = value;
|
||||
|
||||
state = value;
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case APIState.Failing:
|
||||
case APIState.Offline:
|
||||
flushQueue();
|
||||
break;
|
||||
}
|
||||
|
||||
if (oldState != newState)
|
||||
{
|
||||
//OsuGame.Scheduler.Add(delegate
|
||||
{
|
||||
//NotificationManager.ShowMessage($@"We just went {newState}!", newState == APIState.Online ? Color4.YellowGreen : Color4.OrangeRed, 5000);
|
||||
log.Add($@"We just went {newState}!");
|
||||
OnStateChange?.Invoke(oldState, newState);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void Queue(APIRequest request)
|
||||
{
|
||||
queue.Enqueue(request);
|
||||
}
|
||||
|
||||
internal event StateChangeDelegate OnStateChange;
|
||||
|
||||
internal delegate void StateChangeDelegate(APIState oldState, APIState newState);
|
||||
|
||||
internal enum APIState
|
||||
{
|
||||
/// <summary>
|
||||
/// We cannot login (not enough credentials).
|
||||
/// </summary>
|
||||
Offline,
|
||||
|
||||
/// <summary>
|
||||
/// We are having connectivity issues.
|
||||
/// </summary>
|
||||
Failing,
|
||||
|
||||
/// <summary>
|
||||
/// We are in the process of (re-)connecting.
|
||||
/// </summary>
|
||||
Connecting,
|
||||
|
||||
/// <summary>
|
||||
/// We are online.
|
||||
/// </summary>
|
||||
Online
|
||||
}
|
||||
|
||||
private void flushQueue(bool failOldRequests = true)
|
||||
{
|
||||
var oldQueue = queue;
|
||||
|
||||
//flush the queue.
|
||||
queue = new ConcurrentQueue<APIRequest>();
|
||||
|
||||
if (failOldRequests)
|
||||
{
|
||||
APIRequest req;
|
||||
while (queue.TryDequeue(out req))
|
||||
req.Fail(new Exception(@"Disconnected from server"));
|
||||
}
|
||||
}
|
||||
|
||||
internal void Logout()
|
||||
{
|
||||
authentication.Clear();
|
||||
State = APIState.Offline;
|
||||
}
|
||||
}
|
||||
}
|
93
osu.Game/Online/API/APIRequest.cs
Normal file
93
osu.Game/Online/API/APIRequest.cs
Normal file
@ -0,0 +1,93 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using osu.Framework.IO.Network;
|
||||
|
||||
namespace osu.Game.Online.API
|
||||
{
|
||||
/// <summary>
|
||||
/// An API request with a well-defined response type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the response (used for deserialisation).</typeparam>
|
||||
internal class APIRequest<T> : APIRequest
|
||||
{
|
||||
protected override WebRequest CreateWebRequest() => new JsonWebRequest<T>(Uri);
|
||||
|
||||
public APIRequest()
|
||||
{
|
||||
base.Success += onSuccess;
|
||||
}
|
||||
|
||||
private void onSuccess()
|
||||
{
|
||||
Success?.Invoke((WebRequest as JsonWebRequest<T>).ResponseObject);
|
||||
}
|
||||
|
||||
public new event APISuccessHandler<T> Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// AN API request with no specified response type.
|
||||
/// </summary>
|
||||
public class APIRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// The maximum amount of time before this request will fail.
|
||||
/// </summary>
|
||||
internal int Timeout = WebRequest.DEFAULT_TIMEOUT;
|
||||
|
||||
protected virtual string Target => string.Empty;
|
||||
|
||||
protected virtual WebRequest CreateWebRequest() => new WebRequest(Uri);
|
||||
|
||||
protected virtual string Uri => $@"{api.Endpoint}/api/v2/{Target}";
|
||||
|
||||
private double remainingTime => Math.Max(0, Timeout - (DateTime.Now.TotalMilliseconds() - (startTime ?? 0)));
|
||||
|
||||
internal bool ExceededTimeout => remainingTime == 0;
|
||||
|
||||
private double? startTime;
|
||||
|
||||
internal double StartTime => startTime ?? -1;
|
||||
|
||||
private APIAccess api;
|
||||
protected WebRequest WebRequest;
|
||||
|
||||
public event APISuccessHandler Success;
|
||||
public event APIFailureHandler Failure;
|
||||
|
||||
internal void Perform(APIAccess api)
|
||||
{
|
||||
if (startTime == null)
|
||||
startTime = DateTime.Now.TotalMilliseconds();
|
||||
|
||||
this.api = api;
|
||||
|
||||
if (remainingTime <= 0)
|
||||
throw new TimeoutException(@"API request timeout hit");
|
||||
|
||||
WebRequest = CreateWebRequest();
|
||||
WebRequest.RetryCount = 0;
|
||||
WebRequest.Headers[@"Authorization"] = $@"Bearer {api.AccessToken}";
|
||||
|
||||
WebRequest.BlockingPerform();
|
||||
|
||||
//OsuGame.Scheduler.Add(delegate {
|
||||
Success?.Invoke();
|
||||
//});
|
||||
}
|
||||
|
||||
internal void Fail(Exception e)
|
||||
{
|
||||
WebRequest?.Abort();
|
||||
//OsuGame.Scheduler.Add(delegate {
|
||||
Failure?.Invoke(e);
|
||||
//});
|
||||
}
|
||||
}
|
||||
|
||||
public delegate void APIFailureHandler(Exception e);
|
||||
public delegate void APISuccessHandler();
|
||||
public delegate void APISuccessHandler<T>(T content);
|
||||
}
|
162
osu.Game/Online/API/OAuth.cs
Normal file
162
osu.Game/Online/API/OAuth.cs
Normal file
@ -0,0 +1,162 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using osu.Framework.IO.Network;
|
||||
|
||||
namespace osu.Game.Online.API
|
||||
{
|
||||
internal class OAuth
|
||||
{
|
||||
private readonly string clientId;
|
||||
private readonly string clientSecret;
|
||||
private readonly string endpoint;
|
||||
|
||||
public OAuthToken Token;
|
||||
|
||||
internal OAuth(string clientId, string clientSecret, string endpoint)
|
||||
{
|
||||
Debug.Assert(clientId != null);
|
||||
Debug.Assert(clientSecret != null);
|
||||
Debug.Assert(endpoint != null);
|
||||
|
||||
this.clientId = clientId;
|
||||
this.clientSecret = clientSecret;
|
||||
this.endpoint = endpoint;
|
||||
}
|
||||
|
||||
internal bool AuthenticateWithLogin(string username, string password)
|
||||
{
|
||||
var req = new AccessTokenRequestPassword(username, password)
|
||||
{
|
||||
Url = $@"{endpoint}/oauth/access_token",
|
||||
Method = HttpMethod.POST,
|
||||
ClientId = clientId,
|
||||
ClientSecret = clientSecret
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
req.BlockingPerform();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Token = req.ResponseObject;
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool AuthenticateWithRefresh(string refresh)
|
||||
{
|
||||
try
|
||||
{
|
||||
var req = new AccessTokenRequestRefresh(refresh)
|
||||
{
|
||||
Url = $@"{endpoint}/oauth/access_token",
|
||||
Method = HttpMethod.POST,
|
||||
ClientId = clientId,
|
||||
ClientSecret = clientSecret
|
||||
};
|
||||
req.BlockingPerform();
|
||||
|
||||
Token = req.ResponseObject;
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
//todo: potentially only kill the refresh token on certain exception types.
|
||||
Token = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Should be run before any API request to make sure we have a valid key.
|
||||
/// </summary>
|
||||
private bool ensureAccessToken()
|
||||
{
|
||||
//todo: we need to mutex this to ensure only one authentication request is running at a time.
|
||||
|
||||
//If we already have a valid access token, let's use it.
|
||||
if (accessTokenValid) return true;
|
||||
|
||||
//If not, let's try using our refresh token to request a new access token.
|
||||
if (!string.IsNullOrEmpty(Token?.RefreshToken))
|
||||
AuthenticateWithRefresh(Token.RefreshToken);
|
||||
|
||||
return accessTokenValid;
|
||||
}
|
||||
|
||||
private bool accessTokenValid => Token?.IsValid ?? false;
|
||||
|
||||
internal bool HasValidAccessToken => RequestAccessToken() != null;
|
||||
|
||||
internal string RequestAccessToken()
|
||||
{
|
||||
if (!ensureAccessToken()) return null;
|
||||
|
||||
return Token.AccessToken;
|
||||
}
|
||||
|
||||
internal void Clear()
|
||||
{
|
||||
Token = null;
|
||||
}
|
||||
|
||||
private class AccessTokenRequestRefresh : AccessTokenRequest
|
||||
{
|
||||
internal readonly string RefreshToken;
|
||||
|
||||
internal AccessTokenRequestRefresh(string refreshToken)
|
||||
{
|
||||
RefreshToken = refreshToken;
|
||||
GrantType = @"refresh_token";
|
||||
}
|
||||
|
||||
protected override void PrePerform()
|
||||
{
|
||||
Parameters[@"refresh_token"] = RefreshToken;
|
||||
base.PrePerform();
|
||||
}
|
||||
}
|
||||
|
||||
private class AccessTokenRequestPassword : AccessTokenRequest
|
||||
{
|
||||
internal readonly string Username;
|
||||
internal readonly string Password;
|
||||
|
||||
internal AccessTokenRequestPassword(string username, string password)
|
||||
{
|
||||
Username = username;
|
||||
Password = password;
|
||||
GrantType = @"password";
|
||||
}
|
||||
|
||||
protected override void PrePerform()
|
||||
{
|
||||
Parameters[@"username"] = Username;
|
||||
Parameters[@"password"] = Password;
|
||||
base.PrePerform();
|
||||
}
|
||||
}
|
||||
|
||||
private class AccessTokenRequest : JsonWebRequest<OAuthToken>
|
||||
{
|
||||
protected string GrantType;
|
||||
|
||||
internal string ClientId;
|
||||
internal string ClientSecret;
|
||||
|
||||
protected override void PrePerform()
|
||||
{
|
||||
Parameters[@"grant_type"] = GrantType;
|
||||
Parameters[@"client_id"] = ClientId;
|
||||
Parameters[@"client_secret"] = ClientSecret;
|
||||
base.PrePerform();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
65
osu.Game/Online/API/OAuthToken.cs
Normal file
65
osu.Game/Online/API/OAuthToken.cs
Normal file
@ -0,0 +1,65 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace osu.Game.Online.API
|
||||
{
|
||||
[Serializable]
|
||||
internal class OAuthToken
|
||||
{
|
||||
/// <summary>
|
||||
/// OAuth 2.0 access token.
|
||||
/// </summary>
|
||||
[JsonProperty(@"access_token")]
|
||||
public string AccessToken;
|
||||
|
||||
[JsonProperty(@"expires_in")]
|
||||
public long ExpiresIn
|
||||
{
|
||||
get
|
||||
{
|
||||
return AccessTokenExpiry - DateTime.Now.ToUnixTimestamp();
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
AccessTokenExpiry = DateTime.Now.AddSeconds(value).ToUnixTimestamp();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsValid => !string.IsNullOrEmpty(AccessToken) && ExpiresIn > 30;
|
||||
|
||||
public long AccessTokenExpiry;
|
||||
|
||||
/// <summary>
|
||||
/// OAuth 2.0 refresh token.
|
||||
/// </summary>
|
||||
[JsonProperty(@"refresh_token")]
|
||||
public string RefreshToken;
|
||||
|
||||
public override string ToString() => $@"{AccessToken}/{AccessTokenExpiry.ToString(NumberFormatInfo.InvariantInfo)}/{RefreshToken}";
|
||||
|
||||
public static OAuthToken Parse(string value)
|
||||
{
|
||||
try
|
||||
{
|
||||
string[] parts = value.Split('/');
|
||||
return new OAuthToken()
|
||||
{
|
||||
AccessToken = parts[0],
|
||||
AccessTokenExpiry = long.Parse(parts[1], NumberFormatInfo.InvariantInfo),
|
||||
RefreshToken = parts[2]
|
||||
};
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
37
osu.Game/Online/API/Requests/GetMessagesRequest.cs
Normal file
37
osu.Game/Online/API/Requests/GetMessagesRequest.cs
Normal file
@ -0,0 +1,37 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.IO.Network;
|
||||
using osu.Game.Online.Social;
|
||||
|
||||
namespace osu.Game.Online.API.Requests
|
||||
{
|
||||
internal class GetMessagesRequest : APIRequest<List<Message>>
|
||||
{
|
||||
List<Channel> channels;
|
||||
long? since;
|
||||
|
||||
public GetMessagesRequest(List<Channel> channels, long? sinceId)
|
||||
{
|
||||
this.channels = channels;
|
||||
this.since = sinceId;
|
||||
}
|
||||
|
||||
protected override WebRequest CreateWebRequest()
|
||||
{
|
||||
string channelString = string.Empty;
|
||||
foreach (Channel c in channels)
|
||||
channelString += c.Id + ",";
|
||||
channelString = channelString.TrimEnd(',');
|
||||
|
||||
var req = base.CreateWebRequest();
|
||||
req.AddParameter(@"channels", channelString);
|
||||
if (since.HasValue) req.AddParameter(@"since", since.Value.ToString());
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
protected override string Target => @"chat/messages";
|
||||
}
|
||||
}
|
13
osu.Game/Online/API/Requests/ListChannels.cs
Normal file
13
osu.Game/Online/API/Requests/ListChannels.cs
Normal file
@ -0,0 +1,13 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Online.Social;
|
||||
|
||||
namespace osu.Game.Online.API.Requests
|
||||
{
|
||||
internal class ListChannelsRequest : APIRequest<List<Channel>>
|
||||
{
|
||||
protected override string Target => @"chat/channels";
|
||||
}
|
||||
}
|
52
osu.Game/Online/API/SecurePassword.cs
Normal file
52
osu.Game/Online/API/SecurePassword.cs
Normal file
@ -0,0 +1,52 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Security;
|
||||
|
||||
namespace osu.Game.Online.API
|
||||
{
|
||||
internal class SecurePassword
|
||||
{
|
||||
private readonly SecureString storage = new SecureString();
|
||||
private readonly Representation representation;
|
||||
|
||||
//todo: move this to a central constants file.
|
||||
private const string password_entropy = @"cu24180ncjeiu0ci1nwui";
|
||||
|
||||
public SecurePassword(string input, bool encrypted = false)
|
||||
{
|
||||
//if (encrypted)
|
||||
//{
|
||||
// string rep;
|
||||
// input = DPAPI.Decrypt(input, password_entropy, out rep);
|
||||
// Enum.TryParse(rep, out representation);
|
||||
//}
|
||||
//else
|
||||
{
|
||||
representation = Representation.Raw;
|
||||
}
|
||||
|
||||
foreach (char c in input)
|
||||
storage.AppendChar(c);
|
||||
storage.MakeReadOnly();
|
||||
}
|
||||
|
||||
internal string Get(Representation request = Representation.Raw)
|
||||
{
|
||||
switch (request)
|
||||
{
|
||||
default:
|
||||
return storage.UnsecureRepresentation();
|
||||
//case Representation.Encrypted:
|
||||
// return DPAPI.Encrypt(DPAPI.KeyType.UserKey, storage.UnsecureRepresentation(), password_entropy, representation.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum Representation
|
||||
{
|
||||
Raw,
|
||||
Encrypted
|
||||
}
|
||||
}
|
32
osu.Game/Online/Chat/Channel.cs
Normal file
32
osu.Game/Online/Chat/Channel.cs
Normal file
@ -0,0 +1,32 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace osu.Game.Online.Chat
|
||||
{
|
||||
public class Channel
|
||||
{
|
||||
[JsonProperty(@"name")]
|
||||
public string Name;
|
||||
|
||||
[JsonProperty(@"description")]
|
||||
public string Topic;
|
||||
|
||||
[JsonProperty(@"type")]
|
||||
public string Type;
|
||||
|
||||
[JsonProperty(@"channel_id")]
|
||||
public int Id;
|
||||
|
||||
public List<Message> Messages = new List<Message>();
|
||||
|
||||
internal bool Joined;
|
||||
|
||||
[JsonConstructor]
|
||||
public Channel()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
34
osu.Game/Online/Chat/Message.cs
Normal file
34
osu.Game/Online/Chat/Message.cs
Normal file
@ -0,0 +1,34 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace osu.Game.Online.Chat
|
||||
{
|
||||
public class Message
|
||||
{
|
||||
[JsonProperty(@"message_id")]
|
||||
public long Id;
|
||||
|
||||
[JsonProperty(@"user_id")]
|
||||
public string UserId;
|
||||
|
||||
[JsonProperty(@"channel_id")]
|
||||
public string ChannelId;
|
||||
|
||||
[JsonProperty(@"timestamp")]
|
||||
public DateTime Timestamp;
|
||||
|
||||
[JsonProperty(@"content")]
|
||||
internal string Content;
|
||||
|
||||
[JsonProperty(@"sender")]
|
||||
internal string User;
|
||||
|
||||
[JsonConstructor]
|
||||
public Message()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -1,12 +1,16 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using osu.Framework.Framework;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.GameModes.Menu;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Game.Graphics.Processing;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.Social;
|
||||
|
||||
namespace osu.Game
|
||||
{
|
||||
@ -16,6 +20,8 @@ namespace osu.Game
|
||||
|
||||
protected override string MainResourceFile => @"osu.Game.Resources.dll";
|
||||
|
||||
internal APIAccess API;
|
||||
|
||||
public override void Load()
|
||||
{
|
||||
base.Load();
|
||||
@ -23,6 +29,19 @@ namespace osu.Game
|
||||
Window.Size = new Size(Config.Get<int>(OsuConfig.Width), Config.Get<int>(OsuConfig.Height));
|
||||
Window.OnSizeChanged += window_OnSizeChanged;
|
||||
|
||||
API = new APIAccess()
|
||||
{
|
||||
Username = Config.Get<string>(OsuConfig.Username),
|
||||
Password = Config.Get<string>(OsuConfig.Password),
|
||||
Token = Config.Get<string>(OsuConfig.Token)
|
||||
};
|
||||
|
||||
//var req = new ListChannelsRequest();
|
||||
//req.Success += content =>
|
||||
//{
|
||||
//};
|
||||
//API.Queue(req);
|
||||
|
||||
AddProcessingContainer(new RatioAdjust());
|
||||
|
||||
//Add(new FontTest());
|
||||
@ -31,10 +50,18 @@ namespace osu.Game
|
||||
Add(new CursorContainer());
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
//refresh token may have changed.
|
||||
Config.Set(OsuConfig.Token, API.Token);
|
||||
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
|
||||
private void window_OnSizeChanged()
|
||||
{
|
||||
Config.Set<int>(OsuConfig.Width, Window.Size.Width);
|
||||
Config.Set<int>(OsuConfig.Height, Window.Size.Height);
|
||||
Config.Set(OsuConfig.Width, Window.Size.Width);
|
||||
Config.Set(OsuConfig.Height, Window.Size.Height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,8 @@
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile />
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
@ -31,6 +33,10 @@
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="OpenTK, Version=1.1.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\ppy.OpenTK.1.1.2225.2\lib\net20\OpenTK.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
@ -51,6 +57,15 @@
|
||||
<Compile Include="Graphics\Cursor\CursorContainer.cs" />
|
||||
<Compile Include="Graphics\Processing\RatioAdjust.cs" />
|
||||
<Compile Include="Graphics\TextAwesome.cs" />
|
||||
<Compile Include="Online\API\APIAccess.cs" />
|
||||
<Compile Include="Online\API\APIRequest.cs" />
|
||||
<Compile Include="Online\API\OAuth.cs" />
|
||||
<Compile Include="Online\API\OAuthToken.cs" />
|
||||
<Compile Include="Online\API\Requests\GetMessagesRequest.cs" />
|
||||
<Compile Include="Online\API\SecurePassword.cs" />
|
||||
<Compile Include="Online\API\Requests\ListChannels.cs" />
|
||||
<Compile Include="Online\Social\Channel.cs" />
|
||||
<Compile Include="Online\Social\Message.cs" />
|
||||
<Compile Include="OsuGame.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
|
@ -3,7 +3,7 @@
|
||||
Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
-->
|
||||
|
||||
<packages>
|
||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
|
||||
<package id="ppy.OpenTK" version="1.1.2225.2" targetFramework="net452" />
|
||||
</packages>
|
Loading…
Reference in New Issue
Block a user