From dcd4b4450d4fa41d165e4714f22f6078ce80b4e2 Mon Sep 17 00:00:00 2001
From: Dean Herbert <pe@ppy.sh>
Date: Mon, 15 May 2017 13:26:35 +0900
Subject: [PATCH 1/6] Add error message in chat when attempting to use commands

---
 osu.Game/Online/Chat/Channel.cs      |  4 +-
 osu.Game/Online/Chat/ErrorMessage.cs | 25 ++++++++++
 osu.Game/Online/Chat/Message.cs      |  5 ++
 osu.Game/Overlays/ChatOverlay.cs     | 69 +++++++++++++++-------------
 osu.Game/osu.Game.csproj             |  1 +
 5 files changed, 71 insertions(+), 33 deletions(-)
 create mode 100644 osu.Game/Online/Chat/ErrorMessage.cs

diff --git a/osu.Game/Online/Chat/Channel.cs b/osu.Game/Online/Chat/Channel.cs
index d159f482a9..2925c3ccb4 100644
--- a/osu.Game/Online/Chat/Channel.cs
+++ b/osu.Game/Online/Chat/Channel.cs
@@ -38,9 +38,9 @@ namespace osu.Game.Online.Chat
 
         public event Action<IEnumerable<Message>> NewMessagesArrived;
 
-        public void AddNewMessages(IEnumerable<Message> messages)
+        public void AddNewMessages(params Message[] messages)
         {
-            messages = messages.Except(Messages).ToList();
+            messages = messages.Except(Messages).ToArray();
 
             Messages.AddRange(messages);
 
diff --git a/osu.Game/Online/Chat/ErrorMessage.cs b/osu.Game/Online/Chat/ErrorMessage.cs
new file mode 100644
index 0000000000..a410e9044a
--- /dev/null
+++ b/osu.Game/Online/Chat/ErrorMessage.cs
@@ -0,0 +1,25 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using osu.Game.Users;
+
+namespace osu.Game.Online.Chat
+{
+    public class ErrorMessage : Message
+    {
+        private static int errorId = -1;
+
+        public ErrorMessage(string message) : base(errorId--)
+        {
+            Timestamp = DateTime.Now;
+            Content = message;
+
+            Sender = new User
+            {
+                Username = @"system",
+                Colour = @"ff0000",
+            };
+        }
+    }
+}
\ No newline at end of file
diff --git a/osu.Game/Online/Chat/Message.cs b/osu.Game/Online/Chat/Message.cs
index 372e43be38..bf53a68910 100644
--- a/osu.Game/Online/Chat/Message.cs
+++ b/osu.Game/Online/Chat/Message.cs
@@ -37,6 +37,11 @@ namespace osu.Game.Online.Chat
         {
         }
 
+        public Message(long id)
+        {
+            Id = id;
+        }
+
         public override bool Equals(object obj)
         {
             var objMessage = obj as Message;
diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs
index 7c5651b5ff..d43d0b22aa 100644
--- a/osu.Game/Overlays/ChatOverlay.cs
+++ b/osu.Game/Overlays/ChatOverlay.cs
@@ -306,7 +306,7 @@ namespace osu.Game.Overlays
 
                 //batch messages per channel.
                 foreach (var id in ids)
-                    careChannels.Find(c => c.Id == id)?.AddNewMessages(messages.Where(m => m.TargetId == id));
+                    careChannels.Find(c => c.Id == id)?.AddNewMessages(messages.Where(m => m.TargetId == id).ToArray());
 
                 lastMessageId = messages.LastOrDefault()?.Id ?? lastMessageId;
 
@@ -326,38 +326,45 @@ namespace osu.Game.Overlays
         {
             var postText = textbox.Text;
 
-            if (!string.IsNullOrEmpty(postText) && api.LocalUser.Value != null)
+            if (string.IsNullOrEmpty(postText) || api.LocalUser.Value == null) return;
+
+            if (currentChannel == null) return;
+
+            if (postText[0] == '/')
             {
-                if (currentChannel == null) return;
-
-                var message = new Message
-                {
-                    Sender = api.LocalUser.Value,
-                    Timestamp = DateTimeOffset.Now,
-                    TargetType = TargetType.Channel, //TODO: read this from currentChannel
-                    TargetId = currentChannel.Id,
-                    Content = postText
-                };
-
-                textbox.ReadOnly = true;
-                var req = new PostMessageRequest(message);
-
-                req.Failure += e =>
-                {
-                    textbox.FlashColour(Color4.Red, 1000);
-                    textbox.ReadOnly = false;
-                };
-
-                req.Success += m =>
-                {
-                    currentChannel.AddNewMessages(new[] { m });
-
-                    textbox.ReadOnly = false;
-                    textbox.Text = string.Empty;
-                };
-
-                api.Queue(req);
+                // TODO: handle commands
+                currentChannel.AddNewMessages(new ErrorMessage("Chat commands are not supported yet!"));
+                textbox.Text = string.Empty;
+                return;
             }
+                
+            var message = new Message
+            {
+                Sender = api.LocalUser.Value,
+                Timestamp = DateTimeOffset.Now,
+                TargetType = TargetType.Channel, //TODO: read this from currentChannel
+                TargetId = currentChannel.Id,
+                Content = postText
+            };
+
+            textbox.ReadOnly = true;
+            var req = new PostMessageRequest(message);
+
+            req.Failure += e =>
+            {
+                textbox.FlashColour(Color4.Red, 1000);
+                textbox.ReadOnly = false;
+            };
+
+            req.Success += m =>
+            {
+                currentChannel.AddNewMessages(m);
+
+                textbox.ReadOnly = false;
+                textbox.Text = string.Empty;
+            };
+
+            api.Queue(req);
         }
     }
 }
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 1f866f0e17..919b2bb732 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -75,6 +75,7 @@
     <Compile Include="Beatmaps\Drawables\BeatmapBackgroundSprite.cs" />
     <Compile Include="Beatmaps\DifficultyCalculator.cs" />
     <Compile Include="Online\API\Requests\PostMessageRequest.cs" />
