Skip to content
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
ca75625
Create Fabric Permission API
Patbox Feb 22, 2026
63f2ba3
Improve javadocs, move from interface to a final class, safer Command…
Patbox Feb 26, 2026
4b87796
Merge branch '26.1' into permission-api
Patbox Feb 26, 2026
5f31c28
Merge branch '26.1' into permission-api
Patbox Feb 26, 2026
98e6e08
Async permission check utility methods, mention possible slowness of …
Patbox Feb 27, 2026
07cc1e1
Provide better support for async permission checks on provider side
Patbox Mar 3, 2026
a83a17b
Merge branch '26.1' into permission-api
Patbox Mar 3, 2026
13d5cda
Fix async check (oops)
Patbox Mar 3, 2026
2bf037a
Wrap permission key and codec into a single object, swap from tristat…
Patbox Mar 4, 2026
050baef
Write package-info, test key serialization
Patbox Mar 4, 2026
e002854
Intern keys, don't use supply async
Patbox Mar 6, 2026
ee133e5
Fix interner being initialized too late
Patbox Mar 6, 2026
60cc5eb
a
Patbox Mar 6, 2026
b9d0c87
Mark as experimental
Patbox Mar 12, 2026
77f1229
Merge branch '26.1' into permission-api
Patbox Mar 14, 2026
703a71e
Merge remote-tracking branch 'origin/permission-api' into permission-api
Patbox Mar 14, 2026
1d1cfc3
Drop serializable context keys for now
Patbox Mar 14, 2026
73eab46
Merge branch '26.1' into permission-api
Patbox Mar 21, 2026
9d52cb9
Rework how permissions are resolved, remove async methods, add server…
Patbox Mar 28, 2026
856cf52
Merge branch '26.1' into permission-api
Patbox Mar 28, 2026
49648a4
Checkstyle fix
Patbox Mar 28, 2026
cbeeb1a
Fix not being marked as experimental in fabric.mod.json
Patbox Mar 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions fabric-permission-api-v1/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
version = getSubprojectVersion(project)

loom {
accessWidenerPath = file('src/main/resources/fabric-permission-api-v1.classtweaker')
}

moduleDependencies(project, [
":fabric-api-base"
])

testDependencies(project, [
":fabric-command-api-v2",
":fabric-lifecycle-events-v1"
])
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package net.fabricmc.fabric.api.permission.v1;

import org.jspecify.annotations.Nullable;

/**
* Mutable version of {@link PermissionContext}, intended for creation of custom context conditions.
*/
public interface MutablePermissionContext extends PermissionContext {
<T> MutablePermissionContext set(PermissionContext.Key<T> key, @Nullable T value);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package net.fabricmc.fabric.api.permission.v1;

import java.util.Objects;
import java.util.concurrent.CompletableFuture;

import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.Nullable;

import net.minecraft.resources.Identifier;

import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.impl.permission.PermissionCheckCallbackImpl;

/**
* The event used for getting the permission result for given context.
* Implemented callbacks for this event should be thread safe, as permission methods can be called from another thread.
* Additionally, the execution should be reasonably fast for non-player and online player cases.
* Offline player checks are allowed to be slower and can happen asynchronously.
*
* <p>When implementing this callback, only {@link PermissionCheckCallback#onPermissionCheck} needs to be implemented,
* but for better performance in case of support for async lookup, the {@link PermissionCheckCallback#onAsyncPermissionCheck}
* method should also be implemented. In case it wasn't it will default to running {@link PermissionCheckCallback#onPermissionCheck}
* on current thread.
*
* <p>To check for permissions, you should use dedicated methods from {@link PermissionContextOwner} interface
* and it's implementations over invoking this event.
*/
public interface PermissionCheckCallback {
/**
* Registers the permission callback.
*
* @param callback permission check callback to register
*/
static void register(PermissionCheckCallback callback) {
register(Event.DEFAULT_PHASE, callback);
}

/**
* Registers the permission callback.
*
* @param phase ordering phase to place the callback
* @param callback permission check callback to register
*/
static void register(Identifier phase, PermissionCheckCallback callback) {
Objects.requireNonNull(phase, "phase can't be null!");
Objects.requireNonNull(callback, "callback can't be null!");

PermissionCheckCallbackImpl.MAIN_EVENT.register(phase, callback::onPermissionCheck);
PermissionCheckCallbackImpl.ASYNC_EVENT.register(phase, callback::onAsyncPermissionCheck);
}

/**
* Orders the phases in provided order.
*
* @param firstPhase the id of the phase that should happen first
* @param lastPhase the id of the phase that should happen last
*/
static void addPhaseOrdering(Identifier firstPhase, Identifier lastPhase) {
Objects.requireNonNull(firstPhase, "firstPhase can't be null!");
Objects.requireNonNull(lastPhase, "lastPhase can't be null!");

PermissionCheckCallbackImpl.MAIN_EVENT.addPhaseOrdering(firstPhase, lastPhase);
PermissionCheckCallbackImpl.ASYNC_EVENT.addPhaseOrdering(firstPhase, lastPhase);
}

/**
* Main check method, executes on current thread.
*
* @param context context to check for
* @param permission a permission node representing a permission
* @param <T> type of permission
* @return value of type T if present, null to pass through.
*/
@ApiStatus.OverrideOnly
@Nullable
<T> T onPermissionCheck(PermissionContext context, PermissionNode<T> permission);

/**
* Async permission check method.
*
* @param context context to check for
* @param permission a permission node representing a permission
* @param <T> type of permission
* @return a completable future value of type T if present, null or null containing completable future to quickly pass through to next callback.
*/
@ApiStatus.OverrideOnly
default <T> CompletableFuture<@Nullable T> onAsyncPermissionCheck(PermissionContext context, PermissionNode<T> permission) {
return CompletableFuture.completedFuture(this.onPermissionCheck(context, permission));
}
}
Loading
Loading