1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-06 06:17:23 +08:00

Merge pull request #210 from peppy/online-improvements

Add login support, better API state change handling.
This commit is contained in:
Thomas Müller 2016-12-01 18:50:54 +01:00 committed by GitHub
commit 20947623d5
26 changed files with 744 additions and 267 deletions

@ -1 +1 @@
Subproject commit ab7b953ec9393d8df5aeb23d23e8b1f8154601c1
Subproject commit 6edd5eacdd25cc8c4f4dbca3414678c0b7dc5deb

View File

@ -14,9 +14,9 @@ using osu.Game;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.Chat;
using osu.Game.Online.Chat.Display;
using OpenTK;
using osu.Framework.Allocation;
using osu.Game.Online.Chat.Drawables;
namespace osu.Desktop.VisualTests.Tests
{
@ -45,7 +45,7 @@ namespace osu.Desktop.VisualTests.Tests
{
base.Reset();
if (api.State != APIAccess.APIState.Online)
if (api.State != APIState.Online)
api.OnStateChange += delegate { initializeChannels(); };
else
initializeChannels();
@ -65,7 +65,7 @@ namespace osu.Desktop.VisualTests.Tests
{
careChannels = new List<Channel>();
if (api.State != APIAccess.APIState.Online)
if (api.State != APIState.Online)
return;
Add(flow = new FlowContainer

View File

@ -12,6 +12,7 @@ using osu.Framework.Graphics.Textures;
using osu.Framework.MathUtils;
using osu.Game.Database;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
using osu.Game.Graphics.UserInterface;
using OpenTK;
using OpenTK.Graphics;
@ -128,45 +129,5 @@ namespace osu.Game.Beatmaps.Drawables
}
};
}
public class Triangles : Container
{
private Texture triangle;
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
triangle = textures.Get(@"Play/osu/triangle@2x");
}
protected override void LoadComplete()
{
base.LoadComplete();
for (int i = 0; i < 10; i++)
{
Add(new Sprite
{
Texture = triangle,
Origin = Anchor.TopCentre,
RelativePositionAxes = Axes.Both,
Position = new Vector2(RNG.NextSingle(), RNG.NextSingle()),
Scale = new Vector2(RNG.NextSingle() * 0.4f + 0.2f),
Alpha = RNG.NextSingle() * 0.3f
});
}
}
protected override void Update()
{
base.Update();
foreach (Drawable d in Children)
{
d.Position -= new Vector2(0, (float)(d.Scale.X * (Time.Elapsed / 880)));
if (d.DrawPosition.Y + d.DrawSize.Y * d.Scale.Y < 0)
d.MoveToY(1);
}
}
}
}
}

View File

@ -156,7 +156,7 @@ namespace osu.Game.Configuration
Set(OsuConfig.AudioDevice, string.Empty);
//Set(OsuConfig.ReleaseStream, ReleaseStream.Lazer, true);
Set(OsuConfig.UpdateFailCount, 0);
//Set(OsuConfig.SavePassword, Password != null);
Set(OsuConfig.SavePassword, false);
Set(OsuConfig.SaveUsername, true);
//Set(OsuConfig.TreeSortMode, TreeGroupMode.Show_All);
//Set(OsuConfig.TreeSortMode2, TreeSortMode.Title);

View File

@ -0,0 +1,85 @@
//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.Linq;
using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.MathUtils;
using OpenTK;
namespace osu.Game.Graphics.Backgrounds
{
public class Triangles : Container
{
private Texture triangle;
public Triangles()
{
Alpha = 0.3f;
}
private float triangleScale = 1;
public float TriangleScale
{
get { return triangleScale; }
set
{
triangleScale = value;
Children.ForEach(t => t.ScaleTo(triangleScale));
}
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
triangle = textures.Get(@"Play/osu/triangle@2x");
}
private int aimTriangleCount => (int)((DrawWidth * DrawHeight) / 800 / triangleScale);
protected override void Update()
{
base.Update();
foreach (Drawable d in Children)
{
d.Position -= new Vector2(0, (float)(d.Scale.X * (50 / DrawHeight) * (Time.Elapsed / 880)) / triangleScale);
if (d.DrawPosition.Y + d.DrawSize.Y * d.Scale.Y < 0)
d.Expire();
}
bool useRandomX = Children.Count() < aimTriangleCount / 2;
while (Children.Count() < aimTriangleCount)
addTriangle(useRandomX);
}
protected virtual Sprite CreateTriangle()
{
var scale = triangleScale * RNG.NextSingle() * 0.4f + 0.2f;
return new Sprite
{
Texture = triangle,
Origin = Anchor.TopCentre,
RelativePositionAxes = Axes.Both,
Scale = new Vector2(scale),
Alpha = RNG.NextSingle(),
Depth = scale,
};
}
private void addTriangle(bool randomX)
{
var sprite = CreateTriangle();
sprite.Position = new Vector2(RNG.NextSingle(), randomX ? RNG.NextSingle() : 1);
Add(sprite);
}
}
}

