1
0
mirror of https://github.com/ppy/osu.git synced 2025-03-05 13:13:22 +08:00

Merge branch 'timeshift-api-integration'

This commit is contained in:
smoogipoo 2018-12-13 17:07:39 +09:00
commit 2aafaf9ce2
20 changed files with 563 additions and 720 deletions

View File

@ -1,135 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Multiplayer;
using osu.Game.Rulesets;
using osu.Game.Screens.Multi.Lounge.Components;
using osu.Game.Users;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseDrawableRoom : OsuTestCase
{
private RulesetStore rulesets;
protected override void LoadComplete()
{
base.LoadComplete();
DrawableRoom first;
Add(new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Y,
Width = 580f,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
first = new DrawableRoom(new Room
{
Name = { Value = @"Great Room Right Here" },
Host = { Value = new User { Username = @"Naeferith", Id = 9492835, Country = new Country { FlagName = @"FR" } } },
Status = { Value = new RoomStatusOpen() },
Type = { Value = new GameTypeTeamVersus() },
Beatmap =
{
Value = new BeatmapInfo
{
StarDifficulty = 4.65,
Ruleset = rulesets.GetRuleset(3),
Metadata = new BeatmapMetadata
{
Title = @"Critical Crystal",
Artist = @"Seiryu",
},
BeatmapSet = new BeatmapSetInfo
{
OnlineInfo = new BeatmapSetOnlineInfo
{
Covers = new BeatmapSetOnlineCovers
{
Cover = @"https://assets.ppy.sh//beatmaps/376340/covers/cover.jpg?1456478455",
},
},
},
},
},
Participants =
{
Value = new[]
{
new User { Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 1355 } } },
new User { Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 8756 } } },
},
},
}),
new DrawableRoom(new Room
{
Name = { Value = @"Relax It's The Weekend" },
Host = { Value = new User { Username = @"peppy", Id = 2, Country = new Country { FlagName = @"AU" } } },
Status = { Value = new RoomStatusPlaying() },
Type = { Value = new GameTypeTagTeam() },
Beatmap =
{
Value = new BeatmapInfo
{
StarDifficulty = 1.96,
Ruleset = rulesets.GetRuleset(0),
Metadata = new BeatmapMetadata
{
Title = @"Serendipity",
Artist = @"ZAQ",
},
BeatmapSet = new BeatmapSetInfo
{
OnlineInfo = new BeatmapSetOnlineInfo
{
Covers = new BeatmapSetOnlineCovers
{
Cover = @"https://assets.ppy.sh//beatmaps/526839/covers/cover.jpg?1493815706",
},
},
},
},
},
Participants =
{
Value = new[]
{
new User { Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 578975 } } },
new User { Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 24554 } } },
},
},
}),
}
});
AddStep(@"select", () => first.State = SelectionState.Selected);
AddStep(@"change title", () => first.Room.Name.Value = @"I Changed Name");
AddStep(@"change host", () => first.Room.Host.Value = new User { Username = @"DrabWeb", Id = 6946022, Country = new Country { FlagName = @"CA" } });
AddStep(@"change status", () => first.Room.Status.Value = new RoomStatusPlaying());
AddStep(@"change type", () => first.Room.Type.Value = new GameTypeVersus());
AddStep(@"change beatmap", () => first.Room.Beatmap.Value = null);
AddStep(@"change participants", () => first.Room.Participants.Value = new[]
{
new User { Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 1254 } } },
new User { Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 123189 } } },
});
AddStep(@"deselect", () => first.State = SelectionState.NotSelected);
}
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
{
this.rulesets = rulesets;
}
}
}

View File

