squiretournamentservices / squirecore Goto Github PK
View Code? Open in Web Editor NEWThe backend library used by Squire Tournament Services
License: GNU Affero General Public License v3.0
The backend library used by Squire Tournament Services
License: GNU Affero General Public License v3.0
When the user tries to access the site in their browser, there is an infinite loop between the front end and the back end if the tournament is missing from the database.
An infinite loop should not be possible; I presume there should be some kind of error message presented instead.
Tournaments track lots of redundant data that is useful during the tournament, but that data takes up too much space after it is over. Excess data that can be trimmed includes the confirmations and drops in a round, round number to id map and opponents being tracked in the round registry, name to id map in the player reg, deck ordering in the player, and most importantly, everything back a unique identifier for all cards in decks.
As stated, only tournament data needs to be in long-term storage, not tournament managers. However, we want to periodically save whole tournaments and their operation logs for regression testing.
In order for the tournament manager to function properly, a given operations log much deterministically create tournaments. Otherwise, different clients will think different things are happening.
There are two possible solutions. Either, all pairing systems are deterministic or the Pair
operation returns a Pairings
struct, which the client can then use to CreateMatches
. Neither of these is perfect. The first greatly restricts the domain of pairing techniques we can employ. However, the second would require reworking how creating a match interacts with each pairing system. For example, creating a match would have to remove people from the queue of fluid pairings but do nothing during swiss pairings.
SquireWeb needs pages for users to login and register. This should redirect them to the tournament creation page.
SquireWeb needs a page for creating and submitting a new user account. This should include email and password as well as all the components of the squire account struct: display name, sharing permissions, etc.
Squire Lib is used by many projects, including Squire Desktop, at current it stores a formatted deck list and, cards from MTGJson in memory.
The issue is the repeating of data and, having to store a large JSON file.
{
"name": ..., /*name and, deck info*/
[
{
"oracle-id": ...,
"quantity": 1,
"sideboard": false
}, ...
]
}
{
[
{
"oracle-id": ...,
"card": {...}
}
]
}
When a user submits a sync request for a tournament, we must verify that the changes that they are submitting for that tournament are allowed to be submitted by them. In other words, players can't submit operations for other players, judges can't submit admin operations, etc. However, if they submit a known operation, they are ok. So, this check should happen after the tournament manager creates the sync processor but before it is processed.
The confirm all operation confirms matches that have no results.
If there is an active match that does not have a result, the operation should fail.
During the first trial run of the new SquireBot, a number of bugs were found whose origins were calls to unwrap
. We should make it a goal to remove all (or most) unwrap
calls.
Currently, rounds don't track any information about the context in which they were created. While this is serviceable, we can do better.
Rounds should have a new field, say context
. This context will be of a new type, RoundContext
, whose variants correspond to pairing styles. This will help with queries to rounds. For example, the swiss pairing style would have a "swiss round number" in its context. This way, folks can query "rounds that were pair of the third swiss round."
There shouldn't be any major challenges with this.
It is possible to confirm the result of a match without that match having any results (wins or draws). This shouldn't happen.
Currently, the record result operation never checks if the player is in the match. The operation should have an additional field for the player's id to ensure that the player is in the round.
Currently, normal Vec
s are used to sync tournaments. However, since the sync process requires removing operations from the start of the Vec
, it would be more efficient to use a double-ended vector, like VecDeque
.
When opening the server in a browser after attempting to run the backend from the main SquireCore
directory, the following runtime error occurs:
thread 'tokio-runtime-worker' panicked at 'called `Result::unwrap()` on an `Err` value: Os { code: 2, kind: NotFound, message: "No such file or directory" }', squire_core/src/assets.rs:10:14
The backend server should be able to be run from anywhere.
Invoke cargo run
from the main SquireCore
directory instead of squire_core
, and open the server in your web browser.
Oh my God, why haven't I done this yet?
Currently, there are operations for confirming a result for one player and confirming all active matches. There should be an operation for totally confirming the result of a single match.
The tournament manager and tournament operations will be the primary way to communicate between the server and client. This needs to be finalized.
The SquireWeb needs a front landing page. This should include a short bit about what Squire is, links to our Discord and GitHub, and paths to either login or create a user.
There are two major issues with player registration right now.
One, there is no recourse for repeat player names when registering players (not quests).
Two, there is no way for players to re-register for a tournament. A player could drop and wish to join the tournament again. They should be able to do this.
For the first problem, both the RegisterPlayer
and AdminRegisterPlayer
operations could take an additional argument, Option<String>
. This optional argument will be the player's name should another player already have their name. Note that this should be separate from the player's account.
For the second, a new operation should be added, AdminReRegisterGuest
. Since a player's account is already unambiguous, the logic for re-registering players can be added to the existing operations. However, (re-)registering a guest can be rather ambiguous. For that reason, this should be a new operation.
Most of the client-server tournament sync protocol is done. However, there is still one rather large issue remaining. Say client A registers a new player locally, and syncs this with the server (and other clients). This works perfectly fine until they try to use that player's PlayerId
in a tournament operation. All Uuid
s are randomly generated locally. This means that all tournament operations that get stored and synced need to use the non-id varients of their identifiers. I don't believe there is a way to ensure this at the type level, so this should be clearly documented in the TournamentManager
.
Currently, we can only effectively specify single tournament settings. This is good for adjusting existing tournaments but is cumbersome to use for storing default tournament settings.
We need a structure that contains defaults for all tournament settings. This structure should be able to take a pairing and scoring preset and export a Vec
of single tournament settings.
Deck pruning has been a feature of the Squire tournament model since SquireBot's first tournament, but not much has changed since. Currently, we allow players to submit an arbitrary number of decks regardless of the tournament's max deck count. The idea behind this was to allow for more flexibility for players.
However, this flexibility requires that TOs have the ability to remove excess decks, i.e. deck pruning. While this works, it is a very sharp corner for both TOs and players. A player might be unaware of if they submitted multiple decks. After pruning, this could mean that a player is playing with a deck they submitted but that got pruned. To avoid this, both players and TOs need clearly understand this design decision of SL.
Instead, adding a deck (admin or otherwise) should check the player's deck count and reject the request if the deck count already equals the max count.
Alternatively, we could do nothing, but that seems ill-advised.
If we do this, all tournaments should have a default max deck count of 1.
The API and data model for the results of a round are messy. Currently, a player can record multiple results for themself, but there isn't a way for these results to be cleared. This problem is magnified when admin have to overwrite previous results.
This probably won't require changes to the RoundResult
enum, but rather how the Round
struct interprets and stores that info.
Relates to #27
Squire accounts will be deeply tied to tournaments. Currently, a player's name (a string) is all that's needed to register for a tournament. Once accounts have been established, registration should be more deeply tied to an account. Tournament registration should be changed to take an enum whose variant encodes registering with an account as a guest (a guest is simple to the current registration, just a name).
Moreover, judges and admins should be tracked in a tournament as well. These must be accounts.
Rational32
is a type that cannot be linked to C for some unknown reason. To fix this a set of wrappers must be written to allow FFI to use standings.
The greedy pairings algorithm works for pods of more than two players, but not well. This is particularly an issue during swiss tournaments. After just a few rounds, the greedy algorithm can lead to several byes being given. There is a need for more algorithms for pairing.
One contender for a new algorithm is what I call the "Branching" algorithm. Like the greedy alg, the branching alg is deterministic, but it attempts to maintain multiple pairings possibilities (where the greedy alg only ever maintains a single possible pairing). As the name would suggest, the branching algorithm uses a tree to model possible pairings (notably not a binary tree). Here is how it will work.
We are given a list of players (presumably ordered) and a pod size. For ease, let the list of players be [A, B, C ...]
and the pod size be 4. Like the greedy algorithm, we construct pairings one by one. The first player will be inserted at the root of a tree. We now attempt to add B to the tree. If B has already played A, we skip B, and they will be the first player we attempt to pair in the next iteration. Otherwise, we find (at least one) spot for them in the tree. In this case, B will branch from A. At this point, our tree looks like so:
A
|
B
Next, we will try to insert C into the tree. Provided A and C have not played against each other, we can insert C as a branch. For this example, let's say C has played neither A nor B. So, we insert C at the bottom of the branch. This creates the following tree:
A
|
B
|
C
We now must insert player D. Let's say the D and C have played against each other, so they can't be paired against each other. This will result in a second branch from B, which looks like:
A
|
B
/ \
C D
Lastly, we will insert E, who has played against none of these players. When inserting E, we will insert E into every branch we can. For every branch that we can't, we create a new branch. In this case, that looks like:
A
|
B
/ \
C D
| |
E E
At this point, we have at least one branch of length 4 (our pod size). So, we will take the leftmost branch as our pairing. So, we have [A, B, C, E]
. For this one pairing, this is the same result we would get if using the greedy alg. However, if C and E had played against each other, our tree would give us a pairing of [A, B, D, E]
as the tree would look like this:
A
|
B
/|\
C D E
|
E
This alg really shines compared to the greedy alg when you have an early branch, like when B and C have already played against each other. With the greedy algorithm, A and C would never be paired together if B and C were opponents in a different match. This way, we can track possible pairings for [A, B, X, X]
and [A, C, X, X]
where the greedy alg would just track the first possibility.
To round things out, should the entire list of remaining players be turned into a tree, there is literally no valid pairing for that player left, so they will be unpaired (and given a bye in a Swiss tournament).
An additional note about this alg: For pods of two players, this algorithm will produce the same results as the greedy alg.
As described, this algorithm doesn't support repair tolerance. It can be adjusted to do so, though. Likely, this would mean tracking a branch's "weight" (equal to its repair count). Any time that count would go up and not go above the tolerance, we allow it but we also branch.
Using the example where A and B have played against each other, we insert B normally, but we would attempt to insert C in a new branch and into the B's branch.
Like players, a tournament should also track its judges and admins. This is needed for the auth strategy for client-server communication.
The first step in getting Squire accounts set up will be making the account structs. These should go in squire_lib
as we will being using them in the tournament model in order to register players, judges, and admins.
For users, their struct should contain identifying information such as their display name, user name, and Uuid
as well as their privacy/sharing information. Notably, authentication material such as a password should not be in the struct.
For organizations, similar identifying information is needed as well as the owner, default judge and admin accounts, and default tournament settings. Eventually, this should also be (optionally) linked to a discord server.
In the tournament settings, there are subsets of settings. For example, under "Pairings Settings", there are settings for swiss/f fluid settings (whichever the tournament is using). This swiss/fluid subset of settings should be in drop downs
Create drop downs for both the settings categories (general, scoring, and pairings) and for the subcategories (pairing style and scoring style).
The SquireCore server needs to be selective with who can see deck information. Most tournaments that require deck reg are closed decklist tournaments. Tournaments should store if they are open or closed decklist tournaments. There should be a settings option to toggle that (with a default of closed).
SC should be selective with who can request a decklist. Tournament admin, judges, and the owner of the deck should always be able to see their decklist, but no one else if the tournament is closed decklist. There are a couple of ways of doing this, each with its own downsides. We could selectively strip out deck info from tournaments that are closed decklist based on who is requesting the info. Or we could always strip out this information and require folks to use the get decklist APIs.
Currently, standings are always calculated. These should be cached.
The tournament should hold an Option<Standings>
. When anything that could affect the standings is changed, the option is turned to None
. The get_standings
method should return this cache if Some
or calculate and store new standings if None
.
To cut down on serialization size, this field can be skipped.
Once a basic schema for the accounts is established, squire_core
will need CRUD (create, retrieve, update, delete) API endpoints in order to manage accounts. Endpoints for querying accounts are also needed. This will should include endpoints for getting a single account, all accounts, a count of accounts, and eventually searching for accounts.
The request and response structs for these endpoints should be added to squire_sdk
. They also need to have tests in squire_core/src/tests
.
It would be nice to be able to seed seating order. To enable this, each round needs to track the order in which each player was added. This should be an additional field that's a Vec<PlayerId>
. There also needs to be a field in the tournament to control this as well as a setting to toggle this behavior.
When operations are performed, there is a chance for errors to occur. In this case, we should not just ignore them. Instead, a message should be sent to the tournament viewer component so the error can be translated into a message for the TO to read. That messages should be present as a pop-up.
The tournament viewer component's message type should have a variant that is a tournament error. This error then needs to be translated and presented.
Most tournament operations are gated by status checks, i.e. you can't register if the tournament has ended. All these checks need to be reviewed to ensure they are functioning properly. There is a known issue with the check in the regular add_deck
method.
Decks are a common thing in larger tournaments. SquireLib
should support some method of tracking and recommending deck checks.
Currently, there are only "simple" tournament tabs in the web frontend. This works for now, but more specialized tabs will be needed in the future. Very roughly, "advanced" tabs are those that display/manipulate TournamentManger
details and non-advanced tabs display/manipulate Tournament
details.
An "Advanced" drop-down tab is added to the set of tournament tabs. From that, various advanced tabs can be selected.
The SquireCore server should expose APIs for pairing folks without needing to run the tournament through Squire. This would be a rough wrapper around the pairing algorithm functions.
There are endpoint groups, such as api/v1/pairings/calculate
, that take all the information needed to provide those calculations. For pairings, a request struct would look like this:
pub struct CalculatePairingsRequest {
pub alg: PairingAlgorithm,
pub players: Vec<PlayerId>, // Players are ordered
pub rounds: HashMap<PlayerId, HashSet<PlayerId>>,
pub settings: Vec<PairingSettings>,
}
Note that we aren't passing whole Round
s around as we don't want to make others beholden to our Round
model. This shouldn't be an issue as additional features like seeded seating orders are done in our Tournament
model so that responsibility is on the client.
This isn't an issue for things like PlayerId
as they (de)serialize to Uuid
s.
Currently, there is not a mechanic for a tournament to change its pairings method. This will be useful for when a bracketed pairing system is added and used to cut to top X.
Along with this, pairing systems share common settings. For example, both fluid and swiss tournaments have a match size setting. This should be preserved when switching systems.
SquireWeb needs a tournament creation page. This should allow for all of the different settings to be adjusted. The pairing method can be exclusively swiss for now. This will have to change eventually, though.
"" is valid, and maybe shouldn't be. Other fields may also lack validation.
The heart of SquireWeb will be the tournament control plane. For now, this can be relatively simple. We assume that they have edit permissions (if they don't, the backend will catch this so they can't sync).
The control plane needs ways to switch between subtabs for:
Each of these tabs needs to support all relevant operations (player registration, pairing creation, etc).
Because both the SDK and Lib will be run in WASM environments, they need to be tested in those environments in CI.
Currently, the types used in tournament sync, namely OpSlice
, hold vectors of operations. This makes sense since they could, at any point, run into a sync issue and need to return themselves and be (de)serialized. However, we can do slightly better. There is no need for this extra allocation if everything stays local.
To solve this, OpSlice
s should hold a Cow<'a, [FullOp]>
and deserialize into the owned variant. Things that hold an OpSlice
, such as a Blockage
, are free to restring their slices to be 'static
to avoid messy lifetime annotations when they are not needed. Of course, this could be added later if additional performance is needed.
When changing the min and, max deck count via tid_update_settings
such that new_min > old_max, whilst making sure the new values are valid the method fails.
The method should not fail as, the new values would be valid.
When new_min >= old_max
swap the order of apply setMin and, setMax
Often, table numbers suffice as round identifiers for active rounds. This should be supported as another round identifier.
SquireLib forms the base of every Squire service, so it needs to have a solid foundation. Key components of the library (if not the whole library) should use the no_panic
macro from the crate of the same name. This will help prevent issues like panics in SquireCore as a source of DoS.
Most panics come from the use of unwrap
throughout the codebase. These unwraps should be removed.
At a minimum, mark methods that the backend is likely to use with no_panic
(from the no_panic
crate), namely apply_op
.
Once accounts are established and connected to the functioning of a tournament, authentication for tournament and account endpoints will be needed. This auth should attest that the request is acting on behalf of some account. Not all endpoints need auth, however. Simple queries to view a tournament or an account need no auth. Requests that require mutable access to backing data will always need to be authenticated so that the backing data can ensure that account has permissions to change it.
Eventually, multiple authentication methods will be supported, including Discord and Google auth, but we should also have basic username/password auth as well.
In order to give TOs access to actions like rolling back a tournament (a feature of the TournamentManager
), we need a separate tab for them to look at the log of tournament actions that have been taken. This should include the full list of operations and some indicator for the location of where the log was last synced.
There will be similar "advanced features" tabs needed in the future too, such as an error log of failed local changes and a tournament merge resolver.
Add a new tab in the tournament viewer. Display the log of operations in a similar fashion to how the players and rounds are being displayed. This should include a "selected operation" but an operation filter isn't needed for now.
This will very likely require changes to the ClientState
trait in squire_sdk
. There will need a method to query the operations log of a tournament. There will also need to be a method on the TournamentManger
that returns a reference to the operation log.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.