View File

@ -4,6 +4,7 @@ using osu.Framework.Input;
using OpenTK;
using osu.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Transformations;
namespace osu.Game.Graphics.Containers
{
@ -35,11 +36,16 @@ namespace osu.Game.Graphics.Containers
this.input = input;
}
bool firstUpdate = true;
protected override void Update()
{
base.Update();
content.Position = (ToLocalSpace(input.CurrentState.Mouse.NativeState.Position) - DrawSize / 2) * ParallaxAmount;
content.MoveTo((ToLocalSpace(input.CurrentState.Mouse.NativeState.Position) - DrawSize / 2) * ParallaxAmount, firstUpdate ? 0 : 1000, EasingTypes.OutQuint);
content.Scale = new Vector2(1 + ParallaxAmount);
firstUpdate = false;
}
}
}

View File

@ -116,7 +116,7 @@ namespace osu.Game.Graphics.Cursor
float distance = diff.Length;
Vector2 direction = diff / distance;
float interval = size.X / 2;
float interval = (size.X / 2) * 0.9f;
for (float d = interval; d < distance; d += interval)
{

View File

@ -3,9 +3,12 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Threading;
using osu.Framework;
using osu.Framework.Configuration;
using osu.Framework.Logging;
using osu.Framework.Threading;
using osu.Game.Online.API.Requests;
@ -17,8 +20,8 @@ namespace osu.Game.Online.API
private OAuth authentication;
public string Endpoint = @"https://new.ppy.sh";
const string ClientId = @"daNBnfdv7SppRVc61z0XuOI13y6Hroiz";
const string ClientSecret = @"d6fgZuZeQ0eSXkEj5igdqQX6ztdtS6Ow";
const string ClientId = @"5";
const string ClientSecret = @"FGc9GAtyHzeQDshWP5Ah7dega8hJACAJpQtw6OXk";
ConcurrentQueue<APIRequest> queue = new ConcurrentQueue<APIRequest>();
@ -26,15 +29,11 @@ namespace osu.Game.Online.API
public string Username;
private SecurePassword password;
//private SecurePassword password;
public string Password
{
set
{
password = string.IsNullOrEmpty(value) ? null : new SecurePassword(value);
}
}
public string Password;
public Bindable<User> LocalUser = new Bindable<User>();
public string Token
{
@ -50,7 +49,7 @@ namespace osu.Game.Online.API
}
}
protected bool HasLogin => Token != null || (!string.IsNullOrEmpty(Username) && password != null);
protected bool HasLogin => Token != null || (!string.IsNullOrEmpty(Username) && !string.IsNullOrEmpty(Password));
private Thread thread;
@ -65,6 +64,25 @@ namespace osu.Game.Online.API
thread.Start();
}
private List<IOnlineComponent> components = new List<IOnlineComponent>();
public void Register(IOnlineComponent component)
{
Scheduler.Add(delegate
{
components.Add(component);
component.APIStateChanged(this, state);
});
}
public void Unregister(IOnlineComponent component)
{
Scheduler.Add(delegate
{
components.Remove(component);
});
}
public string AccessToken => authentication.RequestAccessToken();
/// <summary>
@ -102,7 +120,7 @@ namespace osu.Game.Online.API
if (State < APIState.Connecting)
State = APIState.Connecting;
if (!authentication.HasValidAccessToken && !authentication.AuthenticateWithLogin(Username, password.Get(Representation.Raw)))
if (!authentication.HasValidAccessToken && !authentication.AuthenticateWithLogin(Username, Password))
{
//todo: this fails even on network-related issues. we should probably handle those differently.
//NotificationManager.ShowMessage("Login failed!");
@ -111,6 +129,17 @@ namespace osu.Game.Online.API
continue;
}
var userReq = new GetUserRequest();
userReq.Success += (u) => {
LocalUser.Value = u;
};
if (!handleRequest(userReq))
{
State = APIState.Failing;
continue;
}
//we're connected!
State = APIState.Online;
failureCount = 0;
@ -142,7 +171,17 @@ namespace osu.Game.Online.API
private void ClearCredentials()
{
Username = null;
password = null;
Password = null;
}
public void Login(string username, string password)
{
Debug.Assert(State == APIState.Offline);
Username = username;
Password = password;
State = APIState.Connecting;
}
/// <summary>
@ -154,6 +193,7 @@ namespace osu.Game.Online.API
{
try
{
Logger.Log($@"Performing request {req}", LoggingTarget.Network);
req.Perform(this);
State = APIState.Online;
@ -221,6 +261,7 @@ namespace osu.Game.Online.API
log.Add($@"We just went {newState}!");
Scheduler.Add(delegate
{
components.ForEach(c => c.APIStateChanged(this, newState));
OnStateChange?.Invoke(oldState, newState);
});
}
@ -237,29 +278,6 @@ namespace osu.Game.Online.API
public delegate void StateChangeDelegate(APIState oldState, APIState newState);
public 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;
@ -277,6 +295,7 @@ namespace osu.Game.Online.API
public void Logout()
{
ClearCredentials();
authentication.Clear();
State = APIState.Offline;
}
@ -286,4 +305,27 @@ namespace osu.Game.Online.API
Scheduler.Update();
}
}
public 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
}
}