@ -1,208 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Online.Multiplayer;
using osu.Game.Rulesets;
using osu.Game.Screens;
using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Multi;
using osu.Game.Screens.Multi.Lounge;
using osu.Game.Screens.Multi.Lounge.Components;
using osu.Game.Users;
using osuTK.Input;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseLounge : ManualInputManagerTestCase
{
private TestLoungeScreen loungeScreen;
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
{
loungeScreen = new TestLoungeScreen();
Room[] rooms =
{
new Room
{
Name = { Value = @"Just Another Room" },
Host = { Value = new User { Username = @"DrabWeb", Id = 6946022, Country = new Country { FlagName = @"CA" } } },
Status = { Value = new RoomStatusPlaying() },
Availability = { Value = RoomAvailability.Public },
Type = { Value = new GameTypeTagTeam() },
Beatmap =
{
Value = new BeatmapInfo
{
StarDifficulty = 5.65,
Ruleset = rulesets.GetRuleset(0),
Metadata = new BeatmapMetadata
{
Title = @"Sidetracked Day (Short Ver.)",
Artist = @"VINXIS",
AuthorString = @"Hobbes2",
},
BeatmapSet = new BeatmapSetInfo
{
OnlineInfo = new BeatmapSetOnlineInfo
{
Covers = new BeatmapSetOnlineCovers
{
Cover = @"https://assets.ppy.sh/beatmaps/767600/covers/cover.jpg?1526243446",
},
},
},
}
},
MaxParticipants = { Value = 10 },
Participants =
{
Value = new[]
{
new User { Username = @"flyte", Id = 3103765, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 142 } } },
new User { Username = @"Cookiezi", Id = 124493, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 546 } } },
new User { Username = @"Angelsim", Id = 1777162, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 287 } } },
new User { Username = @"Rafis", Id = 2558286, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 468 } } },
new User { Username = @"hvick225", Id = 50265, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 325 } } },
new User { Username = @"peppy", Id = 2, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 625 } } },
}
}
},
new Room
{
Name = { Value = @"Not Just Any Room" },
Host = { Value = new User { Username = @"Monstrata", Id = 2706438, Country = new Country { FlagName = @"CA" } } },
Status = { Value = new RoomStatusOpen() },
Availability = { Value = RoomAvailability.FriendsOnly },
Type = { Value = new GameTypeTeamVersus() },
Beatmap =
{
Value = new BeatmapInfo
{
StarDifficulty = 2.73,
Ruleset = rulesets.GetRuleset(0),
Metadata = new BeatmapMetadata
{
Title = @"lit(var)",
Artist = @"kensuke ushio",
AuthorString = @"Monstrata",
},
BeatmapSet = new BeatmapSetInfo
{
OnlineInfo = new BeatmapSetOnlineInfo
{
Covers = new BeatmapSetOnlineCovers
{
Cover = @"https://assets.ppy.sh/beatmaps/623972/covers/cover.jpg?1521167183",
},
},
},
}
},
Participants =
{
Value = new[]
{
new User { Username = @"Jeby", Id = 3136279, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 3497 } } },
new User { Username = @"DualAkira", Id = 5220933, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 643 } } },
new User { Username = @"Datenshi Yohane", Id = 7171857, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 10555 } } },
}
}
},
new Room
{
Name = { Value = @"room THE FINAL" },
Host = { Value = new User { Username = @"Delis", Id = 1603923, Country = new Country { FlagName = @"JP" } } },
Status = { Value = new RoomStatusPlaying() },
Availability = { Value = RoomAvailability.Public },
Type = { Value = new GameTypeTagTeam() },
Beatmap =
{
Value = new BeatmapInfo
{
StarDifficulty = 4.48,
Ruleset = rulesets.GetRuleset(3),
Metadata = new BeatmapMetadata
{
Title = @"ONIGIRI FREEWAY",
Artist = @"OISHII",
AuthorString = @"Mentholzzz",
},
BeatmapSet = new BeatmapSetInfo
{
OnlineInfo = new BeatmapSetOnlineInfo
{
Covers = new BeatmapSetOnlineCovers
{
Cover = @"https://assets.ppy.sh/beatmaps/663098/covers/cover.jpg?1521898837",
},
},
},
}
},
MaxParticipants = { Value = 30 },
Participants =
{
Value = new[]
{
new User { Username = @"KizuA", Id = 6510442, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 5372 } } },
new User { Username = @"Colored", Id = 827563, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 810 } } },
new User { Username = @"Beryl", Id = 3817591, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 10096 } } },
}
}
},
};
AddStep(@"show", () => Add(loungeScreen));
selectAssert(0);
AddAssert(@"no room selected", () => loungeScreen.SelectedRoom == null);
selectAssert(1);
AddStep(@"open room 1", () => clickRoom(1));
AddUntilStep(() => loungeScreen.ChildScreen?.IsCurrentScreen == true, "wait until room current");
AddStep(@"make lounge current", loungeScreen.MakeCurrent);
filterAssert(@"THE FINAL", LoungeTab.Public, 1);
filterAssert(string.Empty, LoungeTab.Public, 2);
filterAssert(string.Empty, LoungeTab.Private, 1);
filterAssert(string.Empty, LoungeTab.Public, 2);
filterAssert(@"no matches", LoungeTab.Public, 0);
filterAssert(string.Empty, LoungeTab.Public, 2);
AddStep(@"exit", loungeScreen.Exit);
}
private void clickRoom(int n)
{
InputManager.Click(MouseButton.Left);
}
private void selectAssert(int n)
{
AddStep($@"select room {n}", () => clickRoom(n));
}
private void filterAssert(string filter, LoungeTab tab, int endCount)
{
AddStep($@"filter '{filter}', {tab}", () => loungeScreen.SetFilter(filter, tab));
}
private class TestLoungeScreen : LoungeScreen
{
protected override BackgroundScreen CreateBackground() => new BackgroundScreenDefault();
[Resolved]
private RoomManager manager { get; set; }
public Room SelectedRoom => manager.Current.Value;
public void SetFilter(string filter, LoungeTab tab)
{
Filter.Search.Current.Value = filter;
Filter.Tabs.Current.Value = tab;
}
}
}
}

View File

