This commit is contained in:
2026-05-26 23:16:33 -04:00
commit 3b023d3c6b
32 changed files with 2931 additions and 0 deletions
+130
View File
@@ -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;
```