+    <Compile Include="Online\Chat\ErrorMessage.cs" />
     <Compile Include="Overlays\Chat\ChatTabControl.cs" />
     <Compile Include="Overlays\Music\FilterControl.cs" />
     <Compile Include="Overlays\Music\PlaylistItem.cs" />

From 3b1d5ce7df2881d981d507fb602f12ebefc600f9 Mon Sep 17 00:00:00 2001
From: Dean Herbert <pe@ppy.sh>
Date: Mon, 15 May 2017 13:31:51 +0900
Subject: [PATCH 2/6] CI fix

---
 osu.Game/Overlays/ChatOverlay.cs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs
index d43d0b22aa..111d3e5eb4 100644
--- a/osu.Game/Overlays/ChatOverlay.cs
+++ b/osu.Game/Overlays/ChatOverlay.cs
@@ -337,7 +337,7 @@ namespace osu.Game.Overlays
                 textbox.Text = string.Empty;
                 return;
             }
-                
+
             var message = new Message
             {
                 Sender = api.LocalUser.Value,

From 23760d6805251e7f499e196421ba91d47b760008 Mon Sep 17 00:00:00 2001
From: Dean Herbert <pe@ppy.sh>
Date: Mon, 15 May 2017 19:36:03 +0900
Subject: [PATCH 3/6] Adjust osu! logo metrics and add shockwave impact
 animation

---
 osu-resources                         |  2 +-
 osu.Game/Screens/Menu/ButtonSystem.cs |  6 ++++
 osu.Game/Screens/Menu/OsuLogo.cs      | 42 ++++++++++++++++++++++++---
 3 files changed, 45 insertions(+), 5 deletions(-)

diff --git a/osu-resources b/osu-resources
index b90c4ed490..ffccbeb98d 160000
--- a/osu-resources
+++ b/osu-resources
@@ -1 +1 @@
-Subproject commit b90c4ed490f76f2995662b3a8af3a32b8756a012
+Subproject commit ffccbeb98dc9e8f0965520270b5885e63f244c83
diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs
index 2f418d2f88..72db445052 100644
--- a/osu.Game/Screens/Menu/ButtonSystem.cs
+++ b/osu.Game/Screens/Menu/ButtonSystem.cs
@@ -245,8 +245,14 @@ namespace osu.Game.Screens.Menu
                         buttonArea.FadeIn(300);
 
                         if (lastState == MenuState.Initial)
+                        {
                             buttonArea.Delay(150, true);
 
+                            if (osuLogo.Scale.X > 0.5f)
+                                using (osuLogo.BeginDelayedSequence(200, true))
+                                    osuLogo.Impact();
+                        }
+
                         Scheduler.AddDelayed(() => toolbar?.Show(), 150);
 
                         foreach (Button b in buttonsTopLevel)
diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs
index f3fffedd43..e28adeacff 100644
--- a/osu.Game/Screens/Menu/OsuLogo.cs
+++ b/osu.Game/Screens/Menu/OsuLogo.cs
@@ -35,7 +35,7 @@ namespace osu.Game.Screens.Menu
 
         public Action Action;
 
-        public float SizeForFlow => logo == null ? 0 : logo.DrawSize.X * logo.Scale.X * logoBounceContainer.Scale.X * logoHoverContainer.Scale.X * 0.78f;
+        public float SizeForFlow => logo == null ? 0 : logo.DrawSize.X * logo.Scale.X * logoBounceContainer.Scale.X * logoHoverContainer.Scale.X * 0.74f;
 
         private readonly Sprite ripple;
 
@@ -63,8 +63,14 @@ namespace osu.Game.Screens.Menu
         public bool Interactive = true;
         private readonly Box flashLayer;
 
+        private readonly Container impactContainer;
+
+        private const float default_size = 480;
+
         public OsuLogo()
         {
+            Size = new Vector2(default_size);
+
             Anchor = Anchor.Centre;
             Origin = Anchor.Centre;
 
@@ -92,7 +98,7 @@ namespace osu.Game.Screens.Menu
                                             Anchor = Anchor.Centre,
                                             Origin = Anchor.Centre,
                                             RelativeSizeAxes = Axes.Both,
-                                            Scale = new Vector2(0.8f),
+                                            Scale = new Vector2(0.88f),
                                             Masking = true,
                                             Children = new Drawable[]
                                             {
@@ -137,6 +143,7 @@ namespace osu.Game.Screens.Menu
                                 {
                                     Anchor = Anchor.Centre,
                                     Origin = Anchor.Centre,
+                                    RelativeSizeAxes = Axes.Both,
                                     Children = new Drawable[]
                                     {
                                         ripple = new Sprite
@@ -148,11 +155,30 @@ namespace osu.Game.Screens.Menu
                                         }
                                     }
                                 },
+                                impactContainer = new CircularContainer
+                                {
+                                    Anchor = Anchor.Centre,
+                                    Origin = Anchor.Centre,
+                                    Alpha = 0,
+                                    BorderColour = Color4.White,
+                                    RelativeSizeAxes = Axes.Both,
+                                    BorderThickness = 10,
+                                    Masking = true,
+                                    Children = new Drawable[]
+                                    {
+                                        new Box
+                                        {
+                                            RelativeSizeAxes = Axes.Both,
+                                            AlwaysPresent = true,
+                                            Alpha = 0,
+                                        }
+                                    }
+                                },
                                 new MenuVisualisation
                                 {
                                     Anchor = Anchor.Centre,
                                     Origin = Anchor.Centre,
-                                    Size = logo.Size,
+                                    RelativeSizeAxes = Axes.Both,
                                     BlendingMode = BlendingMode.Additive,
                                     Alpha = 0.2f,
                                 }
@@ -211,7 +237,8 @@ namespace osu.Game.Screens.Menu
         protected override bool OnHover(InputState state)
         {
             if (!Interactive) return false;
-            logoHoverContainer.ScaleTo(1.2f, 500, EasingTypes.OutElastic);
+
+            logoHoverContainer.ScaleTo(1.1f, 500, EasingTypes.OutElastic);
             return true;
         }
 
@@ -219,5 +246,12 @@ namespace osu.Game.Screens.Menu
         {
             logoHoverContainer.ScaleTo(1, 500, EasingTypes.OutElastic);
         }
+
+        public void Impact()
+        {
+            impactContainer.FadeOutFromOne(250, EasingTypes.In);
+            impactContainer.ScaleTo(0.96f);
+            impactContainer.ScaleTo(1.12f, 250);
+        }
     }
 }
