Skip to content
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

LBP1 Playlists #649

Merged
merged 13 commits into from
Sep 5, 2024
Merged

LBP1 Playlists #649

merged 13 commits into from
Sep 5, 2024

Conversation

Beyley
Copy link
Member

@Beyley Beyley commented Aug 31, 2024

You can only create playlists in-game when latest retail LBP1 has the memory address 0x0074c2c0 patched to be 0x01 instead of 0x00. In the EBOOT this is located at address 0x0073c2c0. Updates to Refresher and the OFW patchers will need to be made to enable this feature. For testing you also need to make sure that the /lbp/network_settings.nws endpoint is being accessed correctly, since playlists require the AlexDB option to be set to function.

Luckily the game is perfectly happy viewing and playing other's playlists even if you don't have the feature enabled yourself. So there's no worry about un-patched users having issues.

I would like to tackle APIv3 support for playlists in a future PR, but aside from that, this is a complete implementation of the feature for LBP1.

It should be possible to represent this (at least close enough) in LBP3 by just not showing sub-playlists, and showing all playlists by a user, instead of just playlists inside of your root playlist. For LBP2/PSP/Vita we can just display playlists as a slot results override you trigger from the API, or as a special cased playlist:name in text search, although we won't be able to display sub-playlists there either.

As a basic technical summary of how this is layed out, all players contain a "Root Playlist". In LBP1, when you view your "My Playlists" view, it will create a root playlist for you, if you don't have one. All playlists you create in the future are then considered "sub-playlists" of that root playlist, or of some other playlist.

The game allows you to add any playlist (eg. other's playlists aswell) as a sub-playlist to any of your playlists (except your root playlist). And it also allows you to add any user slot to any of your playlists (except your root playlist), as a sub-level of the playlist.

The original developer's intention for viewing a user's playlists is that there is a button on the profile labeled "Playlists", and it would show the contents of the root playlist, however I found this to be rather uncreative, so in LBP1 I've injected the contents of your root playlist into your user level list as "sticky" slot results, so it always appears regardless of the page. This seems to work fairly well, and doesn't impact pagination from what I can see.

Sometimes the locations appear at the tail end of the string, right before the `}`, so we need to extract that out.
@Beyley Beyley requested a review from jvyden August 31, 2024 02:07
Copy link
Member

@jvyden jvyden left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First half of my review, posting because it's all easy stuff and I neeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed caffeine before I continue.

Refresh.GameServer/Types/Playlists/GamePlaylist.cs Outdated Show resolved Hide resolved
Refresh.GameServer/Types/Playlists/GamePlaylist.cs Outdated Show resolved Hide resolved
Refresh.GameServer/Types/Playlists/SubPlaylistRelation.cs Outdated Show resolved Hide resolved
Refresh.GameServer/Types/Playlists/GamePlaylist.cs Outdated Show resolved Hide resolved
Beyley and others added 3 commits August 31, 2024 15:15
Co-authored-by: jvyden <[email protected]>
Signed-off-by: Beyley Thomas <[email protected]>
How this was implemented in the past was strange to say the least. Slot ID 0 is invalid anyway, so let's re-use "story id == 0" for "is user level".
@Beyley Beyley requested a review from jvyden August 31, 2024 23:15
@Beyley Beyley mentioned this pull request Sep 3, 2024
@jvyden
Copy link
Member

jvyden commented Sep 5, 2024

Gonna test locally then go from there

@jvyden jvyden dismissed their stale review September 5, 2024 22:32

Conversations resolved but I haven't tested locally

@jvyden
Copy link
Member

jvyden commented Sep 5, 2024

Migrations seem to fail:

[09/05/24 18:43:53] [Debug] [Startup] Warming up database provider...
Unhandled exception. System.AggregateException: Exception occurred in a Realm.MigrationCallback callback. (The class GamePlaylist is not in the limited set of classes for this realm (Parameter 'className'))
 ---> System.ArgumentException: The class GamePlaylist is not in the limited set of classes for this realm (Parameter 'className')
   at Realms.Helpers.Argument.Ensure(Boolean condition, String message, String paramName)
   at Realms.Realm.Dynamic.All(String className)
   at Refresh.GameServer.Database.GameDatabaseProvider.Migrate(Migration migration, UInt64 oldVersion) in /home/jvyden/Documents/Refresh/Refresh.GameServer/Database/GameDatabaseProvider.cs:line 665
   at Realms.SharedRealmHandle.OnMigration(IntPtr oldRealmPtr, IntPtr newRealmPtr, IntPtr migrationSchema, Schema oldSchema, UInt64 schemaVersion, IntPtr managedConfigHandle)
   --- End of inner exception stack trace ---
   at Realms.NativeException.ThrowIfNecessary()
   at Realms.SharedRealmHandle.Open(Configuration configuration)
   at Realms.RealmConfiguration.CreateHandle(Configuration& configuration)
   at Realms.RealmConfigurationBase.CreateRealm()
   at Realms.Realm.GetInstance(RealmConfigurationBase config)
   at Bunkum.RealmDatabase.RealmDatabaseProvider`1.GetContext()
   at Refresh.GameServer.Database.GameDatabaseProvider.Warmup() in /home/jvyden/Documents/Refresh/Refresh.GameServer/Database/GameDatabaseProvider.cs:line 93
   at Bunkum.Core.BunkumServer.RunStartupTasks()
   at Bunkum.Core.BunkumServer.Start(Nullable`1 taskOverride)
   at Refresh.GameServer.RefreshGameServer.Start() in /home/jvyden/Documents/Refresh/Refresh.GameServer/RefreshGameServer.cs:line 176
   at Program.<Main>$(String[] args) in /home/jvyden/Documents/Refresh/Refresh.GameServer/Startup.cs:line 22
   at Program.<Main>(String[] args)