View File

@ -0,0 +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;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace osu.Game.Online.API
{
public interface IOnlineComponent
{
void APIStateChanged(APIAccess api, APIState state);
}
}

View File

@ -30,7 +30,7 @@ namespace osu.Game.Online.API
{
var req = new AccessTokenRequestPassword(username, password)
{
Url = $@"{endpoint}/oauth/access_token",
Url = $@"{endpoint}/oauth/token",
Method = HttpMethod.POST,
ClientId = clientId,
ClientSecret = clientSecret
@ -55,7 +55,7 @@ namespace osu.Game.Online.API
{
var req = new AccessTokenRequestRefresh(refresh)
{
Url = $@"{endpoint}/oauth/access_token",
Url = $@"{endpoint}/oauth/token",
Method = HttpMethod.POST,
ClientId = clientId,
ClientSecret = clientSecret

View File

@ -0,0 +1,18 @@
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Online.API.Requests
{
public class GetUserRequest : APIRequest<User>
{
private int? userId;
public GetUserRequest(int? userId = null)
{
this.userId = userId;
}
protected override string Target => userId.HasValue ? $@"users/{userId}" : @"me";
}
}

View File

@ -4,15 +4,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Transformations;
using OpenTK;
using osu.Framework;
using osu.Framework.Allocation;
namespace osu.Game.Online.Chat.Display
namespace osu.Game.Online.Chat.Drawables
{
public class ChannelDisplay : Container
{

View File

@ -8,7 +8,7 @@ using osu.Framework.Graphics.Sprites;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Online.Chat.Display
namespace osu.Game.Online.Chat.Drawables
{
public class ChatLine : Container
{

View File

@ -10,6 +10,9 @@ namespace osu.Game.Online
[JsonProperty(@"username")]
public string Name;
[JsonProperty(@"id")]
public int Id;
[JsonProperty(@"colour")]
public string Colour;
}

View File

@ -21,6 +21,7 @@ using osu.Game.Database;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Transformations;
using osu.Game.Modes;
using osu.Game.Overlays.Toolbar;
using osu.Game.Screens;
using osu.Game.Screens.Menu;
using osu.Game.Screens.Play;
@ -31,7 +32,7 @@ namespace osu.Game
{
public Toolbar Toolbar;
private ChatConsole chat;
private ChatOverlay chat;
private MusicController musicController;
@ -116,7 +117,7 @@ namespace osu.Game
});
//overlay elements
(chat = new ChatConsole(API) { Depth = 0 }).Preload(this, overlayContent.Add);
(chat = new ChatOverlay { Depth = 0 }).Preload(this, overlayContent.Add);
(Options = new OptionsOverlay { Depth = -1 }).Preload(this, overlayContent.Add);
(musicController = new MusicController() { Depth = -3 }).Preload(this, overlayContent.Add);
(Toolbar = new Toolbar

View File

@ -16,12 +16,14 @@ using osu.Game.Database;
using osu.Game.Graphics.Cursor;
using osu.Game.Graphics.Processing;
using osu.Game.IPC;
using osu.Game.Online;
using osu.Game.Online.API;
using osu.Game.Overlays;
using osu.Game.Online.API.Requests;
namespace osu.Game
{
public class OsuGameBase : BaseGame
public class OsuGameBase : BaseGame, IOnlineComponent
{
internal OsuConfigManager Config;
@ -43,7 +45,7 @@ namespace osu.Game
Dependencies.Cache(this);
Dependencies.Cache(Config);
Dependencies.Cache(new BeatmapDatabase(Host.Storage, Host));
//this completely overrides the framework default. will need to change once we make a proper FontStore.
Dependencies.Cache(Fonts = new FontStore { ScaleAdjust = 0.01f }, true);
@ -73,6 +75,19 @@ namespace osu.Game
Password = Config.Get<string>(OsuConfig.Password),
Token = Config.Get<string>(OsuConfig.Token)
});
API.Register(this);
}
public void APIStateChanged(APIAccess api, APIState state)
{
switch (state)
{
case APIState.Online:
Config.Set(OsuConfig.Username, Config.Get<bool>(OsuConfig.SaveUsername) ? API.Username : string.Empty);
Config.Set(OsuConfig.Password, Config.Get<bool>(OsuConfig.SavePassword) ? API.Password : string.Empty);
break;
}
}
protected override void LoadComplete()

View File

@ -1,12 +1,12 @@
//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.Generic;
using System.Diagnostics;
using System.Linq;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@ -16,11 +16,11 @@ using osu.Framework.Threading;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.Chat;
using osu.Game.Online.Chat.Display;
using osu.Game.Online.Chat.Drawables;
namespace osu.Game.Overlays
{
public class ChatConsole : OverlayContainer
public class ChatOverlay : OverlayContainer, IOnlineComponent
{
private ChannelDisplay channelDisplay;
@ -32,10 +32,8 @@ namespace osu.Game.Overlays
private APIAccess api;
public ChatConsole(APIAccess api)
public ChatOverlay()
{
this.api = api;
RelativeSizeAxes = Axes.X;
Size = new Vector2(1, 300);
Anchor = Anchor.BottomLeft;
@ -57,50 +55,16 @@ namespace osu.Game.Overlays
}
[BackgroundDependencyLoader]
private void load()
private void load(APIAccess api)
{
initializeChannels();
this.api = api;
api.Register(this);
}
private long? lastMessageId;
private List<Channel> careChannels;
private void initializeChannels()
{
careChannels = new List<Channel>();
//if (api.State != APIAccess.APIState.Online)
// return;
SpriteText loading;
Add(loading = new SpriteText
{
Text = @"Loading available channels...",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
TextSize = 40,
});
messageRequest?.Cancel();
ListChannelsRequest req = new ListChannelsRequest();
req.Success += delegate (List<Channel> channels)
{
Scheduler.Add(delegate
{
loading.FadeOut(100);
addChannel(channels.Find(c => c.Name == @"#osu"));
});
//addChannel(channels.Find(c => c.Name == @"#lobby"));
//addChannel(channels.Find(c => c.Name == @"#english"));
messageRequest = Scheduler.AddDelayed(() => FetchNewMessages(api), 1000, true);
};
api.Queue(req);
}
private void addChannel(Channel channel)
{
Add(channelDisplay = new ChannelDisplay(channel));
@ -145,8 +109,58 @@ namespace osu.Game.Overlays
protected override void PopOut()
{
MoveToY(DrawSize.Y, transition_length, EasingTypes.InQuint);
FadeOut(transition_length, EasingTypes.InQuint);
MoveToY(DrawSize.Y, transition_length, EasingTypes.InSine);
FadeOut(transition_length, EasingTypes.InSine);
}
public void APIStateChanged(APIAccess api, APIState state)
{
switch (state)
{
case APIState.Online:
initializeChannels();
break;
default:
messageRequest?.Cancel();
break;
}
}
private void initializeChannels()
{
Clear();
careChannels = new List<Channel>();
//if (api.State != APIAccess.APIState.Online)
// return;
SpriteText loading;
Add(loading = new SpriteText
{
Text = @"Loading available channels...",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
TextSize = 40,
});
messageRequest?.Cancel();
ListChannelsRequest req = new ListChannelsRequest();
req.Success += delegate (List<Channel> channels)
{
Scheduler.Add(delegate
{
loading.FadeOut(100);
addChannel(channels.Find(c => c.Name == @"#osu"));
});
//addChannel(channels.Find(c => c.Name == @"#lobby"));
//addChannel(channels.Find(c => c.Name == @"#english"));
messageRequest = Scheduler.AddDelayed(() => FetchNewMessages(api), 1000, true);
};
api.Queue(req);
}
}
}

View File

@ -1,4 +1,5 @@
using OpenTK;
using System;
using OpenTK;
using osu.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
@ -10,38 +11,72 @@ using osu.Game.Online.API;
namespace osu.Game.Overlays.Options.General
{
public class LoginOptions : OptionsSubsection
public class LoginOptions : OptionsSubsection, IOnlineComponent
{
private Container loginForm;
protected override string Header => "Sign In";
public LoginOptions()
{
Children = new[]
{
loginForm = new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new[] { new LoadingAnimation() }
}
};
}
private Action performLogout;
protected override string Header => "Sign In";
[BackgroundDependencyLoader(permitNulls: true)]
private void load(APIAccess api)
{
api?.Register(this);
}
public void APIStateChanged(APIAccess api, APIState state)
{
if (api == null)
return;
loginForm.Children = new Drawable[]
switch (state)
{
new LoginForm(api)
};
case APIState.Offline:
Children = new Drawable[]
{
new LoginForm()
};
break;
case APIState.Failing:
Children = new Drawable[]
{
new SpriteText
{
Text = "Connection failing :(",
},
};
break;
case APIState.Connecting:
Children = new Drawable[]
{
new SpriteText
{
Text = "Connecting...",
},
};
break;
case APIState.Online:
Children = new Drawable[]
{
new SpriteText
{
Text = $"Connected as {api.Username}!",
},
new OsuButton
{
RelativeSizeAxes = Axes.X,
Text = "Sign out",
Action = api.Logout
}
};
break;
}
}
class LoginForm : FlowContainer
{
public LoginForm(APIAccess api)
private TextBox username;
private TextBox password;
private APIAccess api;
public LoginForm()
{
Direction = FlowDirection.VerticalOnly;
AutoSizeAxes = Axes.Y;
@ -51,16 +86,29 @@ namespace osu.Game.Overlays.Options.General
Children = new Drawable[]
{
new SpriteText { Text = "Username" },
new TextBox { Height = 20, RelativeSizeAxes = Axes.X, Text = api?.Username ?? string.Empty },
username = new TextBox { Height = 20, RelativeSizeAxes = Axes.X, Text = api?.Username ?? string.Empty },
new SpriteText { Text = "Password" },
new TextBox { Height = 20, RelativeSizeAxes = Axes.X },
password = new PasswordTextBox { Height = 20, RelativeSizeAxes = Axes.X },
new OsuButton
{
RelativeSizeAxes = Axes.X,
Text = "Log in",
Action = performLogin
}
};
}
private void performLogin()
{
if (!string.IsNullOrEmpty(username.Text) && !string.IsNullOrEmpty(password.Text))
api.Login(username.Text, password.Text);
}
[BackgroundDependencyLoader(permitNulls: true)]
private void load(APIAccess api)
{
this.api = api;
}
}
}
}

View File

@ -2,22 +2,21 @@
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Transformations;
using osu.Framework.Input;
using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Colour;
using osu.Game.Modes;
using osu.Game.Screens.Play;
using osu.Framework.Input;
using osu.Game.Online.API;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Overlays
namespace osu.Game.Overlays.Toolbar
{
public class Toolbar : OverlayContainer
{
@ -29,7 +28,6 @@ namespace osu.Game.Overlays
public Action OnMusicController;
private ToolbarModeSelector modeSelector;
private ToolbarButton userButton;
private Box solidBackground;
private Box gradientBackground;
@ -127,10 +125,7 @@ namespace osu.Game.Overlays
{
Icon = FontAwesome.fa_search
},
userButton = new ToolbarButton
{
Icon = FontAwesome.fa_user,
},
new ToolbarUserButton(),
new ToolbarButton
{
Icon = FontAwesome.fa_bars
@ -143,12 +138,6 @@ namespace osu.Game.Overlays
Size = new Vector2(1, height);
}
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
userButton.Text = config.Get<string>(OsuConfig.Username);
}
public void SetGameMode(PlayMode mode) => modeSelector.SetGameMode(mode);
}
}

View File

@ -4,20 +4,19 @@
using System;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Transformations;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework;
using osu.Framework.Graphics.Primitives;
namespace osu.Game.Overlays
namespace osu.Game.Overlays.Toolbar
{
public class ToolbarButton : Container
{
public const float WIDTH = 60;
public FontAwesome Icon
{
get { return DrawableIcon.Icon; }
@ -58,6 +57,7 @@ namespace osu.Game.Overlays
private FlowContainer tooltipContainer;
private SpriteText tooltip1;
private SpriteText tooltip2;
protected FlowContainer Flow;
public ToolbarButton()
{
@ -66,16 +66,17 @@ namespace osu.Game.Overlays
HoverBackground = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = new Color4(80, 80, 80, 180),
BlendingMode = BlendingMode.Additive,
Colour = new Color4(60, 60, 60, 255),
Alpha = 0,
},
new FlowContainer
Flow = new FlowContainer
{
Direction = FlowDirection.HorizontalOnly,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Padding = new MarginPadding { Left = 5, Right = 5 },
Padding = new MarginPadding { Left = 15, Right = 15 },
Spacing = new Vector2(5),
RelativeSizeAxes = Axes.Y,
AutoSizeAxes = Axes.X,
Children = new Drawable[]
@ -87,7 +88,6 @@ namespace osu.Game.Overlays
},
DrawableText = new SpriteText
{
Margin = new MarginPadding { Left = 5 },
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
},
@ -118,7 +118,6 @@ namespace osu.Game.Overlays
};
RelativeSizeAxes = Axes.Y;
Size = new Vector2(WIDTH, 1);
}
protected override void Update()
@ -126,7 +125,7 @@ namespace osu.Game.Overlays
base.Update();
//todo: find a way to avoid using this (autosize needs to be able to ignore certain drawables.. in this case the tooltip)
Size = new Vector2(WIDTH + (DrawableText.IsVisible ? DrawableText.DrawSize.X : 0), 1);
Size = new Vector2(Flow.DrawSize.X, 1);
}
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true;
@ -134,21 +133,44 @@ namespace osu.Game.Overlays
protected override bool OnClick(InputState state)
{
Action?.Invoke();
HoverBackground.FlashColour(Color4.White, 400);
HoverBackground.FlashColour(new Color4(255, 255, 255, 180), 800, EasingTypes.OutQuint);
return true;
}
protected override bool OnHover(InputState state)
{
HoverBackground.FadeTo(0.4f, 200);
HoverBackground.FadeIn(200);
tooltipContainer.FadeIn(100);
return false;
}
protected override void OnHoverLost(InputState state)
{
HoverBackground.FadeTo(0, 200);
HoverBackground.FadeOut(200);
tooltipContainer.FadeOut(100);
}
}
public class OpaqueBackground : Container
{
public OpaqueBackground()
{
RelativeSizeAxes = Axes.Both;
Masking = true;
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = new Color4(30, 30, 30, 255)
},
new Triangles
{
RelativeSizeAxes = Axes.Both,
Alpha = 0.05f,
},
};
}
}
}