@ -1,152 +0,0 @@
// Copyright (c) 2007-2018 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 NUnit.Framework;
using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Online.Multiplayer;
using osu.Game.Rulesets;
using osu.Game.Screens.Multi.Match;
using osu.Game.Screens.Multi.Match.Components;
using osu.Game.Users;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseMatch : OsuTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(TestCaseMatch),
typeof(GameTypePicker),
typeof(RoomSettingsOverlay)
};
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
{
Room room = new Room
{
Name = { Value = @"One Awesome Room" },
Status = { Value = new RoomStatusOpen() },
Availability = { Value = RoomAvailability.Public },
Type = { Value = new GameTypeTeamVersus() },
Beatmap =
{
Value = new BeatmapInfo
{
StarDifficulty = 5.02,
Ruleset = rulesets.GetRuleset(1),
Metadata = new BeatmapMetadata
{
Title = @"Paradigm Shift",
Artist = @"Morimori Atsushi",
AuthorString = @"eiri-",
},
BeatmapSet = new BeatmapSetInfo
{
OnlineInfo = new BeatmapSetOnlineInfo
{
Covers = new BeatmapSetOnlineCovers
{
Cover = @"https://assets.ppy.sh/beatmaps/765055/covers/cover.jpg?1526955337",
},
},
},
},
},
MaxParticipants = { Value = 5 },
Participants =
{
Value = new[]
{
new User
{
Username = @"eiri-",
Id = 3388410,
Country = new Country { FlagName = @"US" },
CoverUrl = @"https://assets.ppy.sh/user-profile-covers/3388410/00a8486a247831e1cc4375db519f611ac970bda8bc0057d78b0f540ea38c3e58.jpeg",
IsSupporter = true,
},
new User
{
Username = @"Nepuri",
Id = 6637817,
Country = new Country { FlagName = @"DE" },
CoverUrl = @"https://assets.ppy.sh/user-profile-covers/6637817/9085fc60248b6b5327a72c1dcdecf2dbedba810ae0ab6bcf7224e46b1339632a.jpeg",
IsSupporter = true,
},
new User
{
Username = @"goheegy",
Id = 8057655,
Country = new Country { FlagName = @"GB" },
CoverUrl = @"https://assets.ppy.sh/user-profile-covers/8057655/21cec27c25a11dc197a4ec6a74253dbabb495949b0e0697113352f12007018c5.jpeg",
},
new User
{
Username = @"Alumetri",
Id = 5371497,
Country = new Country { FlagName = @"RU" },
CoverUrl = @"https://assets.ppy.sh/user-profile-covers/5371497/e023b8c7fbe3613e64bd4856703517ea50fbed8a5805dc9acda9efe9897c67e2.jpeg",
},
}
},
};
MatchScreen matchScreen = new MatchScreen(room);
AddStep(@"show", () => Add(matchScreen));
AddStep(@"null beatmap", () => room.Beatmap.Value = null);
AddStep(@"change name", () => room.Name.Value = @"Two Awesome Rooms");
AddStep(@"change status", () => room.Status.Value = new RoomStatusPlaying());
AddStep(@"change availability", () => room.Availability.Value = RoomAvailability.FriendsOnly);
AddStep(@"change type", () => room.Type.Value = new GameTypeTag());
AddStep(@"change beatmap", () => room.Beatmap.Value = new BeatmapInfo
{
StarDifficulty = 4.33,
Ruleset = rulesets.GetRuleset(2),
Metadata = new BeatmapMetadata
{
Title = @"Yasashisa no Riyuu",
Artist = @"ChouCho",
AuthorString = @"celerih",
},
BeatmapSet = new BeatmapSetInfo
{
OnlineInfo = new BeatmapSetOnlineInfo
{
Covers = new BeatmapSetOnlineCovers
{
Cover = @"https://assets.ppy.sh/beatmaps/685391/covers/cover.jpg?1524597970",
},
},
},
});
AddStep(@"null max participants", () => room.MaxParticipants.Value = null);
AddStep(@"change participants", () => room.Participants.Value = new[]
{
new User
{
Username = @"Spectator",
Id = 702598,
Country = new Country { FlagName = @"KR" },
CoverUrl = @"https://assets.ppy.sh/user-profile-covers/702598/3bbf4cb8b8d2cf8b03145000a975ff27e191ab99b0920832e7dd67386280e288.jpeg",
IsSupporter = true,
},
new User
{
Username = @"celerih",
Id = 4696296,
Country = new Country { FlagName = @"CA" },
CoverUrl = @"https://assets.ppy.sh/user-profile-covers/4696296/7f8500731d0ac66d5472569d146a7be07d9460273361913f22c038867baddaef.jpeg",
},
});
AddStep(@"exit", matchScreen.Exit);
}
}
}

View File

