Closed bates64 closed 6 years ago
This is done. What next, and can I have a review? @PullJosh @towerofnix @TheInitializer
@decent/server
?@heyitsmeuralex Why are permissions a bitfield? Those are extremely a pain to work with and extend. Can't we just have permissions
be an object whose values are true and false?
permissions: {
manageServer: false,
manageOthers: true,
manageRoles: false,
...
}
Oh, also, having both manageOthers
and manageUsers
is a bit confusing in my opinion. Have updateUsers
and deleteUsers
.
Also super tiny code nit, the "Roles" dropdown should be "Endpoints" in the Roles section.
@towerofnix:
Can't we just have permissions be an object whose values are true and false?
The larger permissions
gets, the network/storage cost of requesting/storing things goes up and up and up and up exponential. They're flags, handle em like flags.
Almost every language has some kind of bitfield/enum type anyway -- even C! "A pain to work with" is not really an argument for JS, either, now - I'm working on a Discord.js-like decent.js
library right now for us to use in the client (#259), which would turn the permissions bitfield into an object just like that.
Also, no, they're not hard. At all!
const permissions = // ...
const canManageServer = (permissions & MANAGE_SERVER) === MANAGE_SERVER
Oh, also, having both manageOthers and manageUsers is a bit confusing in my opinion. Have updateUsers and deleteUsers.
Will replace both with a single MANAGE_USERS
.
@heyitsmeuralex Just to clarify, what happens if we want 33 types of permissions? And what happens if we decide we want to get rid of a particular permission? Say we get rid of the permission that was taking up bit 10 - does bit 10 become "dead", an obsolete bit that has no meaning but cannot be overwritten for the purpose of a new setting, because that could cause issues on old databases?
Just to clarify, what happens if we want 33 types of permissions?
We make the int 64-bit. Minor change. If JavaScript had unsigned integers it'd be even better. Note: Discord uses a 53-bit bitfield for permissions.
cannot be overwritten for the purpose of a new setting, because that could cause issues on old databases?
For that we'd bump the major version if it came to it.
The larger permissions gets, the network/storage cost of requesting/storing things goes up and up and up and up exponential.
Also, how is the storage exponential? (Storage of all roles) = (Number of roles) * (Number of permissions). There's no exponent there.
Also also, do we want to support unset and "negative" permissions? For example, how could I make a "muted" role, which strictly disables users from sending messages or reacting, but still lets them read messages? And how could this realistically be implemented with a bitset? After all, there are now three states each permission can be in: set to true, set to false, unset. Bitsets obviously can't represent that; and objects do gracefully (unset permissions simply wouldn't exist on the permissions object!).
Storing a hashmap >>>>>>>>>> a 64-bit integer.
how could I make a "muted" role, which strictly disables users from sending messages or reacting
Roles would work like Object.assign - [ 0x08, 0x40 ]
get &
ed left-to-right and produce 0x48
. I understand what you mean there, and that's an interesting thought. I just don't think it's worth how huge requests would become if we start sending 64-key objects down the line.
Frankly, why do you care about hashmap size when we are sending thousands of messages?
Size on the network.
Well yes, but clients are receiving thousands of messages too. It's incomparable to the forty-or-so bytes from some permissions. (This very message is greatly bigger than the stringified hash map for {sendMessages: false, addReactions: false}
!)
Eh, I still don't see enough benefits to constitute it. Discord (extreme webscale) gets by with a bitfield.
No need to deal with major version bumps, so (significantly!) better server-client compatibility?
Actually, no, changing a permission's meaning wouldn't be a breaking change - what do clients use the permission for? They're entirely server-side.
To be nice we'd probably deprecate its usage in client libraries before we delet it though.
Will we ever even reach 64 separate permissions globally?
The client uses the permission system for the UI for updating role permissions, and for determining what the logged-in user can or can't do. (For example, the send-message input/button should be disabled when the user does not have the permission to send messages.)
And that's if we somehow reach 64 separate global permissions. I'd call it a non-issue.
Plus, were we to actually get to that point, we could defer the PR changing the permission to the next major release, which will happen eventually.
Why impose the artificial limit of 64 maximum, and make it significantly harder for developers to understand (e.g. people who aren't super experienced with bitfields), in the first place? The benefits are moot, since they relate strictly to network/storage usage, and that is irrelevant when you take into account messages.
...fine. You win :package: If you change the docs to reflect it
A related topic: what about role priority?
Suppose I have these three roles:
Can send messages? | Can read messages? | |
---|---|---|
"Everyone" role | No | No |
"Chat" role | Yes | Yes |
"Muted" role | No | (Unset) |
How do these stack?
Suppose somebody has both the "chat" and "muted" roles, as well as the "everyone" role, since all users are inherently affected by that. Obviously, what we want is that the user can view but not send messages. But how is that computed?
We could say "if there is any occurrence of No, then revoke this permission". So users who have "Chat" have Yes to both, but users who have "Muted" have No to "send messages", so they can't send messages. But this doesn't work, because everyone already has a No for both permissions - from the "Everyone" role.
I'm pretty sure Discord handles this by using a role priority system: roles which are closer to the head of the list (index 0; the top of the <ul>
) are considered to have a higher priority than roles below.
Suppose we order our roles so that "Muted" has a higher priority than "Chat", and both are higher priority than "Everyone". Thus the computed permissions are:
Starting from the most prioritized role, as soon as a permission that is not unset is encountered, that is used as the final computed permission.
Describing Discord's system this way does make it seem a little complicated, but I think it's relatively intuitive to use, and I can't immediately think another way to compute the same results.
:+1:
Looks good. Can you:
{ default: Boolean }
option on roles.
Users
, Everyone
, and Guests
(not logged in){ color: Color }
to roles and channels.
I want to get the API spec stabilized now, even before
@decent/server
has caught up to it. We've already got @MegaApuTurkUltra and co considering a C++ server implementation, and I don't want people to have to rewrite loads of stuff just because of a silly, breaking sub-1.0.0 PR here.After the spec is drafted we can release it in a branch as a prerelease -
1.0.0-preview
- or something, then properly release it once@decent/server
has caught up. Note that everything after this initial release must adhere to semver!To-do list:
Date.now()
format{ type }
/api/users/:id/mentions?skip=10&limit=5
etc?permissionLevel
and authorization; use roles/permissions insteadWork is going on in the
1.0.0
branch.