Permission Groups#2587
Conversation
There was a problem hiding this comment.
I would suggest to add this to the group command instead, maybe /group <whitelist/blacklist> <add/remove> <groupName> <path>
There was a problem hiding this comment.
I did /group <whitelist/blacklist> <groupName> <add/remove/[blank]> <path>
I switch groupName and add/remove/[blank] for easy looking up if [blank] is used. but If you really want the other way around I can look into that
| } | ||
|
|
||
| pub fn getGroup(name: []const u8) error{GroupNotFound}!*PermissionGroup { | ||
| return groups.getPtr(name) orelse return error.GroupNotFound; |
There was a problem hiding this comment.
hashmaps don't have stable pointers
There was a problem hiding this comment.
I added the assert but thats probaly not enough right? I am sorry I am not that knowledgeable to know what would be the best course of action here.
There was a problem hiding this comment.
I finally brought myself back to this pr. I have changed the hashmap from StringHashMapUnmanaged(PermissionGroup) to StringHashMapUnmanaged(*PermissionGroup).
This should from my understanding fix the problem with the stable pointers
| } | ||
|
|
||
| pub fn loadPermissionGroups(self: *ServerWorld) !void { | ||
| const path = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/groups.zig.zon", .{self.path}) catch unreachable; |
There was a problem hiding this comment.
before we put everything into a single file, I would like to at least see an estimation of how big the file could be on a large server (for comparison the survival playtest despite having a peak of only around 10 concurrent players has seen over 500 distinct accounts).
There was a problem hiding this comment.
Its for me the question what these groups will be used for. If we say only for command permissions I don't see the file being big. But if we say that for #81 there will actually be a group for every block then the file could grow quite big.
But separating them opens other issues. Should we load all groups at the start of a world? Or should we only load the groups in use? (would prevent commands on them).
There was a problem hiding this comment.
For #81 you should be allowed to assign the same group to multiple blocks. But it will still likely end up at 1 group per player on average.
There was a problem hiding this comment.
So you want seperate files correct? What is then the answer to to the my follow up question about when to load the zon then?
There was a problem hiding this comment.
Since that makes management easier, I think for now we can just load them all into memory.
There was a problem hiding this comment.
after finnally looking back at this. Where should I then save the currentId? in the world.zig.zon?
There was a problem hiding this comment.
I have fixed the problem with a metadata.zon file inisde the group folder.
The groups are now split into seperate files per group.
I do want to update the group commands a bit, but I will do that in a follow up PR (@ syntax, list subcommand to list all groups a player is member of etc)
| source.sendMessage("#ff0000Permission path {s} is not present inside group {s} permission {s}list", .{path, groupName, @tagName(listType)}); | ||
| } | ||
| }, | ||
| } |
There was a problem hiding this comment.
Imo all code paths should end with return, to prevent mistakes in future, where behavior is by mistake added to multiple code paths. This makes sense because all code paths are mutually exclusive. Such change would also flatten the structure a little bit by moving else contents directly into function body. Definitely not critical tho.
| } | ||
| if (pathOp) |_op| { | ||
| switch (_op) { | ||
| .add => group.permissions.addPermission(listType, path), |
There was a problem hiding this comment.
Please add feedback to all positive paths.
"Permission path added to group X" or alike.
| allocator.destroy(self); | ||
| } | ||
|
|
||
| pub fn hasPermission(self: *PermissionGroup, permissionPath: []const u8) Permissions.PermissionResult { |
There was a problem hiding this comment.
I think this is excessively descriptive. You have permission.zig PermissionGroup, addPermission and permissionPath. We get it, permissions, we don't need to repeat it 4 times unless there is ambiguity. If there is, we should be consistent, so you should savePermissionGroup instead of saveGroups.
But there isn't. It can be Group, hasPermission, path and it should be fine.
| while (it.next()) |group| { | ||
| const path = std.fmt.allocPrint(allocator.allocator, "{s}/{d}.zon", .{groupsPath, group.value_ptr.*.id}) catch unreachable; | ||
| defer allocator.free(path); | ||
| var groupZon: ZonElement = .initObject(allocator); |
There was a problem hiding this comment.
Move into PermissionGroup next to fromZon as toZon
|
|
||
| pub fn removeFromGroup(self: *User, groupName: []const u8) bool { | ||
| sync.threadContext.assertCorrectContext(.server); | ||
| const slice = (self.permissionGroups.getKeyPtr(groupName) orelse return false).*; |
There was a problem hiding this comment.
Variable names should carry the logical meaning of their content, not the type they contain. Type system already describe contained type, you can retrieve it at any time using language server. Comptime type parameters are an exception, as zig doesn't have contracts/protocols/interfaces, but that's not what we have here.
After this lengthy explanation, please rename to... Idk, groupNamePtr? Not sure what it contains.
| defer groupDir.close(); | ||
| var iterator = groupDir.iterate(); | ||
| while (try iterator.next()) |file| { | ||
| if (file.kind == .file and std.mem.endsWith(u8, file.name, ".zon")) { |
There was a problem hiding this comment.
Would be better to make two guard clauses out of that condition.
IntegratedQuantum
left a comment
There was a problem hiding this comment.
In order to keep the line changes down, could you please split this into 3 PRs:
- one only adds the core groups system and saving/loading in the world
- one adds the changes to the
User - one adds the
groupcommand
| if (file.kind == .file and std.mem.endsWith(u8, file.name, ".zon")) { | ||
| const zon = try groupDir.readToZon(main.stackAllocator, file.name); | ||
| defer zon.deinit(main.stackAllocator); | ||
| if (std.mem.eql(u8, file.name, "metadata.zon")) continue; |
There was a problem hiding this comment.
This is stored outside the groups subdirectory, so no need to check for it here right?
There was a problem hiding this comment.
no. its inside the groups folder.
There was a problem hiding this comment.
It seems I saved it inside and tried to load it outside.... I don't know how i didn't see that. But I do want it to b inside the folder
| try files.cubyzDir().writeZon(path, worldData); | ||
| } | ||
|
|
||
| pub fn loadPermissionGroups(_: *ServerWorld, dir: main.files.Dir) !void { |
There was a problem hiding this comment.
I think this function should also be in permission.zig.
|
|
||
| pub fn init(allocator: NeverFailingAllocator) *PermissionGroup { | ||
| sync.threadContext.assertCorrectContext(.server); | ||
| currentId += 1; |
There was a problem hiding this comment.
Should immediately save the metadata file.
| try std.testing.expectError(error.GroupNotFound, getGroup("root")); | ||
| } | ||
|
|
||
| test "invalidGroupPermissionEmptyGroups" { |
There was a problem hiding this comment.
Also test the success case at least once.
| try self.saveWorldConfig(); | ||
|
|
||
| try self.saveAllPlayers(); | ||
| try self.savePermissionGroups(); |
There was a problem hiding this comment.
Should be done if and only if the data changed.
|
Ok I did a few changes, here is a summary:
|
Follow Up PR for #2530
This adds the rest from the
permissionLayerPR which was split of. For Documentation see #2530