\ No newline at end of file

From 696c53646d6969e85304b113c6c81be23280b2f7 Mon Sep 17 00:00:00 2001
From: Dean Herbert <pe@ppy.sh>
Date: Tue, 16 May 2017 12:51:07 +0900
Subject: [PATCH 4/6] Change severity of unaccessed private collections to hint

---
 osu.sln.DotSettings | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings
index 03d9e34805..70bfacd6ef 100644
--- a/osu.sln.DotSettings
+++ b/osu.sln.DotSettings
@@ -20,6 +20,8 @@
 	<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ClassNeverInstantiated_002EGlobal/@EntryIndexedValue">HINT</s:String>
 	<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ClassNeverInstantiated_002ELocal/@EntryIndexedValue">WARNING</s:String>
 	<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ClassWithVirtualMembersNeverInherited_002EGlobal/@EntryIndexedValue">HINT</s:String>
+	<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=CollectionNeverQueried_002EGlobal/@EntryIndexedValue">SUGGESTION</s:String>
+	<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=CollectionNeverQueried_002ELocal/@EntryIndexedValue">HINT</s:String>
 	<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=CompareOfFloatsByEqualityOperator/@EntryIndexedValue">HINT</s:String>
 	<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertClosureToMethodGroup/@EntryIndexedValue">WARNING</s:String>
 	<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertIfDoToWhile/@EntryIndexedValue">WARNING</s:String>