View File

@ -2,14 +2,12 @@
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Extensions;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using OpenTK.Graphics;
using osu.Framework;
using osu.Framework.Allocation;
using osu.Game.Modes;
using osu.Game.Screens.Play;
using OpenTK.Graphics;
namespace osu.Game.Overlays
namespace osu.Game.Overlays.Toolbar
{
public class ToolbarModeButton : ToolbarButton
{
@ -30,7 +28,23 @@ namespace osu.Game.Overlays
{
set
{
//Background.Colour = value ? new Color4(100, 100, 100, 255) : new Color4(20, 20, 20, 255);
if (value)
{
DrawableIcon.Colour = Color4.White;
DrawableIcon.Masking = true;
DrawableIcon.EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Glow,
Colour = new Color4(255, 194, 224, 100),
Radius = 15,
Roundness = 15,
};
}
else
{
DrawableIcon.Masking = false;
DrawableIcon.Colour = new Color4(255, 194, 224, 255);
}
}
}

View File

@ -3,26 +3,25 @@
using System;
using System.Linq;
using osu.Framework.Caching;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Transformations;
using osu.Game.Graphics.Backgrounds;
using osu.Game.Modes;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework;
using osu.Framework.Caching;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Allocation;
using osu.Game.Modes;
using osu.Game.Screens.Play;
namespace osu.Game.Overlays
namespace osu.Game.Overlays.Toolbar
{
class ToolbarModeSelector : Container
{
const float padding = 10;
private FlowContainer modeButtons;
private Box modeButtonLine;
private Drawable modeButtonLine;
private ToolbarModeButton activeButton;
public Action<PlayMode> OnPlayModeChange;
@ -33,11 +32,7 @@ namespace osu.Game.Overlays
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = new Color4(20, 20, 20, 255)
},
new OpaqueBackground(),
modeButtons = new FlowContainer
{
RelativeSizeAxes = Axes.Y,
@ -45,14 +40,29 @@ namespace osu.Game.Overlays
Direction = FlowDirection.HorizontalOnly,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Padding = new MarginPadding { Left = 10, Right = 10 },
},
modeButtonLine = new Box
modeButtonLine = new Container
{
RelativeSizeAxes = Axes.X,
Size = new Vector2(0.3f, 3),
Anchor = Anchor.BottomLeft,
Origin = Anchor.TopCentre,
Colour = Color4.White
Origin = Anchor.TopLeft,
Masking = true,
EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Glow,
Colour = new Color4(255, 194, 224, 100),
Radius = 15,
Roundness = 15,
},
Children = new []
{
new Box
{
RelativeSizeAxes = Axes.Both,
}
}
}
};
@ -72,9 +82,13 @@ namespace osu.Game.Overlays
}
});
}
}
// We need to set the size within LoadComplete, because
Size = new Vector2(amountButtons * ToolbarButton.WIDTH + padding * 2, 1);
protected override void Update()
{
base.Update();
Size = new Vector2(modeButtons.DrawSize.X, 1);
}
public void SetGameMode(PlayMode mode)
@ -97,7 +111,7 @@ namespace osu.Game.Overlays
base.UpdateLayout();
if (!activeMode.EnsureValid())
activeMode.Refresh(() => modeButtonLine.MoveToX(activeButton.DrawPosition.X + activeButton.DrawSize.X / 2 + padding, 200, EasingTypes.OutQuint));
activeMode.Refresh(() => modeButtonLine.MoveToX(activeButton.DrawPosition.X, 200, EasingTypes.OutQuint));
}
}
}

