Added permission system #106

Merged
thetxt merged 8 commits from Fix448/oxide:permission into dev 2026-06-25 17:53:29 +02:00
Contributor

Added permission system with /op and /deop command

Added permission system with /op and /deop command
added permission system
All checks were successful
Build, test and push / formatting (pull_request_target) Successful in 9s
Build, test and push / clippy (pull_request_target) Successful in 56s
Build, test and push / test (pull_request_target) Successful in 2m23s
9b30cf433e
thetxt self-assigned this 2026-06-01 18:11:05 +02:00
thetxt added this to the 0.9.0 milestone 2026-06-01 18:11:10 +02:00
@ -138,6 +139,7 @@ impl Clone for Player {
dimension: self.dimension.clone(),
loaded_chunks: self.loaded_chunks.clone(),
portal_cooldown: self.portal_cooldown,
permission: Permission::Everyone,
Owner

this should be self.permission

this should be self.permission
Owner

thanks for contributing! Please talk to me first in the future, to ensure that I can merge your changes.
From my quick look I only found one minor thing that should be fixed (attached a comment in the review).
Other than that I think this is missing saving/loading permissions to/from disk. Ideally using the vanilla ops file format

thanks for contributing! Please talk to me first in the future, to ensure that I can merge your changes. From my quick look I only found one minor thing that should be fixed (attached a comment in the review). Other than that I think this is missing saving/loading permissions to/from disk. Ideally using the vanilla ops file format
Fix448 changed title from Added permission system to WIP: Added permission system 2026-06-01 20:08:15 +02:00
Fix448 changed title from WIP: Added permission system to Added permission system 2026-06-01 20:08:46 +02:00
Author
Contributor

Would it be OK to use serde and serde_json for the ops.json file?

Would it be OK to use serde and serde_json for the ops.json file?
changed Permission::Everyone in clone()
All checks were successful
Build, test and push / formatting (pull_request_target) Successful in 9s
Build, test and push / clippy (pull_request_target) Successful in 59s
Build, test and push / test (pull_request_target) Successful in 2m21s
96d2701a95
Owner

@Fix448 wrote in #106 (comment):

Would it be OK to use serde and serde_json for the ops.json file?

I would prefer to not add additional dependencies if we can avoid it

@Fix448 wrote in https://git.thetxt.io/thetxt/oxide/pulls/106#issuecomment-780: > Would it be OK to use serde and serde_json for the ops.json file? I would prefer to not add additional dependencies if we can avoid it
Author
Contributor

I don't think we can avoid it in the long run.

I don't think we can avoid it in the long run.
Owner

okay, fine

okay, fine
Moved config to /lib/src/
All checks were successful
Build, test and push / formatting (pull_request_target) Successful in 9s
Build, test and push / clippy (pull_request_target) Successful in 56s
Build, test and push / test (pull_request_target) Successful in 2m22s
4a31c0f533
Owner

sorry for the delay... I think it makes more sense to load the permissions of a player from disk when they join as a part of lib::types::player::Player::new

And then everything that is now in lib/src/config/ops.rs should probably just be part of a new lib/src/types/permissions.rs file with the enum definition from lib/src/types/command.rs also merged in there

Then the player should probably have set_permission method that also calls the appropriate function to save to disk. This way there is only one function call to set a permission and save it, instead of having to think of both.

Please let me know if overlooked something or if anything I wrote didnt make sense

sorry for the delay... I think it makes more sense to load the permissions of a player from disk when they join as a part of lib::types::player::Player::new And then everything that is now in lib/src/config/ops.rs should probably just be part of a new lib/src/types/permissions.rs file with the enum definition from lib/src/types/command.rs also merged in there Then the player should probably have set_permission method that also calls the appropriate function to save to disk. This way there is only one function call to set a permission and save it, instead of having to think of both. Please let me know if overlooked something or if anything I wrote didnt make sense
Contributor

I just wanted to ask why you have so many diffrent permissions because in minecraft there are only Player Operator and Console. An if oxide in the future maybe gets a plugins system or like that a person can make a luckperms port or smth like that.

I just wanted to ask why you have so many diffrent permissions because in minecraft there are only Player Operator and Console. An if oxide in the future maybe gets a plugins system or like that a person can make a luckperms port or smth like that.
Author
Contributor

Minecraft actually has all of these permissions internally, as described on the corresponding Minecraft Wiki page, but you can only change them by editing the file.

Minecraft actually has all of these permissions internally, as described on the corresponding [Minecraft Wiki page](https://minecraft.wiki/w/Permission_level), but you can only change them by editing the file.
Owner

yeah thats correct, but it reminds that I do manually send the EntityEvent packet to set the permission level to 4. This should also be updated to send the correct level down to the client

I found those in the Player::change_dimsion Player::respawn and new_player.rs files (or grep for //set op permission level 4)

yeah thats correct, but it reminds that I do manually send the EntityEvent packet to set the permission level to 4. This should also be updated to send the correct level down to the client I found those in the Player::change_dimsion Player::respawn and new_player.rs files (or grep for //set op permission level 4)
send correct permission data to player
All checks were successful
Build, test and push / formatting (pull_request_target) Successful in 10s
Build, test and push / clippy (pull_request_target) Successful in 1m0s
Build, test and push / test (pull_request_target) Successful in 2m23s
1ccd5ed311
Author
Contributor

While developing, I ran into two issues:

@thetxt wrote in #106 (comment):

sorry for the delay... I think it makes more sense to load the permissions of a player from disk when they join as a part of lib::types::player::Player::new

In my tests, this new method was only called if the player had not already joined (I don't know if this is intended).

Then the player should probably have set_permission method that also calls the appropriate function to save to disk. This way there is only one function call to set a permission and save it, instead of having to think of both.

This method isn't complete, because I cannot send the lib::packets::clientbound::play::Commands packet from the lib crate, as the get_command_packet_data is in the server crate

While developing, I ran into two issues: @thetxt wrote in https://git.thetxt.io/thetxt/oxide/pulls/106#issuecomment-787: > sorry for the delay... I think it makes more sense to load the permissions of a player from disk when they join as a part of lib::types::player::Player::new In my tests, this new method was only called if the player had not already joined (I don't know if this is intended). > Then the player should probably have set_permission method that also calls the appropriate function to save to disk. This way there is only one function call to set a permission and save it, instead of having to think of both. This method isn't complete, because I cannot send the `lib::packets::clientbound::play::Commands` packet from the lib crate, as the `get_command_packet_data` is in the server crate
Owner

In my tests, this new method was only called if the player had not already joined (I don't know if this is intended).
I just added a println to the start of the new method and it was triggered for me with an existing player. I also wouldnt know how else the player would get loaded.
Maybe you got tripped up by the let Ok(mut file) = File::open(Player::get_playerdata_path(uuid)) else { at the start which runs some extra code if the player doesnt have a file yet?

This method isn't complete, because I cannot send the lib::packets::clientbound::play::Commands packet from the lib crate, as the get_command_packet_data is in the server crate
Im not quite sure what the best way to solve this is at the moment. I think its fine if you add a comment for now. Then I may look at it in the future. Or I will create a separate issue for it before merging this pr

> In my tests, this new method was only called if the player had not already joined (I don't know if this is intended). I just added a println to the start of the new method and it was triggered for me with an existing player. I also wouldnt know how else the player would get loaded. Maybe you got tripped up by the `let Ok(mut file) = File::open(Player::get_playerdata_path(uuid)) else {` at the start which runs some extra code if the player doesnt have a file yet? > This method isn't complete, because I cannot send the lib::packets::clientbound::play::Commands packet from the lib crate, as the get_command_packet_data is in the server crate Im not quite sure what the best way to solve this is at the moment. I think its fine if you add a comment for now. Then I may look at it in the future. Or I will create a separate issue for it before merging this pr
add comment to Player::set_permission
All checks were successful
Build, test and push / formatting (pull_request_target) Successful in 10s
Build, test and push / clippy (pull_request_target) Successful in 57s
Build, test and push / test (pull_request_target) Successful in 2m23s
4c7b15a4e5
Owner

looks good so far, will do some testing in the following days and then merge. Thanks :)

looks good so far, will do some testing in the following days and then merge. Thanks :)
Owner

just did some testing and everything worked fine, just that there is no autocomplete for the /op and /deop command players names and that the search is also case sensitive (although I think thats also the case in vanilla, based on short testing).
According to the generated files from the official server they use the game profile parser, although there seems to be some special sauce to autocomplete existing ops in the /deop command. But I think that isn't very important right now

just did some testing and everything worked fine, just that there is no autocomplete for the /op and /deop command players names and that the search is also case sensitive (although I think thats also the case in vanilla, based on short testing). According to the generated files from the official server they use the game profile parser, although there seems to be some special sauce to autocomplete existing ops in the /deop command. But I think that isn't very important right now
thetxt referenced this pull request from a commit 2026-06-25 17:58:01 +02:00
Sign in to join this conversation.
No description provided.