From e09f1c7c918cbe3ccb1770170178294cd585fc37 Mon Sep 17 00:00:00 2001
From: Dean Herbert <pe@ppy.sh>
Date: Tue, 16 May 2017 12:51:43 +0900
Subject: [PATCH 5/6] Remove AudioDevice from game-level setting

Should be (and is already)  in FrameworkConfig.
---
 osu.Game/Configuration/OsuConfigManager.cs | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs
index 43b0456973..8f177d6b56 100644
--- a/osu.Game/Configuration/OsuConfigManager.cs
+++ b/osu.Game/Configuration/OsuConfigManager.cs
@@ -39,8 +39,6 @@ namespace osu.Game.Configuration
 
             // Audio
 
-            Set(OsuSetting.AudioDevice, string.Empty);
-
             Set(OsuSetting.MenuVoice, true);
             Set(OsuSetting.MenuMusic, true);
 
@@ -99,7 +97,6 @@ namespace osu.Game.Configuration
         MenuParallax,
         BeatmapDetailTab,
         Username,
-        AudioDevice,
         ReleaseStream,
         SavePassword,
         SaveUsername,

From f0ea445e4696082bf6a906f7762eeb7e5e1ee825 Mon Sep 17 00:00:00 2001
From: Dean Herbert <pe@ppy.sh>
Date: Tue, 16 May 2017 15:56:53 +0900
Subject: [PATCH 6/6] Fix initial requests of channel mesages not being
 performed

fetchReq was being set even for initial lookups, which caused only one to run, and subsequent requests to be excessive to try and catch up.
---
 osu.Game/Overlays/ChatOverlay.cs | 25 +++++++++++++++++++++----
 1 file changed, 21 insertions(+), 4 deletions(-)

diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs
index 111d3e5eb4..5bbf20e196 100644
--- a/osu.Game/Overlays/ChatOverlay.cs
+++ b/osu.Game/Overlays/ChatOverlay.cs
@@ -245,7 +245,7 @@ namespace osu.Game.Overlays
                     addChannel(channels.Find(c => c.Name == @"#lobby"));
                 });
 
-                messageRequest = Scheduler.AddDelayed(() => fetchNewMessages(), 1000, true);
+                messageRequest = Scheduler.AddDelayed(fetchNewMessages, 1000, true);
             };
 
             api.Queue(req);
@@ -289,17 +289,34 @@ namespace osu.Game.Overlays
             channelTabs.AddItem(channel);
 
             // we need to get a good number of messages initially for each channel we care about.
-            fetchNewMessages(channel);
+            fetchInitialMessages(channel);
 
             if (CurrentChannel == null)
                 CurrentChannel = channel;
         }
 
-        private void fetchNewMessages(Channel specificChannel = null)
+        private void fetchInitialMessages(Channel channel)
+        {
+            var req = new GetMessagesRequest(new List<Channel> { channel }, null);
+
+            req.Success += delegate (List<Message> messages)
+            {
+                channel.AddNewMessages(messages.ToArray());
+                Debug.Write("success!");
+            };
+            req.Failure += delegate
+            {
+                Debug.Write("failure!");
+            };
+
+            api.Queue(req);
+        }
+
+        private void fetchNewMessages()
         {
             if (fetchReq != null) return;
 
-            fetchReq = new GetMessagesRequest(specificChannel != null ? new List<Channel> { specificChannel } : careChannels, lastMessageId);
+            fetchReq = new GetMessagesRequest(careChannels, lastMessageId);
             fetchReq.Success += delegate (List<Message> messages)
             {
                 var ids = messages.Where(m => m.TargetType == TargetType.Channel).Select(m => m.TargetId).Distinct();