View File

@ -0,0 +1,140 @@
//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.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.Configuration;
using osu.Game.Online.API;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Overlays.Toolbar
{
class ToolbarUserButton : ToolbarButton, IOnlineComponent
{
private Avatar avatar;
public ToolbarUserButton()
{
DrawableText.Font = @"Exo2.0-MediumItalic";
Add(new OpaqueBackground { Depth = 1 });
Flow.Add(avatar = new Avatar());
}
[BackgroundDependencyLoader]
private void load(APIAccess api, OsuConfigManager config)
{
api.Register(this);
}
public void APIStateChanged(APIAccess api, APIState state)
{
switch (state)
{
default:
Text = @"Guest";
avatar.UserId = 1;
break;
case APIState.Online:
Text = api.Username;
avatar.UserId = api.LocalUser.Value.Id;
break;
}
}
public class Avatar : Container
{
public Drawable Sprite;
private int userId;
private OsuGame game;
private Texture guestTexture;
public Avatar()
{
Size = new Vector2(32);
Anchor = Anchor.CentreLeft;
Origin = Anchor.CentreLeft;
CornerRadius = Size.X / 8;
EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Shadow,
Radius = 4,
Colour = new Color4(0, 0, 0, 0.1f),
};
Masking = true;
}
[BackgroundDependencyLoader]
private void load(OsuGame game, TextureStore textures)
{
this.game = game;
guestTexture = textures.Get(@"Online/avatar-guest@2x");
}
public int UserId
{
get { return userId; }
set
{
if (userId == value)
return;
userId = value;
Sprite newSprite;
if (userId > 1)
newSprite = new OnlineSprite($@"https://a.ppy.sh/{userId}");
else
newSprite = new Sprite { Texture = guestTexture };
newSprite.FillMode = FillMode.Fit;
newSprite.Preload(game, s =>
{
Sprite?.FadeOut();
Sprite?.Expire();
Sprite = s;
Add(s);
s.FadeInFromZero(200);
});
}
}
public class OnlineSprite : Sprite
{
private readonly string url;
private readonly int userId;
public OnlineSprite(string url)
{
Debug.Assert(url != null);
this.url = url;
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
Texture = textures.Get(url);
}
}
}
}
}

View File

@ -8,6 +8,7 @@ using osu.Framework.Audio.Track;
using osu.Framework.GameModes;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Transformations;
using osu.Game.Graphics.Containers;
using osu.Game.Screens.Backgrounds;
using OpenTK.Graphics;
@ -34,13 +35,21 @@ namespace osu.Game.Screens.Menu
{
Children = new Drawable[]
{
logo = new OsuLogo()
new ParallaxContainer
{
Alpha = 0,
BlendingMode = BlendingMode.Additive,
Interactive = false,
Colour = Color4.DarkGray,
Ripple = false
ParallaxAmount = 0.01f,
Children = new Drawable[]
{
logo = new OsuLogo
{
Alpha = 0,
Triangles = false,
BlendingMode = BlendingMode.Additive,
Interactive = false,
Colour = Color4.DarkGray,
Ripple = false
}
}
}
};
}

View File

@ -2,14 +2,19 @@
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Diagnostics;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Graphics.Transformations;
using osu.Framework.Input;
using osu.Framework.MathUtils;
using osu.Game.Graphics.Backgrounds;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Screens.Menu
{
@ -21,16 +26,27 @@ namespace osu.Game.Screens.Menu
private Sprite logo;
private CircularContainer logoContainer;
private Container logoBounceContainer;
private Container logoHoverContainer;
private MenuVisualisation vis;
private Container colourAndTriangles;
public Action Action;
public float SizeForFlow => logo == null ? 0 : logo.DrawSize.X * logo.Scale.X * logoBounceContainer.Scale.X * 0.8f;
public float SizeForFlow => logo == null ? 0 : logo.DrawSize.X * logo.Scale.X * logoBounceContainer.Scale.X * logoHoverContainer.Scale.X * 0.78f;
private Sprite ripple;
private Container rippleContainer;
public bool Triangles
{
set
{
colourAndTriangles.Alpha = value ? 1 : 0;
}
}
public override bool Contains(Vector2 screenSpacePos)
{
return logoContainer.Contains(screenSpacePos);
@ -61,41 +77,77 @@ namespace osu.Game.Screens.Menu
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
logoContainer = new CircularContainer
logoHoverContainer = new Container
{
AutoSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Children = new[]
{
logo = new Sprite
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
},
},
rippleContainer = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Children = new Drawable[]
{
ripple = new Sprite()
new BufferedContainer
{
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
logoContainer = new CircularContainer
{
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Scale = new Vector2(0.8f),
Children = new Drawable[]
{
colourAndTriangles = new Container
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = new Color4(233, 103, 161, 255),
},
new OsuLogoTriangles
{
RelativeSizeAxes = Axes.Both,
},
}
},
},
},
logo = new Sprite
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Scale = new Vector2(0.5f),
},
}
},
rippleContainer = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Children = new Drawable[]
{
ripple = new Sprite()
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
BlendingMode = BlendingMode.Additive,
Scale = new Vector2(0.5f),
Alpha = 0.15f
}
}
},
vis = new MenuVisualisation
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = logo.Size,
BlendingMode = BlendingMode.Additive,
Alpha = 0.05f
Alpha = 0.2f,
}
}
},
vis = new MenuVisualisation
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = logo.Size,
BlendingMode = BlendingMode.Additive,
Alpha = 0.2f,
}
}
}
@ -105,15 +157,15 @@ namespace osu.Game.Screens.Menu
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
logo.Texture = textures.Get(@"Menu/logo");
ripple.Texture = textures.Get(@"Menu/logo");
logo.Texture = textures.Get(@"Menu/logo@2x");
ripple.Texture = textures.Get(@"Menu/logo@2x");
}
protected override void LoadComplete()
{
base.LoadComplete();
ripple.ScaleTo(1.1f, 500);
ripple.ScaleTo(ripple.Scale * 1.1f, 500);
ripple.FadeOut(500);
ripple.Loop(300);
}
@ -122,14 +174,14 @@ namespace osu.Game.Screens.Menu
{
if (!Interactive) return false;
logoBounceContainer.ScaleTo(1.1f, 1000, EasingTypes.Out);
logoBounceContainer.ScaleTo(0.9f, 1000, EasingTypes.Out);
return true;
}
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
{
logoBounceContainer.ScaleTo(1.2f, 500, EasingTypes.OutElastic);
logoBounceContainer.ScaleTo(1f, 500, EasingTypes.OutElastic);
return true;
}
@ -144,13 +196,39 @@ namespace osu.Game.Screens.Menu
protected override bool OnHover(InputState state)
{
if (!Interactive) return false;
logoBounceContainer.ScaleTo(1.2f, 500, EasingTypes.OutElastic);
logoHoverContainer.ScaleTo(1.2f, 500, EasingTypes.OutElastic);
return true;
}
protected override void OnHoverLost(InputState state)
{
logoBounceContainer.ScaleTo(1, 500, EasingTypes.OutElastic);
logoHoverContainer.ScaleTo(1, 500, EasingTypes.OutElastic);
}
class OsuLogoTriangles : Triangles
{
public OsuLogoTriangles()
{
TriangleScale = 4;
Alpha = 1;
}
protected override Sprite CreateTriangle()
{
var triangle = base.CreateTriangle();
triangle.Alpha = 1;
triangle.Colour = getTriangleShade();
return triangle;
}
private Color4 getTriangleShade()
{
float val = RNG.NextSingle();
return Interpolation.ValueAt(val,
new Color4(222, 91, 149, 255),
new Color4(255, 125, 183, 255),
0, 1);
}
}
}
}