@ -0,0 +1,144 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Threading.Tasks;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Logging;
using osu.Game.Graphics.Sprites;
using osu.Game.Online;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Tests.Visual
{
public class TestCasePollingComponent : OsuTestCase
{
private Container pollBox;
private TestPoller poller;
private const float safety_adjust = 1f;
private int count;
[SetUp]
public void SetUp() => Schedule(() =>
{
count = 0;
Children = new Drawable[]
{
pollBox = new Container
{
Alpha = 0,
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new Box
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Scale = new Vector2(0.4f),
Colour = Color4.LimeGreen,
RelativeSizeAxes = Axes.Both,
},
new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Text = "Poll!",
}
}
}
};
});
//[Test]
public void TestInstantPolling()
{
createPoller(true);
AddStep("set poll interval to 1", () => poller.TimeBetweenPolls = TimePerAction * safety_adjust);
checkCount(1);
checkCount(2);
checkCount(3);
AddStep("set poll interval to 5", () => poller.TimeBetweenPolls = TimePerAction * safety_adjust * 5);
checkCount(4);
checkCount(4);
checkCount(4);
skip();
checkCount(5);
checkCount(5);
AddStep("set poll interval to 1", () => poller.TimeBetweenPolls = TimePerAction * safety_adjust);
checkCount(6);
checkCount(7);
}
[Test]
public void TestSlowPolling()
{
createPoller(false);
AddStep("set poll interval to 1", () => poller.TimeBetweenPolls = TimePerAction * safety_adjust * 5);
checkCount(0);
skip();
checkCount(0);
skip();
skip();
skip();
skip();
checkCount(0);
skip();
skip();
checkCount(0);
}
private void skip() => AddStep("skip", () =>
{
// could be 4 or 5 at this point due to timing discrepancies (safety_adjust @ 0.2 * 5 ~= 1)
// easiest to just ignore the value at this point and move on.
});
private void checkCount(int checkValue)
{
Logger.Log($"value is {count}");
AddAssert($"count is {checkValue}", () => count == checkValue);
}
private void createPoller(bool instant) => AddStep("create poller", () =>
{
poller?.Expire();
Add(poller = instant ? new TestPoller() : new TestSlowPoller());
poller.OnPoll += () =>
{
pollBox.FadeOutFromOne(500);
count++;
};
});
protected override double TimePerAction => 5000;
public class TestPoller : PollingComponent
{
public event Action OnPoll;
protected override Task Poll()
{
Schedule(() => OnPoll?.Invoke());
return base.Poll();
}
}
public class TestSlowPoller : TestPoller
{
protected override Task Poll() => Task.Delay((int)(TimeBetweenPolls / 2f / Clock.Rate)).ContinueWith(_ => base.Poll());
}
}
}

View File

@ -1,141 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Online.Multiplayer;
using osu.Game.Rulesets;
using osu.Game.Screens.Multi.Lounge.Components;
using osu.Game.Users;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseRoomInspector : OsuTestCase
{
private RulesetStore rulesets;
protected override void LoadComplete()
{
base.LoadComplete();
Room room = new Room
{
Name = { Value = @"My Awesome Room" },
Host = { Value = new User { Username = @"flyte", Id = 3103765, Country = new Country { FlagName = @"JP" } } },
Status = { Value = new RoomStatusOpen() },
Type = { Value = new GameTypeTeamVersus() },
Beatmap =
{
Value = new BeatmapInfo
{
StarDifficulty = 3.7,
Ruleset = rulesets.GetRuleset(3),
Metadata = new BeatmapMetadata
{
Title = @"Platina",
Artist = @"Maaya Sakamoto",
AuthorString = @"uwutm8",
},
BeatmapSet = new BeatmapSetInfo
{
OnlineInfo = new BeatmapSetOnlineInfo
{
Covers = new BeatmapSetOnlineCovers
{
Cover = @"https://assets.ppy.sh/beatmaps/560573/covers/cover.jpg?1492722343",
},
},
},
}
},
MaxParticipants = { Value = 200 },
Participants =
{
Value = new[]
{
new User { Username = @"flyte", Id = 3103765, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 142 } } },
new User { Username = @"Cookiezi", Id = 124493, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 546 } } },
new User { Username = @"Angelsim", Id = 1777162, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 287 } } },
new User { Username = @"Rafis", Id = 2558286, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 468 } } },
new User { Username = @"hvick225", Id = 50265, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 325 } } },
new User { Username = @"peppy", Id = 2, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 625 } } },
}
}
};
Add(new RoomInspector
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Width = 0.5f,
});
AddStep(@"change title", () => room.Name.Value = @"A Better Room Than The Above");
AddStep(@"change host", () => room.Host.Value = new User { Username = @"DrabWeb", Id = 6946022, Country = new Country { FlagName = @"CA" } });
AddStep(@"change status", () => room.Status.Value = new RoomStatusPlaying());
AddStep(@"change type", () => room.Type.Value = new GameTypeTag());
AddStep(@"change beatmap", () => room.Beatmap.Value = null);
AddStep(@"change max participants", () => room.MaxParticipants.Value = null);
AddStep(@"change participants", () => room.Participants.Value = new[]
{
new User { Username = @"filsdelama", Id = 2831793, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 854 } } },
new User { Username = @"_index", Id = 652457, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 150 } } }
});
AddStep(@"change room", () =>
{
Room newRoom = new Room
{
Name = { Value = @"My New, Better Than Ever Room" },
Host = { Value = new User { Username = @"Angelsim", Id = 1777162, Country = new Country { FlagName = @"KR" } } },
Status = { Value = new RoomStatusOpen() },
Type = { Value = new GameTypeTagTeam() },
Beatmap =
{
Value = new BeatmapInfo
{
StarDifficulty = 7.07,
Ruleset = rulesets.GetRuleset(0),
Metadata = new BeatmapMetadata
{
Title = @"FREEDOM DIVE",
Artist = @"xi",
AuthorString = @"Nakagawa-Kanon",
},
BeatmapSet = new BeatmapSetInfo
{
OnlineInfo = new BeatmapSetOnlineInfo
{
Covers = new BeatmapSetOnlineCovers
{
Cover = @"https://assets.ppy.sh/beatmaps/39804/covers/cover.jpg?1456506845",
},
},
},
},
},
MaxParticipants = { Value = 10 },
Participants =
{
Value = new[]
{
new User { Username = @"Angelsim", Id = 1777162, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 4 } } },
new User { Username = @"HappyStick", Id = 256802, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 752 } } },
new User { Username = @"-Konpaku-", Id = 2258797, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 571 } } }
}
}
};
});
}
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
{
this.rulesets = rulesets;
}
}
}

