-
Notifications
You must be signed in to change notification settings - Fork 8
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
feat!: Matchmaking rewrite #35
Conversation
da47f20
to
e71f872
Compare
Some general comments before I actually dive into a review:
The naming convention of this seems a bit unintuitive in my opinion? It seems odd to use a function whose name implies it removes/resets data as a way to filter search criteria
This is a bit flawed I think? Unless I'm misunderstanding something about this implementation While A A session, on the other hand, is an active p2p session between online clients. This can be seen in As far as I can tell this even means that a single
If the idea of the
I'm going to ping @wolfendale on this one, since he helped catch several bad queries already I do wonder if there's a better way for us to handle queries rather than raw-dogging SQL?
I am fairly against this design, yeah. There's a few potential ways around this. Postgres has support for complex datatypes through its JSON, JSONB, and JSONPATH datatypes. These can be used to marshall Another solution would be to create a new table called
This would be the easiest solution to implement right now, but it's also limiting. We have to track everything as a solid string, which affects how we can search for them, and it only really works for simple types. Technically since
This sounds like maybe we should explore some query builders/ORMs. ORMs are typically frowned upon in Go however, so maybe not that. Interestingly just yesterday someone posted in the r/golang subreddit about a topic very similar to this https://old.reddit.com/r/golang/comments/1dr8qyi/abusing_gos_template_engine_as_sql_builder_and_orm/
To be clear, this method of using negative PIDs was first implemented by @SuperMarioDaBom and was then carried over to Rambo's. We don't actually know how this works on official servers, unfortunately. I, too, am unsure how to solve this in this case |
That is fair, the naming got imported from the previous implementation. Maybe something like
This differs a bit for what I have seen on MK7 communities. The communities have a gathering ID assigned as a Also, MK8/D tournaments do their own thing afaik, they use a SimpleSearchObject which doesn't inherit from
The
This would mean that we have to convert the variant to a string, which isn't directly doable, we would have to add a function that converts the value to a string and vice-versa.
What if we store the
I see... |
This sounds like you're filtering the criteria themselves rather than filtering by the criterias. But I do agree this is better
I believe we had a miscommunication. Yes, sessions have different dedicated gathering IDs from that of the parent gathering in these cases (I did not intend to imply otherwise). I was talking purely about the semantics of the 2 (a base Also for what it's worth, the 0th attribute is not always the gathering ID in these cases. This is only the case when You're right about MK8/D, I had forgotten about that
I had listed 2 solutions in what you quoted The first one with JSON, JSONB, and JSONPATH datatypes does not need to be converted to a string by us directly. They just need to be able to be marshalled, which most structs can be marshalled as-is they just won't have typical JSON field names (the field names will match 1:1 with the struct names, which is why I mentioned using struct tags) The second one does require converting to a string (and is the one where I proposed the table layout), but as I said all known subtypes of
This would function just the same as how you currently have it would it not? I'm not aware of being able to do direct comparisons on |
It's technically filtering the criterias though, it's intended to allow the server implementation to change the search parameters on the search criteria
That's good to know!
Ah sorry, that seems reasonable yeah.
ngl I kinda just assumed it, but if it isn't possible to compare |
The concept of negative PIDs was a guess and wouldn't make sense on the Switch. Instead, replace it with storing the "main" PID multiple times on the participants array.
Allows removal of matchmake_extension_database dependency inside match-making
…thParam Causes deadlocks if left locked
I wanted the answer, not my own question!
I've had a go at implementing this in Minecraft here: https://github.com/PretendoNetwork/minecraft-wiiu/tree/work/mm-rewrite (Depends on #41) Feel free to inspect for code style notes. Overall it was pretty painless to do with a few extra globals for the SQL database. I liked how old gatherings have registered=false but otherwise remain in the database; that'll be useful for moderation. Since this code actually handles PRUDP disconnections, there are a few spurious kicks coming up from the lower layers. Will investigate further. |
Useful for games with custom behaviours, like Minecraft's friends-of-friends feature and Splatoon's fests
Keep tracking on gathering (un)registrations, participants joining and leaving and host and owner changes. All tracking is managed inside the database functions except for the host and owner changes. The `matchmaking.participants` now remains useless from this change at the moment, so remove it for now.
Currently we don't support ranked matchmaking properly nor the "score based" version.
Also include some minor fixes.
The `min_participants` and `max_participants` are stored on `gatherings`, not on `matchmake_sessions`.
Otherwise the selection method is rendered useless since they are incompatible.
Assign a new host when the current host is disconnecting from a gathering. To simplify the implementation, set the owner of the gathering as the new host. Fixes Splatoon where the client doesn't transfer the host by themselves.
In sight on the good results that the matchmaking rewrite has in the servers that have it and since it now has the green light, I will go ahead and merge this |
Resolves #33
Depends on PretendoNetwork/nex-protocols-go#78
May be affected by PretendoNetwork/nex-go#56
Changes:
This is a rewrite of the matchmaking system to use Postgres, for the reasons explained in the related issue. This only implements the same methods as the previous system and adds more features from those methods, but no new methods have been added on this PR. Some fixes have also been adapted from #28
Notes for server implementers
The public facing API remains mostly the same compared to the previous implementation. The public facing API remains mostly the same compared to the previous implementation. However, now a database manager has to be created with
globals.NewMatchmakingManager
and assigned to each protocol through theSetManager
function insidenex/register_common_secure_server_protocols.go
, which will then initialize the database. The initialization order MUST be the following:match-making
match-making-ext
matchmake-extension
Due to the search of gatherings being now done by Postgres and not within Go land, the
GameSpecificMatchmakeSessionSearchCriteriaChecks
function has been removed in favor of a different functionCleanupMatchmakeSessionSearchCriterias
, which works in a similar way toCleanupSearchMatchmakeSession
but instead allows theMatchmakeSessionSearchCriteria
s to be modified.Usage of this functionality is generally discouraged except for cases of region locking, ranked matchmaking or similar. These cases should be documented, and not be used by default
Technical details
The new matchmaking system has been designed in a way to support different types of
Gathering
, even types outside of NEX. The table fields take into account the theoretical max size of each field, even theuint64
(which is stored as anumeric(10)
)All tables are stored inside the
matchmaking
schema. The tables are the following:gatherings
: Stores information about aGathering
. Uses atype
column to distinguish between gathering types (MatchmakeSession
,Gathering
...). This is done to allow interoperability with other Quazal games. Thestarted_time
is also stored here for tracking.matchmake_sessions
: Stores information about aMatchmakeSession
.persistent_gatherings
: Currently unused. Intended to store information about aPersistentGathering
.A
tracking
schema is also used for logging user actions. At the moment the only user of this schema is thematch-making
protocol, but other protocols can use this schema too. What is currently being logged is:All relevant database functions are present on the
database
directories of each protocol, except for the tracking ones which are instead stored inside thetracking
directories. The database functions are exported in order to easily allow server implementers to override matchmaking methods (such as the case of Puyo Puyo which callsCloseParticipation
regardless of permissions). Note that somematch-making
methods import frommatchmake-extension/database
, so while this wouldn't work out of the box for Quazal games, it would ease the process by simply overriding those methods.The previous implementation was very prone to race conditions. That has been solved by adding a
MatchmakingMutex
on the common globals, which is called by the methods. The database functions don't lock by themselves, in order to perform multiple database operations in one method. There may be ways to optimize these queries, so suggestions are open.The
MatchmakeParam
params ofMatchmakeSession
are stored as amap
, which isn't easily storable on SQL. The parameter values also aren't easily storable by themselves (they are aVariant
), so instead we store the raw bytes from a NEX byte stream as a workaround. This isn't a nice solution and comparison of those params when finding sessions isn't doable, so that has been discarded. Suggestions for better alternatives that allows easy comparison are welcome.autoMatchmakePostpone
previously compared every field, even those that would be considered useless when searching sessions. This has been solved by performing the same checks as aSearchCriteria
, and also simplifies the query a bit.Some SQL queries (specifically the ones used for finding sessions with search criterias) are being generated on the fly due to the search parameters being represented in a format which isn't easily parsable on SQL. Some sections could probably be inlined into the statement, but I haven't put much effort to do so. Most of the big queries have been tested, except for
JoinGatheringWithParticipants
.Gatherings can have multiple participants per PID (such as multiple people playing on the same console). This functionality has been imported from Rambo's MK8 server, where the PID is stored as a negative value.
Here however we perform a raw conversion from signed to unsigned int32 for compatibility. It is unknown how/if this would work on the Switch, since it has been observed PIDs with values higher thanHere however we simply store the same positive PID as the main user multiple times inside the participants array, and then account for duplicates where necessary.MaxInt64
(at least on NEX 4.4.0 on the 3DS).