-
Notifications
You must be signed in to change notification settings - Fork 379
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement creating rooms for queued beatmaps for "daily challenge" system #11204
Conversation
…reation Attempting to write tests for the command that creates 'beatmap of the day' multiplayer rooms was stymied by the fact that immediately weak inequality here was causing the room to not become 'active' up to a second after it was created.
or even creating the entire room in advance to act as the "queue" (with |
I really don't like this idea in general if I'm honest. It feels sloppy - I've really been historically bitten way too much by "special" in-band values like zero IDs having some special meaning like this, which then leads to pitfalls when somebody tries to follow them like a normal foreign key. It also closes off potential avenues of extension. For instance, imagine if this takes and a few months down the line we want to add something like collective goals attached to the beatmap of the day, like getting however many A ranks across the playerbase. At that point I dunno where you put this information if not into this table - some new table? Although you could arguably say that if that is the eventual direction then the |
Also mentioned above, how about creating the room with starts_at set to the future? Whatever the custom settings it'll have, I imagine it'll be linked to the room (or the respective playlist items) anyway. |
Room with a future date sounds much cleaner from a design angle (no more weird detached playlist items whose existence must be kept in mind at all times), but also it implies that whatever will be responsible for queueing the beatmaps of the day will have to reimplement the logic responsible for creating the actual room, e.g. at least a decent subset of I dunno. I'm most likely not going to be the one implementing the enqueueing of items so from my perspective I can't pretend I can push back too hard on that but it does appear to me that if ASS is to create a full playlists room by itself it may become redundant (with respect to code duplication) and/or inconvenient. Unless ASS is going to just send a special api request to web for this or something, but at that point I don't know if things are that much simpler. |
Or can just implement an interop endpoint which accepts some set of parameters and then create the room... |
Yep that would also work. Really need a shot-call from @peppy at this point to commit to either direction because I can't see into ASS source and don't have much of an insight as to what would be easiest/best here. |
Hmm... if I was implementing this I too would have gone for a separate table as done here, just to implement cleanly and in an isolated manner. We've learnt the hard way from trying to make one model work for too many different usages, and that feels like it could be the case here. As for whether we use a future I would see ASS being a very simple "queue more beatmap / mod criteria to queue table" and osu-web handling the actual room creation. If this was to change, it would be hard to orchestrate checks such as "queue is running low!" being broadcast to discord or otherwise, as ASS doesn't have any kind of cron concept. I guess osu-web could still do the creation ahead of time and use a future |
well the table here is just an interim table before eventually transformed into rooms and playlists. Having an interop which creates the room immediately skips that additional step. If the actual system doesn't use room/playlist at all then that'd make more sense I guess (although probably will come with its own complication). |
With a separate table scheduling future rooms is finicky (it'd be doable manually if anything). A remote procedure call would be more suited to scheduling future rooms because web then knows when a queue item is added; otherwise it would have to poll the queue on an interval I suppose. Or manual dispatch (which is doable but seems shoddy/counterproductive)
It is that, yeah, but one other argument that I'll drop here in favour of having it interim is that delaying the room creation simplifies implementation of some manual interventions if required. For instance, reordering items is just a question of updating some rows' orders in the queue, you don't have to go find all of the other scheduled rooms and reorder their dates. Similarly, dropping a queued beatmap for whatever reason is just removing an item from the interim table, while if rooms were to be queued in advance, you'd have to go find all of the subsequent rooms for subsequent days (if any) and pull them back by a day again. Dunno it's really a bit of a "see the future" thing as usual. Difficult to say how this will pan out in reality 🤷 |
Yeah I meant more for manual stuff in the future.
It'll get a bit annoying if we need to make changes, especially to ordering. Like rather than having a cron task that runs once a day and pulls a new room in, we have all these future rooms already lined up, which potentially need date changes, deletion etc. |
Blocking temporarily pending confirmation of rename of the feature (see https://discord.com/channels/188630481301012481/188630652340404224/1239804074355068959) |
Rename of feature to "daily challenge" has been applied. If anyone other than me has ran this on their local envs, first wipe all |
from what I gather, there will be a few other things to be run when the new room is open. How will the schedule be coordinated? Is it checking the rooms table and see if the new room has been created? What if the creation failed due to some reason (like db error)? an alternative would be the table to be completely managed by ass and it just hit an interop endpoint to create the room when it starts maybe? |
Currently the intended flow for this to work is as such:
Not super sure what this is referring to.
I'm not sure what the specific question is here but for now it is best-effort, hard-coded, daily. Rooms are created daily as long as there exist any queued beatmaps for the daily challenge thing. There's no coordination in the sense of "this beatmap must be played on this specific day". At most queued items can be reordered.
The presumption is that step (2) will fail and
I'm not sure what else to say to that other than what has been already said above. Truthfully I'm not really even understanding why there's such apparent pushback to this existing in web, which makes it difficult for me to suggest any kind of compromise. |
the number 3 there.
it'll allow all the process to be run in sync by a single process, with its own failure handling. And possibly removes the need to poll the table |
Unless ASS was to RPC spectator server too to tell it that there is a new room (which I don't think we currently even have the capability to do, on the spectator server side), it doesn't. Unless you're thinking that the interop endpoint could issue like a notifications server message, which, again, I'm neutral on this being the case, were it not for @peppy expressing clear opposition to using the notifications server for delivery to clients for this feature (which is understandable, since expansion of this feature with future capabilities will be much easier to do in spectator server if the expansion happens). |
well, I guess this is fine as long there's someone to handle rerunning/checking osu-web job in case of failure... |
$ownerId = $GLOBALS['cfg']['osu']['legacy']['bancho_bot_user_id']; | ||
|
||
$room = (new Room())->startGame( | ||
User::lookup($ownerId), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use findOrFail
as it should be a simple id lookup and expected to fail if for some reason missing
protected $signature = 'daily-challenge:queue-next'; | ||
|
||
protected $description = 'Creates a new "daily challenge" multiplayer room for the first item in queue.'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
those are supposed to be sorted alphabetically nowadays
{ | ||
parent::tearDown(); | ||
|
||
config_set('osu.legacy.bancho_bot_user_id', $this->originalUtilityUserId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As it turned out the config is always reset between tests
(I probably didn't check how it actually behaves before)
|
||
class DailyChallengeQueueNext extends Command | ||
{ | ||
protected $signature = 'daily-challenge:queue-next'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is it queue-next
or create-next
); | ||
$room->update(['category' => 'daily_challenge']); | ||
|
||
$nextQueueItem->multiplayer_room_id = $room->id; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
using ->getKey()
is preferred over ->id
|
||
public function multiplayerRoom() | ||
{ | ||
return $this->hasOne(Room::class, 'id', 'multiplayer_room_id'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be belongsTo
just like beatmap
(and the extra parameters aren't needed)
$this->artisan('daily-challenge:queue-next') | ||
->expectsOutputToContain('"Daily challenge" queue is empty') | ||
->assertFailed(); | ||
$this->assertSame(0, Room::all()->count()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use expectCountChange
instead
|
||
DB::transaction(function () use ($nextQueueItem) { | ||
$startTime = today(); | ||
$ownerId = $GLOBALS['cfg']['osu']['legacy']['bancho_bot_user_id']; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's more like host
than owner
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah there's "room host" but also "playlist item owner". Mixed these up a bit 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seems working although should be remembered that any failure (including transient errors like db temporarily blipping) on the cron job means it'll need to be manually rerun by someone who has access to osu-web console as I don't think it's got any retry mechanism
(either that or schedule the cron multiple times or something)
oh yeah, the title should probably be adjusted because it's not queuing anything but more like creating queued room. |
RFC. Opening this for early feedback because I want to continue building on this going forward (and because this is likely to go through a few review rounds because I suck at php).
This implements the queueing mechanism described in point (1) of the execution plan of the design document for the new "daily challenge" system. The queueing is based around a new database table:
The table is designed to serve the MVP first and foremost (
beatmap_id
andruleset_id
), but also has some future-proofing built in (allowed
andrequired_mods
, alsoorder
for being able to e.g. swap the items if need be).Items will be added to this table externally (most likely via ASS, cc @peppy for confirm). There is no selection automation in place.
This PR introduces a new artisan command -
daily-challenge:queue-next
- which takes the item with the lowest outstandingorder
(orid
, if tied), and creates a new playlists room based on the parameters stored in the queue (beatmap id / ruleset id / mods). The playlist is assumed to run until midnight of the current day.This is not hooked up to cron yet, because the full flow isn't yet ready. I'll PR a change adding it to cron when the dust has settled on all parts and we have a flow working end-to-end, between web, spectator server, and client.
Possible contention points
Naming - willing to listen to suggestions.
Creating a room requires a user because both the room itself and the playlist items are presumed to be owned by a user. In this case I used BanchoBot, or user w/ ID
$GLOBALS['cfg']['osu']['legacy']['bancho_bot_user_id']
, as a replacement for a user.This is especially a bit dodgy due to restrictions placed on users with respect to the number of allowable rooms open at a time:
osu-web/app/Models/Multiplayer/Room.php
Lines 644 to 646 in 8a37441
That is why I wrote the command to die if another "daily challenge" room was already open when it was ran, so that check above won't be hit in practice, but yeah. I briefly considered adding a carve-out for the check for BanchoBot specifically but that felt really dodgy so I didn't.
I imagine this may be considered suboptimal, especially given the
legacy
name in that key lookup there. We could probably try to decouple the multiplayer room model from always needing to have a user although that may be a operation that requires care to a degree that I'm not necessarily sure I'm able to perform it confidently.Structurally this reuses the whole
$room->startGame()
logic even though many validation checks are technically superfluous in there. It feels alright to me but it could be argued that this is a server-side flow and thus one might want to refactor thatstartPlay()
method into smaller chunks and reuse those. Dunno.The created room uses a new category -
daily_challenge
. I'm pretty sure this will be needed, at least for client-side filtering (because we won't want to show these in open room listings, just directly from main menu), but if not, then we can probably talk about alternatives.One thing that I already know is that this new category as added here will break old lazer clients due to serialisation woes (old versions won't know the new enum value). It is unfortunately too late to do anything about this, we'll just need to tell users to upgrade if this sticks.
af761d5 is a notable outlier in being a functional change to existing code rather than something completely new. It happened because I was having an issue when writing tests that rooms were getting out by the
active()
scope up to a second after having been opened. I'm not sure why that is - guessing it may be time comparison precision being set at 1 second or something - but it appears to me that a weak inequality doesn't really make sense there anyhow so I just made the change inline.