View File

@ -9,21 +9,28 @@ using osu.Framework.Graphics.Sprites;
namespace osu.Game.Beatmaps.Drawables
{
public class UpdateableBeatmapBackgroundSprite : ModelBackedDrawable<WorkingBeatmap>
public class UpdateableBeatmapBackgroundSprite : ModelBackedDrawable<BeatmapInfo>
{
public readonly IBindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
public readonly IBindable<BeatmapInfo> Beatmap = new Bindable<BeatmapInfo>();
[Resolved]
private OsuGameBase game { get; set; }
private BeatmapManager beatmaps { get; set; }
public UpdateableBeatmapBackgroundSprite()
{
Beatmap.BindValueChanged(b => Schedule(() => Model = b));
}
protected override Drawable CreateDrawable(WorkingBeatmap model)
protected override Drawable CreateDrawable(BeatmapInfo model)
{
Drawable drawable = model == null ? (Drawable)new DefaultSprite() : new BeatmapBackgroundSprite(model);
Drawable drawable;
if (model == null)
drawable = new DefaultSprite();
else if (model.BeatmapSet?.OnlineInfo != null)
drawable = new BeatmapSetCover(model.BeatmapSet);
else
drawable = new BeatmapBackgroundSprite(beatmaps.GetWorkingBeatmap(model));
drawable.RelativeSizeAxes = Axes.Both;
drawable.Anchor = Anchor.Centre;

View File

@ -40,7 +40,7 @@ namespace osu.Game.Online.API
/// </summary>
public int Timeout = WebRequest.DEFAULT_TIMEOUT;
protected virtual string Target => string.Empty;
protected abstract string Target { get; }
protected virtual WebRequest CreateWebRequest() => new WebRequest(Uri);

View File

@ -67,11 +67,7 @@ namespace osu.Game.Online.API.Requests.Responses
OnlineBeatmapID = OnlineBeatmapID,
Version = version,
Status = Status,
BeatmapSet = new BeatmapSetInfo
{
OnlineBeatmapSetID = OnlineBeatmapSetID,
Status = BeatmapSet?.Status ?? BeatmapSetOnlineStatus.None
},
BeatmapSet = BeatmapSet.ToBeatmapSet(rulesets),
BaseDifficulty = new BeatmapDifficulty
{
DrainRate = drainRate,

View File

@ -4,11 +4,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Logging;
using osu.Framework.Threading;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Users;
@ -18,7 +17,7 @@ namespace osu.Game.Online.Chat
/// <summary>
/// Manages everything channel related
/// </summary>
public class ChannelManager : Component, IOnlineComponent
public class ChannelManager : PollingComponent
{
/// <summary>
/// The channels the player joins on startup
@ -49,11 +48,14 @@ namespace osu.Game.Online.Chat
public IBindableCollection<Channel> AvailableChannels => availableChannels;
private IAPIProvider api;
private ScheduledDelegate fetchMessagesScheduleder;
public readonly BindableBool HighPollRate = new BindableBool();
public ChannelManager()
{
CurrentChannel.ValueChanged += currentChannelChanged;
HighPollRate.BindValueChanged(high => TimeBetweenPolls = high ? 1000 : 6000, true);
}
/// <summary>
@ -360,73 +362,60 @@ namespace osu.Game.Online.Chat
}
}
public void APIStateChanged(APIAccess api, APIState state)
{
switch (state)
{
case APIState.Online:
fetchUpdates();
break;
default:
fetchMessagesScheduleder?.Cancel();
fetchMessagesScheduleder = null;
break;
}
}
private long lastMessageId;
private const int update_poll_interval = 1000;
private bool channelsInitialised;
private void fetchUpdates()
protected override Task Poll()
{
fetchMessagesScheduleder?.Cancel();
fetchMessagesScheduleder = Scheduler.AddDelayed(() =>
if (!api.IsLoggedIn)
return base.Poll();
var fetchReq = new GetUpdatesRequest(lastMessageId);
var tcs = new TaskCompletionSource<bool>();
fetchReq.Success += updates =>
{
var fetchReq = new GetUpdatesRequest(lastMessageId);
fetchReq.Success += updates =>
if (updates?.Presence != null)
{
if (updates?.Presence != null)
foreach (var channel in updates.Presence)
{
foreach (var channel in updates.Presence)
{
// we received this from the server so should mark the channel already joined.
JoinChannel(channel, true);
}
//todo: handle left channels
handleChannelMessages(updates.Messages);
foreach (var group in updates.Messages.GroupBy(m => m.ChannelId))
JoinedChannels.FirstOrDefault(c => c.Id == group.Key)?.AddNewMessages(group.ToArray());
lastMessageId = updates.Messages.LastOrDefault()?.Id ?? lastMessageId;
// we received this from the server so should mark the channel already joined.
JoinChannel(channel, true);
}
if (!channelsInitialised)
{
channelsInitialised = true;
// we want this to run after the first presence so we can see if the user is in any channels already.
initializeChannels();
}
//todo: handle left channels
fetchUpdates();
};
handleChannelMessages(updates.Messages);
fetchReq.Failure += delegate { fetchUpdates(); };
foreach (var group in updates.Messages.GroupBy(m => m.ChannelId))
JoinedChannels.FirstOrDefault(c => c.Id == group.Key)?.AddNewMessages(group.ToArray());
api.Queue(fetchReq);
}, update_poll_interval);
lastMessageId = updates.Messages.LastOrDefault()?.Id ?? lastMessageId;
}
if (!channelsInitialised)
{
channelsInitialised = true;
// we want this to run after the first presence so we can see if the user is in any channels already.
initializeChannels();
}
tcs.SetResult(true);
};
fetchReq.Failure += _ => tcs.SetResult(false);
api.Queue(fetchReq);
return tcs.Task;
}
[BackgroundDependencyLoader]
private void load(IAPIProvider api)
{
this.api = api;
api.Register(this);
}
}