@Beyley
Copy link
Member Author

Beyley commented Sep 5, 2024

Migrations seem to fail:

oop, thats strange, im not getting that, must have messed up when i split the changes out from my stashed /showModerated stuff

@jvyden
Copy link
Member

jvyden commented Sep 5, 2024

It's probably because I'm migrating from a version without GamePlaylist... I hate this.

I'll fix it.

@jvyden
Copy link
Member

jvyden commented Sep 5, 2024

I decided to remove them entirely; they should only apply to you on your development database anyways.

Copy link
Member

@jvyden jvyden left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay, had some troubles with RPCS3 (I ended up just using PS3)

I see some Realm problems:

[09/05/24 19:35:35] [Info] [Request] Served request to 10.0.0.168:54869: 200 OK on POST '/lbp/createPlaylist?parent_id=1' (77ms)
[09/05/24 19:35:35] [Info] [Request] Served request to 10.0.0.168:54868: 200 OK on GET '/lbp/playlist/1?pageStart=1&pageSize=30' (6ms)
[09/05/24 19:35:38] [Error] [Request] System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
 ---> System.NotSupportedException: The lhs of the binary operator 'Parameter' should be a member expression accessing a property on a managed RealmObjectBase.
Unable to process 'p'.
   at Realms.RealmResultsVisitor.VisitBinary(BinaryExpression node)
   at Realms.RealmResultsVisitor.RecurseToWhereOrRunLambda(MethodCallExpression m)
   at Realms.RealmResultsVisitor.VisitMethodCall(MethodCallExpression node)
   at Realms.RealmResultsProvider.Execute(Expression expression)
   at Realms.RealmResultsProvider.Execute[T](Expression expression)
   at Refresh.GameServer.Database.GameDatabaseContext.<>c__DisplayClass211_0.<GetPlaylistsByAuthorContainingPlaylist>b__1(SubPlaylistRelation r) in /home/jvyden/Documents/Refresh/Refresh.GameServer/Database/GameDatabaseContext.Playlists.cs:line 131
   at System.Linq.Enumerable.SelectEnumerableIterator`2.MoveNext()
   at System.Linq.Enumerable.WhereEnumerableIterator`1.ToList()
   at Refresh.GameServer.Endpoints.Game.Playlists.PlaylistEndpoints.PlaylistsContainingSlot(RequestContext context, DataContext dataContext, String slotType, Int32 slotId) in /home/jvyden/Documents/Refresh/Refresh.GameServer/Endpoints/Game/Playlists/PlaylistEndpoints.cs:line 121
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
   at System.Reflection.MethodBaseInvoker.InvokeDirectByRefWithFewArgs(Object obj, Span`1 copyOfArgs, BindingFlags invokeAttr)
   --- End of inner exception stack trace ---
   at System.Reflection.MethodBaseInvoker.InvokeDirectByRefWithFewArgs(Object obj, Span`1 copyOfArgs, BindingFlags invokeAttr)
   at System.Reflection.MethodBaseInvoker.InvokeWithFewArgs(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at Bunkum.Core.Endpoints.Middlewares.MainMiddleware.InvokeEndpointByRequest(ListenerContext context, Lazy`1 database)
   at Bunkum.Core.Endpoints.Middlewares.MainMiddleware.HandleRequest(ListenerContext context, Lazy`1 database, Action next)
   at Bunkum.Core.BunkumServer.<>c__DisplayClass22_0.<HandleRequest>b__0()
   at Refresh.GameServer.Middlewares.WebsiteMiddleware.HandleRequest(ListenerContext context, Lazy`1 database, Action next) in /home/jvyden/Documents/Refresh/Refresh.GameServer/Middlewares/WebsiteMiddleware.cs:line 53
   at Refresh.GameServer.Middlewares.DeflateMiddleware.HandleRequest(ListenerContext context, Lazy`1 database, Action next) in /home/jvyden/Documents/Refresh/Refresh.GameServer/Middlewares/DeflateMiddleware.cs:line 26
   at Refresh.GameServer.Middlewares.DigestMiddleware.HandleRequest(ListenerContext context, Lazy`1 database, Action next) in /home/jvyden/Documents/Refresh/Refresh.GameServer/Middlewares/DigestMiddleware.cs:line 100
   at Refresh.GameServer.Middlewares.CrossOriginMiddleware.HandleRequest(ListenerContext context, Lazy`1 database, Action next) in /home/jvyden/Documents/Refresh/Refresh.GameServer/Middlewares/CrossOriginMiddleware.cs:line 46
   at Refresh.GameServer.Middlewares.PspVersionMiddleware.HandleRequest(ListenerContext context, Lazy`1 database, Action next) in /home/jvyden/Documents/Refresh/Refresh.GameServer/Middlewares/PspVersionMiddleware.cs:line 24
   at Refresh.GameServer.Middlewares.LegacyAdapterMiddleware.HandleRequest(ListenerContext context, Lazy`1 database, Action next) in /home/jvyden/Documents/Refresh/Refresh.GameServer/Middlewares/LegacyAdapterMiddleware.cs:line 17
   at Bunkum.Core.BunkumServer.HandleRequest(ListenerContext context, Lazy`1 database)
[09/05/24 19:35:38] [Info] [Request] Served request to 10.0.0.168:54867: 500 InternalServerError on GET '/lbp/playlistsContainingSlotByAuthor/playlist/2?author=jvyden420&pageStart=1&pageSize=5' (8ms)
[09/05/24 19:35:38] [Error] [Request] System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
 ---> System.NotSupportedException: The lhs of the binary operator 'Parameter' should be a member expression accessing a property on a managed RealmObjectBase.
Unable to process 'p'.
   at Realms.RealmResultsVisitor.VisitBinary(BinaryExpression node)
   at Realms.RealmResultsVisitor.RecurseToWhereOrRunLambda(MethodCallExpression m)
   at Realms.RealmResultsVisitor.VisitMethodCall(MethodCallExpression node)
   at Realms.RealmResultsProvider.Execute(Expression expression)
   at Realms.RealmResultsProvider.Execute[T](Expression expression)
   at Refresh.GameServer.Database.GameDatabaseContext.<>c__DisplayClass210_0.<GetPlaylistsContainingPlaylist>b__1(SubPlaylistRelation r) in /home/jvyden/Documents/Refresh/Refresh.GameServer/Database/GameDatabaseContext.Playlists.cs:line 125
   at System.Linq.Enumerable.SelectEnumerableIterator`2.MoveNext()
   at System.Linq.Enumerable.WhereEnumerableIterator`1.ToList()
   at Refresh.GameServer.Endpoints.Game.Playlists.PlaylistEndpoints.PlaylistsContainingSlot(RequestContext context, DataContext dataContext, String slotType, Int32 slotId) in /home/jvyden/Documents/Refresh/Refresh.GameServer/Endpoints/Game/Playlists/PlaylistEndpoints.cs:line 121
   at InvokeStub_PlaylistEndpoints.PlaylistsContainingSlot(Object, Span`1)
   at System.Reflection.MethodBaseInvoker.InvokeWithFewArgs(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   --- End of inner exception stack trace ---
   at System.Reflection.MethodBaseInvoker.InvokeWithFewArgs(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at Bunkum.Core.Endpoints.Middlewares.MainMiddleware.InvokeEndpointByRequest(ListenerContext context, Lazy`1 database)
   at Bunkum.Core.Endpoints.Middlewares.MainMiddleware.HandleRequest(ListenerContext context, Lazy`1 database, Action next)
   at Bunkum.Core.BunkumServer.<>c__DisplayClass22_0.<HandleRequest>b__0()
   at Refresh.GameServer.Middlewares.WebsiteMiddleware.HandleRequest(ListenerContext context, Lazy`1 database, Action next) in /home/jvyden/Documents/Refresh/Refresh.GameServer/Middlewares/WebsiteMiddleware.cs:line 53
   at Refresh.GameServer.Middlewares.DeflateMiddleware.HandleRequest(ListenerContext context, Lazy`1 database, Action next) in /home/jvyden/Documents/Refresh/Refresh.GameServer/Middlewares/DeflateMiddleware.cs:line 26
   at Refresh.GameServer.Middlewares.DigestMiddleware.HandleRequest(ListenerContext context, Lazy`1 database, Action next) in /home/jvyden/Documents/Refresh/Refresh.GameServer/Middlewares/DigestMiddleware.cs:line 100
   at Refresh.GameServer.Middlewares.CrossOriginMiddleware.HandleRequest(ListenerContext context, Lazy`1 database, Action next) in /home/jvyden/Documents/Refresh/Refresh.GameServer/Middlewares/CrossOriginMiddleware.cs:line 46
   at Refresh.GameServer.Middlewares.PspVersionMiddleware.HandleRequest(ListenerContext context, Lazy`1 database, Action next) in /home/jvyden/Documents/Refresh/Refresh.GameServer/Middlewares/PspVersionMiddleware.cs:line 24
   at Refresh.GameServer.Middlewares.LegacyAdapterMiddleware.HandleRequest(ListenerContext context, Lazy`1 database, Action next) in /home/jvyden/Documents/Refresh/Refresh.GameServer/Middlewares/LegacyAdapterMiddleware.cs:line 17
   at Bunkum.Core.BunkumServer.HandleRequest(ListenerContext context, Lazy`1 database)
[09/05/24 19:35:38] [Info] [Request] Served request to 10.0.0.168:54866: 500 InternalServerError on GET '/lbp/playlistsContainingSlot/playlist/2?pageStart=1&pageSize=5' (5ms)

@Beyley
Copy link
Member Author

Beyley commented Sep 5, 2024

ugh, realm is so weird, it doesnt like .First(p => p == other)
I guess I didnt test thoroughly enough after the ID -> ref changes, i'll push a fix

Copy link
Member

@jvyden jvyden left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems to work pretty well 👍

@jvyden jvyden merged commit 9b2cd0f into LittleBigRefresh:main Sep 5, 2024
2 checks passed
@Beyley Beyley deleted the lbp1-playlists branch September 5, 2024 23:50
jvyden added a commit that referenced this pull request Sep 10, 2024
This server is responsible for one thing only, which is telling the
client to load the slot listing for any user slot you send it. Retail
LBP2 and LBP3 both implement this, but both require [special
care](https://discord.com/channels/1049223665243389953/1049225857350254632/1280019755482218588)
to make it use a separate domain (which is important if you want to
protect your main gameserver behind cloudflare!)

This is a fairly basic implementation of the server, but should be
sufficient for even large amounts of connected players.

Marking as draft until #649 is merged and hashed live play now is added,
since this branch is based off of that one (didnt wanna deal with merge
conflicts on this PR).

Thanks to aidan for figuring all this out

Example of server in action:


https://github.com/user-attachments/assets/d790a570-c291-4005-b3cf-daf6a95a9f51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants