init
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
# Schema
|
||||
|
||||
Four MySQL tables, created on `start()` via `CREATE TABLE IF NOT EXISTS`. Prefixed `ranks_` (except for `ranks` itself).
|
||||
|
||||
## ranks
|
||||
|
||||
```sql
|
||||
CREATE TABLE IF NOT EXISTS ranks (
|
||||
id VARCHAR(64) NOT NULL,
|
||||
parent_id VARCHAR(64) NULL,
|
||||
display_name VARCHAR(64) NOT NULL,
|
||||
prefix VARCHAR(64) NOT NULL DEFAULT '',
|
||||
priority INT NOT NULL DEFAULT 0,
|
||||
built_in TINYINT(1) NOT NULL DEFAULT 0,
|
||||
created_at BIGINT NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_priority (priority)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
||||
```
|
||||
|
||||
- `id` is the canonical group name. Hytale's built-ins use the `hytale:` prefix (e.g. `hytale:Admin`); your custom ranks should just be lowercase (e.g. `vip`).
|
||||
- `parent_id` is null or another rank id. Inheritance walks the chain.
|
||||
- `built_in = 1` rows can't be deleted via `/rank delete`. Set by the bootstrap seeding.
|
||||
|
||||
## ranks_perms
|
||||
|
||||
```sql
|
||||
CREATE TABLE IF NOT EXISTS ranks_perms (
|
||||
rank_id VARCHAR(64) NOT NULL,
|
||||
permission VARCHAR(255) NOT NULL,
|
||||
PRIMARY KEY (rank_id, permission),
|
||||
KEY idx_rank (rank_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
||||
```
|
||||
|
||||
Direct permissions for each rank. `*` is the wildcard (`hytale:Admin` has it by default).
|
||||
|
||||
## ranks_user_perms
|
||||
|
||||
```sql
|
||||
CREATE TABLE IF NOT EXISTS ranks_user_perms (
|
||||
user_uuid CHAR(36) NOT NULL,
|
||||
permission VARCHAR(255) NOT NULL,
|
||||
PRIMARY KEY (user_uuid, permission)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
||||
```
|
||||
|
||||
Direct (per-user) permissions, bypassing ranks. We don't expose commands for this in v1 - it exists because the `PermissionProvider` SPI requires it and Hytale's `/perm user add` writes here through `addUserPermissions`.
|
||||
|
||||
## ranks_grants
|
||||
|
||||
```sql
|
||||
CREATE TABLE IF NOT EXISTS ranks_grants (
|
||||
id BIGINT NOT NULL AUTO_INCREMENT,
|
||||
user_uuid CHAR(36) NOT NULL,
|
||||
rank_id VARCHAR(64) NOT NULL,
|
||||
granted_at BIGINT NOT NULL,
|
||||
granted_by_uuid CHAR(36) NOT NULL,
|
||||
granted_by_name VARCHAR(64) NOT NULL,
|
||||
expires_at BIGINT NULL,
|
||||
primary_rank TINYINT(1) NOT NULL DEFAULT 0,
|
||||
revoked TINYINT(1) NOT NULL DEFAULT 0,
|
||||
revoked_at BIGINT NULL,
|
||||
revoked_by_uuid CHAR(36) NULL,
|
||||
revoked_by_name VARCHAR(64) NULL,
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_user (user_uuid),
|
||||
KEY idx_user_active (user_uuid, revoked, expires_at)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
||||
```
|
||||
|
||||
Each grant is a separate row. Revokes don't delete - they flip `revoked = 1` and fill the `revoked_*` columns. Useful for "who took my rank away" forensics.
|
||||
|
||||
A user can have multiple active rows of the same rank. `getActiveRankIds` uses `DISTINCT rank_id` so the set is unique.
|
||||
|
||||
`primary_rank` flag is OUR extension on top of Hytale's group model. Only one row per user should have it set; `RankService.setPrimary` enforces this by clearing first.
|
||||
|
||||
## Built-in group seeding
|
||||
|
||||
On first boot, `SchemaBootstrap.seedBuiltInGroups` inserts these rows if absent:
|
||||
|
||||
| id | parent_id | display | built_in |
|
||||
|---|---|---|---|
|
||||
| `hytale:None` | null | None | 1 |
|
||||
| `hytale:Adventurer` | `hytale:None` | Adventurer | 1 |
|
||||
| `hytale:Builder` | `hytale:Adventurer` | Builder | 1 |
|
||||
| `hytale:WorldEditor` | `hytale:Builder` | WorldEditor | 1 |
|
||||
| `hytale:ServerEditor` | `hytale:WorldEditor` | ServerEditor | 1 |
|
||||
| `hytale:Admin` | `hytale:ServerEditor` | Admin | 1 |
|
||||
|
||||
And one permission row: `hytale:Admin` → `*` (wildcard).
|
||||
|
||||
This mirrors what Hytale's `HytalePermissionsProvider` ships with - so `/op` continuing to grant Admin and Admin having all perms keeps working without any extra setup.
|
||||
|
||||
## Migrations
|
||||
|
||||
There aren't any yet. If you change the schema, add a `migrations/00X_what.sql` and a small runner in `SchemaBootstrap` that tracks applied migrations in a `ranks_schema_version` table. Don't reach for Flyway/Liquibase until you have at least three migrations.
|
||||
|
||||
## Useful queries
|
||||
|
||||
Active ranks of a player:
|
||||
|
||||
```sql
|
||||
SELECT DISTINCT rank_id
|
||||
FROM ranks_grants
|
||||
WHERE user_uuid = ?
|
||||
AND revoked = 0
|
||||
AND (expires_at IS NULL OR expires_at > UNIX_TIMESTAMP() * 1000)
|
||||
ORDER BY primary_rank DESC, rank_id;
|
||||
```
|
||||
|
||||
How many players hold each rank right now:
|
||||
|
||||
```sql
|
||||
SELECT rank_id, COUNT(DISTINCT user_uuid) AS holders
|
||||
FROM ranks_grants
|
||||
WHERE revoked = 0
|
||||
AND (expires_at IS NULL OR expires_at > UNIX_TIMESTAMP() * 1000)
|
||||
GROUP BY rank_id
|
||||
ORDER BY holders DESC;
|
||||
```
|
||||
|
||||
All-time grants by staff member (audit):
|
||||
|
||||
```sql
|
||||
SELECT granted_at, user_uuid, rank_id, expires_at
|
||||
FROM ranks_grants
|
||||
WHERE granted_by_name = 'StaffName'
|
||||
ORDER BY granted_at DESC;
|
||||
```
|
||||
Reference in New Issue
Block a user