View File

@ -3,23 +3,154 @@
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using osu.Framework.Configuration;
using osu.Game.Beatmaps;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Users;
namespace osu.Game.Online.Multiplayer
{
public class Room
{
public Bindable<string> Name = new Bindable<string>("My awesome room!");
public Bindable<User> Host = new Bindable<User>();
[JsonProperty("id")]
public Bindable<int?> RoomID { get; } = new Bindable<int?>();
[JsonIgnore]
public readonly Bindable<BeatmapInfo> Beatmap = new Bindable<BeatmapInfo>();
[JsonProperty("name")]
public readonly Bindable<string> Name = new Bindable<string>("My awesome room!");
[JsonProperty("host")]
public readonly Bindable<User> Host = new Bindable<User>();
public bool ShouldSerializeHost() => false;
[JsonProperty("playlist")]
public readonly BindableCollection<PlaylistItem> Playlist = new BindableCollection<PlaylistItem>();
[JsonProperty("duration")]
public readonly Bindable<int> Duration = new Bindable<int>(100);
[JsonIgnore]
public readonly Bindable<int?> MaxAttempts = new Bindable<int?>();
// Todo: Find a better way to do this (https://github.com/ppy/osu-framework/issues/1930)
[JsonProperty("max_attempts", DefaultValueHandling = DefaultValueHandling.Ignore)]
private int? maxAttempts
{
get => MaxAttempts;
set => MaxAttempts.Value = value;
}
public Bindable<RoomStatus> Status = new Bindable<RoomStatus>(new RoomStatusOpen());
public Bindable<RoomAvailability> Availability = new Bindable<RoomAvailability>();
public Bindable<GameType> Type = new Bindable<GameType>(new GameTypeTimeshift());
public Bindable<BeatmapInfo> Beatmap = new Bindable<BeatmapInfo>();
public Bindable<int?> MaxParticipants = new Bindable<int?>();
public Bindable<IEnumerable<User>> Participants = new Bindable<IEnumerable<User>>(Enumerable.Empty<User>());
public Bindable<bool> Created = new Bindable<bool>();
public Room()
{
Beatmap.BindValueChanged(b =>
{
Playlist.Clear();
Playlist.Add(new PlaylistItem { Beatmap = b });
});
}
public void CopyFrom(Room other)
{
RoomID.Value = other.RoomID;
Name.Value = other.Name;
Host.Value = other.Host;
Status.Value = other.Status;
Availability.Value = other.Availability;
Type.Value = other.Type;
MaxParticipants.Value = other.MaxParticipants;
Participants.Value = other.Participants.Value.ToArray();
}
}
public class PlaylistItem
{
[JsonProperty("beatmap")]
private APIBeatmap beatmap { get; set; }
public bool ShouldSerializebeatmap() => false;
[JsonIgnore]
public BeatmapInfo Beatmap { get; set; }
[JsonProperty("beatmap_id")]
public int BeatmapID => 847296; //Beatmap.OnlineBeatmapID ?? 0;
[JsonProperty("ruleset_id")]
public int RulesetID { get; set; }
[JsonIgnore]
public readonly BindableCollection<Mod> AllowedMods = new BindableCollection<Mod>();
[JsonIgnore]
public readonly BindableCollection<Mod> RequiredMods = new BindableCollection<Mod>();
private APIMod[] _allowedMods;
[JsonProperty("allowed_mods")]
private APIMod[] allowedMods
{
get => AllowedMods.Select(m => new APIMod { Acronym = m.Acronym }).ToArray();
set => _allowedMods = value;
}
private APIMod[] _requiredMods;
[JsonProperty("required_mods")]
private APIMod[] requiredMods
{
get => RequiredMods.Select(m => new APIMod { Acronym = m.Acronym }).ToArray();
set => _requiredMods = value;
}
private RulesetInfo ruleset;
[JsonIgnore]
public RulesetInfo Ruleset
{
get => ruleset;
set
{
ruleset = value;
if (_allowedMods != null)
{
AllowedMods.Clear();
AllowedMods.AddRange(value.CreateInstance().GetAllMods().Where(mod => _allowedMods.Any(m => m.Acronym == mod.Acronym)));
_allowedMods = null;
}
if (_requiredMods != null)
{
RequiredMods.Clear();
RequiredMods.AddRange(value.CreateInstance().GetAllMods().Where(mod => _requiredMods.Any(m => m.Acronym == mod.Acronym)));
_requiredMods = null;
}
}
}
public void SetRulesets(RulesetStore rulesets)
{
Beatmap = beatmap.ToBeatmap(rulesets);
}
// Todo: Move this elsewhere for reusability
private class APIMod : IMod
{
public string Acronym { get; set; }
}
}
}