View File

@ -64,11 +64,14 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Beatmaps\Drawables\BeatmapBackgroundSprite.cs" />
<Compile Include="Graphics\Backgrounds\Triangles.cs" />
<Compile Include="Graphics\Cursor\CursorTrail.cs" />
<Compile Include="Graphics\UserInterface\BackButton.cs" />
<Compile Include="Modes\Objects\HitObjectParser.cs" />
<Compile Include="Modes\Score.cs" />
<Compile Include="Modes\ScoreProcesssor.cs" />
<Compile Include="Online\API\IOnlineComponent.cs" />
<Compile Include="Online\API\Requests\GetUserRequest.cs" />
<Compile Include="Overlays\DragBar.cs" />
<Compile Include="Overlays\MusicController.cs" />
<Compile Include="Beatmaps\Beatmap.cs" />
@ -89,6 +92,7 @@
<Compile Include="Beatmaps\Timing\SampleChange.cs" />
<Compile Include="Beatmaps\Timing\TimingChange.cs" />
<Compile Include="Configuration\OsuConfigManager.cs" />
<Compile Include="Overlays\Toolbar\ToolbarUserButton.cs" />
<Compile Include="Screens\BackgroundMode.cs" />
<Compile Include="Screens\Backgrounds\BackgroundModeBeatmap.cs" />
<Compile Include="Screens\Backgrounds\BackgroundModeCustom.cs" />
@ -150,18 +154,18 @@
<Compile Include="Online\API\SecurePassword.cs" />
<Compile Include="Online\API\Requests\ListChannels.cs" />
<Compile Include="Online\Chat\Channel.cs" />
<Compile Include="Online\Chat\Display\ChannelDisplay.cs" />
<Compile Include="Online\Chat\Display\ChatLine.cs" />
<Compile Include="Online\Chat\Drawables\ChannelDisplay.cs" />
<Compile Include="Online\Chat\Drawables\ChatLine.cs" />
<Compile Include="Online\Chat\Message.cs" />
<Compile Include="Online\User.cs" />
<Compile Include="OsuGame.cs" />
<Compile Include="OsuGameBase.cs" />
<Compile Include="Overlays\ChatConsole.cs" />
<Compile Include="Overlays\ChatOverlay.cs" />
<Compile Include="Overlays\OptionsOverlay.cs" />
<Compile Include="Overlays\Toolbar.cs" />
<Compile Include="Overlays\ToolbarButton.cs" />
<Compile Include="Overlays\ToolbarModeButton.cs" />
<Compile Include="Overlays\ToolbarModeSelector.cs" />
<Compile Include="Overlays\Toolbar\Toolbar.cs" />
<Compile Include="Overlays\Toolbar\ToolbarButton.cs" />
<Compile Include="Overlays\Toolbar\ToolbarModeButton.cs" />
<Compile Include="Overlays\Toolbar\ToolbarModeSelector.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Screens\Select\BeatmapInfoWedge.cs" />
<Compile Include="Users\User.cs" />