View File

@ -0,0 +1,108 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Threading.Tasks;
using osu.Framework.Graphics;
using osu.Framework.Threading;
namespace osu.Game.Online
{
/// <summary>
/// A component which requires a constant polling process.
/// </summary>
public abstract class PollingComponent : Component
{
private double? lastTimePolled;
private ScheduledDelegate scheduledPoll;
private bool pollingActive;
private double timeBetweenPolls;
/// <summary>
/// The time that should be waited between polls.
/// </summary>
public double TimeBetweenPolls
{
get => timeBetweenPolls;
set
{
timeBetweenPolls = value;
scheduledPoll?.Cancel();
pollIfNecessary();
}
}
protected override void LoadComplete()
{
base.LoadComplete();
pollIfNecessary();
}
private bool pollIfNecessary()
{
// we must be loaded so we have access to clock.
if (!IsLoaded) return false;
// there's already a poll process running.
if (pollingActive) return false;
// don't try polling if the time between polls hasn't been set.
if (timeBetweenPolls == 0) return false;
if (!lastTimePolled.HasValue)
{
doPoll();
return true;
}
if (Time.Current - lastTimePolled.Value > timeBetweenPolls)
{
doPoll();
return true;
}
// not ennough time has passed since the last poll. we do want to schedule a poll to happen, though.
scheduleNextPoll();
return false;
}
private void doPoll()
{
scheduledPoll = null;
pollingActive = true;
Poll().ContinueWith(_ => pollComplete());
}
/// <summary>
/// Perform the polling in this method. Call <see cref="pollComplete"/> when done.
/// </summary>
protected virtual Task Poll()
{
return Task.CompletedTask;
}
/// <summary>
/// Call when a poll operation has completed.
/// </summary>
private void pollComplete()
{
lastTimePolled = Time.Current;
pollingActive = false;
if (scheduledPoll == null)
scheduleNextPoll();
}
private void scheduleNextPoll()
{
scheduledPoll?.Cancel();
double lastPollDuration = lastTimePolled.HasValue ? Time.Current - lastTimePolled.Value : 0;
scheduledPoll = Scheduler.AddDelayed(doPoll, Math.Max(0, timeBetweenPolls - lastPollDuration));
}
}
}

View File

@ -418,6 +418,8 @@ namespace osu.Game
dependencies.Cache(notifications);
dependencies.Cache(dialogOverlay);
chatOverlay.StateChanged += state => channelManager.HighPollRate.Value = state == Visibility.Visible;
Add(externalLinkOpener = new ExternalLinkOpener());
var singleDisplaySideOverlays = new OverlayContainer[] { settings, notifications };

View File

@ -8,6 +8,7 @@ using osuTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Events;
using osu.Framework.Localisation;
using osu.Game.Beatmaps;

View File

@ -1,6 +1,8 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using Newtonsoft.Json;
namespace osu.Game.Rulesets.Mods
{
public interface IMod
@ -8,6 +10,7 @@ namespace osu.Game.Rulesets.Mods
/// <summary>
/// The shortened name of this mod.
/// </summary>
[JsonProperty]
string Acronym { get; }
}
}

View File

@ -213,8 +213,6 @@ namespace osu.Game.Screens.Multi.Lounge.Components
d.FadeColour(s.GetAppropriateColour(colours), transition_duration);
};
background.Beatmap.BindTo(beatmap);
beatmapBind.BindValueChanged(b => beatmap.Value = beatmaps.GetWorkingBeatmap(b));
nameBind.BindValueChanged(n => name.Text = n);
@ -224,6 +222,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components
typeBind.BindTo(Room.Type);
beatmapBind.BindTo(Room.Beatmap);
participantsBind.BindTo(Room.Participants);
background.Beatmap.BindTo(beatmapBind);
modeTypeInfo.Beatmap.BindTo(beatmapBind);
modeTypeInfo.Type.BindTo(typeBind);

View File

@ -180,7 +180,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components
nameBind.BindValueChanged(n => name.Text = n);
background.Beatmap.BindTo(beatmap);
background.Beatmap.BindTo(beatmapBind);
participantInfo.Host.BindTo(hostBind);
participantInfo.Participants.BindTo(participantsBind);

View File

@ -23,7 +23,7 @@ namespace osu.Game.Screens.Multi.Match.Components
{
public const float HEIGHT = 200;
public readonly IBindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
public readonly IBindable<BeatmapInfo> Beatmap = new Bindable<BeatmapInfo>();
private readonly Box tabStrip;
@ -100,7 +100,7 @@ namespace osu.Game.Screens.Multi.Match.Components
private class BeatmapSelectButton : TriangleButton
{
private readonly IBindable<bool> createdBind = new Bindable<bool>();
private readonly IBindable<int?> roomIDBind = new Bindable<int?>();
[Resolved]
private Room room { get; set; }
@ -113,8 +113,8 @@ namespace osu.Game.Screens.Multi.Match.Components
[BackgroundDependencyLoader]
private void load()
{
createdBind.BindTo(room.Created);
createdBind.BindValueChanged(v => Enabled.Value = !v, true);
roomIDBind.BindTo(room.RoomID);
roomIDBind.BindValueChanged(v => Enabled.Value = !v.HasValue, true);
}
}

View File

@ -14,7 +14,7 @@ namespace osu.Game.Screens.Multi.Match.Components
{
public class MatchTabControl : PageTabControl<MatchPage>
{
private readonly IBindable<bool> created = new Bindable<bool>();
private readonly IBindable<int?> roomIdBind = new Bindable<int?>();
[Resolved]
private Room room { get; set; }
@ -28,10 +28,10 @@ namespace osu.Game.Screens.Multi.Match.Components
[BackgroundDependencyLoader]
private void load()
{
created.BindTo(room.Created);
created.BindValueChanged(v =>
roomIdBind.BindTo(room.RoomID);
roomIdBind.BindValueChanged(v =>
{
if (v)
if (v.HasValue)
{
Items.ForEach(t => t.Enabled.Value = !(t is SettingsMatchPage));
Current.Value = new RoomMatchPage();

View File

@ -97,7 +97,7 @@ namespace osu.Game.Screens.Multi.Match
};
header.OnRequestSelectBeatmap = () => Push(new MatchSongSelect());
header.Beatmap.BindTo(Beatmap);
header.Beatmap.BindTo(beatmapBind);
header.Tabs.Current.ValueChanged += t =>
{

View File

@ -1,15 +1,24 @@
// Copyright (c) 2007-2018 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.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.IO.Network;
using osu.Framework.Logging;
using osu.Game.Beatmaps;
using osu.Game.Online;
using osu.Game.Online.API;
using osu.Game.Online.Multiplayer;
using osu.Game.Rulesets;
namespace osu.Game.Screens.Multi
{
public class RoomManager : Component
public class RoomManager : PollingComponent
{
public IBindableCollection<Room> Rooms => rooms;
private readonly BindableCollection<Room> rooms = new BindableCollection<Room>();
@ -19,14 +28,104 @@ namespace osu.Game.Screens.Multi
[Resolved]
private APIAccess api { get; set; }
[Resolved]
private RulesetStore rulesets { get; set; }
[Resolved]
private BeatmapManager beatmaps { get; set; }
public RoomManager()
{
TimeBetweenPolls = 5000;
}
public void CreateRoom(Room room)
{
room.Host.Value = api.LocalUser;
// Todo: Perform API request
var req = new CreateRoomRequest(room);
room.Created.Value = true;
rooms.Add(room);
req.Success += result => addRoom(room, result);
req.Failure += exception => Logger.Log($"Failed to create room: {exception}");
api.Queue(req);
}
protected override Task Poll()
{
if (!api.IsLoggedIn)
return base.Poll();
var tcs = new TaskCompletionSource<bool>();
var pollReq = new GetRoomsRequest();
pollReq.Success += result =>
{
foreach (var r in result)
{
foreach (var pi in r.Playlist)
{
pi.Ruleset = rulesets.GetRuleset(pi.RulesetID);
pi.SetRulesets(rulesets);
}
// Temporarily
r.Beatmap.Value = r.Playlist.FirstOrDefault()?.Beatmap;
var existing = rooms.FirstOrDefault(e => e.RoomID.Value == r.RoomID.Value);
if (existing == null)
rooms.Add(r);
else
existing.CopyFrom(r);
}
tcs.SetResult(true);
};
pollReq.Failure += _ => tcs.SetResult(false);
api.Queue(pollReq);
return tcs.Task;
}
private void addRoom(Room local, Room remote)
{
local.CopyFrom(remote);
var existing = rooms.FirstOrDefault(e => e.RoomID.Value == local.RoomID.Value);
if (existing != null)
rooms.Remove(existing);
rooms.Add(local);
}
private class CreateRoomRequest : APIRequest<Room>
{
private readonly Room room;
public CreateRoomRequest(Room room)
{
this.room = room;
}
protected override WebRequest CreateWebRequest()
{
var req = base.CreateWebRequest();
req.ContentType = "application/json";
req.Method = HttpMethod.Post;
req.AddRaw(JsonConvert.SerializeObject(room));
return req;
}
protected override string Target => "rooms";
}
private class GetRoomsRequest : APIRequest<List<Room>>
{
protected override string Target => "rooms";
}
}
}