Skip to content

Commit 8da0a1f

Browse files
committed
Get rid of capturing lambda's in the security system
1 parent caf58da commit 8da0a1f

File tree

6 files changed

+213
-36
lines changed

6 files changed

+213
-36
lines changed

src/api/java/mekanism/api/security/ISecurityUtils.java

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import java.util.ServiceLoader;
44
import java.util.UUID;
5+
import java.util.function.Function;
56
import java.util.function.Supplier;
67
import mekanism.api.annotations.NothingNullByDefault;
78
import net.minecraft.world.entity.player.Player;
@@ -44,6 +45,27 @@ public interface ISecurityUtils {
4445
*/
4546
boolean canAccess(Player player, Supplier<@Nullable ISecurityObject> securityProvider, Supplier<@Nullable IOwnerObject> ownerProvider);
4647

48+
/**
49+
* Checks if a player can access the given capability provider; validating that protection is enabled in the config. Additionally, this method also checks to see if
50+
* operators bypassing security is enabled in the config and if it is, provides access to the player if they are an operator.
51+
*
52+
* @param player Player to check access for.
53+
* @param provider The provider to get the capabilities for.
54+
* @param securityProvider Supplier to get the security capability to check. Can return {@code null} if there is no security.
55+
* @param ownerProvider Supplier to get the owner capability to check. Can return {@code null} if there is no owner.
56+
*
57+
* @return {@code true} if the player can access the given provider.
58+
*
59+
* @implNote This method assumes that if the security is {@link SecurityMode#TRUSTED} and there is a clientside player, then the player can access the
60+
* {@link ISecurityObject security object}. This is done because the list of trusted players is not currently synced to all clients.
61+
* @see #canAccess(UUID, Supplier, Supplier, boolean)
62+
* @see #canAccessObject(Player, ISecurityObject)
63+
* @see #canAccessObject(UUID, ISecurityObject, boolean)
64+
* @since 10.5.18
65+
*/
66+
<PROVIDER> boolean canAccess(Player player, PROVIDER provider, Function<PROVIDER, @Nullable ISecurityObject> securityProvider,
67+
Function<PROVIDER, @Nullable IOwnerObject> ownerProvider);
68+
4769
/**
4870
* Checks if a player can access the given security object; validating that protection is enabled in the config. Additionally, this method also checks to see if
4971
* operators bypassing security is enabled in the config and if it is, provides access to the player if they are an operator.
@@ -81,6 +103,28 @@ public interface ISecurityUtils {
81103
*/
82104
boolean canAccess(@Nullable UUID player, Supplier<@Nullable ISecurityObject> securityProvider, Supplier<@Nullable IOwnerObject> ownerProvider, boolean isClient);
83105

106+
/**
107+
* Checks if a player can access the given capability provider; validating that protection is enabled in the config.
108+
*
109+
* @param player Player to check access for.
110+
* @param provider The provider to get the capabilities for.
111+
* @param securityProvider Supplier to get the security capability to check. Can return {@code null} if there is no security.
112+
* @param ownerProvider Supplier to get the owner capability to check. Can return {@code null} if there is no owner.
113+
* @param isClient {@code true} if this method is being run clientside.
114+
*
115+
* @return {@code true} if the player can access the given provider. If the player is {@code null} this will return {@code true} if the provider's security is
116+
* {@link SecurityMode#PUBLIC}.
117+
*
118+
* @implNote This method assumes that if the security is {@link SecurityMode#TRUSTED} and there is a player and {@code isClient} is {@code true}, then the player can
119+
* access the {@link ISecurityObject security object}. This is done because the list of trusted players is not currently synced to all clients.
120+
* @see #canAccess(Player, Supplier, Supplier)
121+
* @see #canAccessObject(Player, ISecurityObject)
122+
* @see #canAccessObject(UUID, ISecurityObject, boolean)
123+
* @since 10.5.18
124+
*/
125+
<PROVIDER> boolean canAccess(@Nullable UUID player, PROVIDER provider, Function<PROVIDER, @Nullable ISecurityObject> securityProvider,
126+
Function<PROVIDER, @Nullable IOwnerObject> ownerProvider, boolean isClient);
127+
84128
/**
85129
* Checks if a player can access the given security object; validating that protection is enabled in the config.
86130
*
@@ -134,6 +178,29 @@ public interface ISecurityUtils {
134178
*/
135179
SecurityMode getSecurityMode(Supplier<@Nullable ISecurityObject> securityProvider, Supplier<@Nullable IOwnerObject> ownerProvider, boolean isClient);
136180

181+
/**
182+
* Gets the "effective" security mode for a given provider. If no provider is given, or it does not expose a {@link ISecurityObject security object}, then the
183+
* security will be assumed to be {@link SecurityMode#PUBLIC} <em>unless</em> an {@link IOwnerObject} is exposed, in which case the security will be assumed
184+
* {@link SecurityMode#PRIVATE} if protection is enabled.
185+
* <br><br>
186+
* When a {@link ISecurityObject security object} is exposed; this method is <em>different</em> from just querying {@link ISecurityObject#getSecurityMode()} as this
187+
* method takes into account whether protection is disabled in the config and whether the owner of the {@link ISecurityObject} has their security frequency configured
188+
* to override the access level of less restrictive {@link ISecurityObject security objects}.
189+
*
190+
* @param provider The provider to get the capabilities for.
191+
* @param securityProvider Supplier to get the security capability to check. Can return {@code null} if there is no security.
192+
* @param ownerProvider Supplier to get the owner capability to check. Can return {@code null} if there is no owner.
193+
* @param isClient {@code true} if this method is being run clientside.
194+
*
195+
* @return Effective security mode.
196+
*
197+
* @implNote If the provider is {@code null} or doesn't expose a {@link ISecurityObject security object}, then the returned mode is {@link SecurityMode#PUBLIC}
198+
* @see #getEffectiveSecurityMode(ISecurityObject, boolean)
199+
* @since 10.5.18
200+
*/
201+
<PROVIDER> SecurityMode getSecurityMode(PROVIDER provider, Function<PROVIDER, @Nullable ISecurityObject> securityProvider,
202+
Function<PROVIDER, @Nullable IOwnerObject> ownerProvider, boolean isClient);
203+
137204
/**
138205
* Gets the "effective" security mode for a given object. This is <em>different</em> from just querying {@link ISecurityObject#getSecurityMode()} as this method takes
139206
* into account whether protection is disabled in the config and whether the owner of the {@link ISecurityObject} has their security frequency configured to override

src/api/java/mekanism/api/security/ITypedSecurityUtils.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package mekanism.api.security;
22

33
import java.util.UUID;
4+
import java.util.function.Function;
45
import java.util.function.Supplier;
56
import mekanism.api.annotations.NothingNullByDefault;
67
import net.minecraft.world.entity.player.Player;
@@ -47,6 +48,8 @@ public interface ITypedSecurityUtils<PROVIDER> {
4748
* @implNote This method assumes that if the security is {@link SecurityMode#TRUSTED} and there is a clientside player, then the player can access the
4849
* {@link ISecurityObject security object}. This is done because the list of trusted players is not currently synced to all clients.
4950
* @see #canAccess(UUID, Object, boolean)
51+
* @see ISecurityUtils#canAccess(Player, Object, Function, Function)
52+
* @see ISecurityUtils#canAccess(UUID, Object, Function, Function, boolean)
5053
* @see ISecurityUtils#canAccess(Player, Supplier, Supplier)
5154
* @see ISecurityUtils#canAccess(UUID, Supplier, Supplier, boolean)
5255
* @see ISecurityUtils#canAccessObject(Player, ISecurityObject)
@@ -58,7 +61,7 @@ default boolean canAccess(Player player, @Nullable PROVIDER provider) {
5861
if (provider == null) {
5962
return true;
6063
}
61-
return ISecurityUtils.INSTANCE.canAccess(player, () -> securityCapability(provider), () -> ownerCapability(provider));
64+
return ISecurityUtils.INSTANCE.canAccess(player, provider, this::securityCapability, this::ownerCapability);
6265
}
6366

6467
/**
@@ -74,6 +77,8 @@ default boolean canAccess(Player player, @Nullable PROVIDER provider) {
7477
* @implNote This method assumes that if the security is {@link SecurityMode#TRUSTED} and there is a player and {@code isClient} is {@code true}, then the player can
7578
* access the {@link ISecurityObject security object}. This is done because the list of trusted players is not currently synced to all clients.
7679
* @see #canAccess(Player, Object)
80+
* @see ISecurityUtils#canAccess(Player, Object, Function, Function)
81+
* @see ISecurityUtils#canAccess(UUID, Object, Function, Function, boolean)
7782
* @see ISecurityUtils#canAccess(Player, Supplier, Supplier)
7883
* @see ISecurityUtils#canAccess(UUID, Supplier, Supplier, boolean)
7984
* @see ISecurityUtils#canAccessObject(Player, ISecurityObject)
@@ -85,7 +90,7 @@ default boolean canAccess(@Nullable UUID player, @Nullable PROVIDER provider, bo
8590
if (provider == null) {
8691
return true;
8792
}
88-
return ISecurityUtils.INSTANCE.canAccess(player, () -> securityCapability(provider), () -> ownerCapability(provider), isClient);
93+
return ISecurityUtils.INSTANCE.canAccess(player, provider, this::securityCapability, this::ownerCapability, isClient);
8994
}
9095

9196
/**
@@ -123,13 +128,14 @@ default UUID getOwnerUUID(@Nullable PROVIDER provider) {
123128
*
124129
* @implNote If the provider is {@code null} or doesn't expose a {@link ISecurityObject security object}, then the returned mode is {@link SecurityMode#PUBLIC}
125130
* @see ISecurityUtils#getSecurityMode(Supplier, Supplier, boolean)
131+
* @see ISecurityUtils#getSecurityMode(Object, Function, Function, boolean)
126132
* @see ISecurityUtils#getEffectiveSecurityMode(ISecurityObject, boolean)
127133
*/
128134
default SecurityMode getSecurityMode(@Nullable PROVIDER provider, boolean isClient) {
129135
if (provider == null) {
130136
return SecurityMode.PUBLIC;
131137
}
132-
return ISecurityUtils.INSTANCE.getSecurityMode(() -> securityCapability(provider), () -> ownerCapability(provider), isClient);
138+
return ISecurityUtils.INSTANCE.getSecurityMode(provider, this::securityCapability, this::ownerCapability, isClient);
133139
}
134140

135141
/**

src/main/java/mekanism/client/gui/element/tab/GuiSecurityTab.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ public ISecurityObject securityObject() {
159159

160160
SecurityMode securityMode() {
161161
OBJECT object = objectSupplier.get();
162-
return ISecurityUtils.INSTANCE.getSecurityMode(() -> securityProvider.apply(object), () -> ownerProvider.apply(object), true);
162+
return ISecurityUtils.INSTANCE.getSecurityMode(object, securityProvider, ownerProvider, true);
163163
}
164164
}
165165
}

src/main/java/mekanism/common/lib/security/BlockSecurityUtils.java

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package mekanism.common.lib.security;
22

3+
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
34
import java.util.Set;
45
import java.util.UUID;
56
import mekanism.api.annotations.NothingNullByDefault;
@@ -15,7 +16,6 @@
1516
import net.minecraft.world.level.block.entity.BlockEntity;
1617
import net.minecraft.world.level.block.state.BlockState;
1718
import net.neoforged.neoforge.capabilities.BlockCapability;
18-
import net.neoforged.neoforge.common.util.Lazy;
1919
import org.jetbrains.annotations.Nullable;
2020

2121
/**
@@ -44,28 +44,43 @@ public BlockCapability<ISecurityObject, Void> securityCapability() {
4444
@Override
4545
public boolean canAccess(Player player, Level level, BlockPos pos, @Nullable BlockState state, @Nullable BlockEntity blockEntity) {
4646
CachingCapabilityLookup lookup = new CachingCapabilityLookup(level, pos, state, blockEntity);
47-
return ISecurityUtils.INSTANCE.canAccess(player, lookup::securityCapability, lookup::ownerCapability);
47+
return ISecurityUtils.INSTANCE.canAccess(player, lookup, CachingCapabilityLookup::securityCapability, CachingCapabilityLookup::ownerCapability);
4848
}
4949

5050
@Override
5151
public boolean canAccess(@Nullable UUID player, Level level, BlockPos pos, @Nullable BlockState state, @Nullable BlockEntity blockEntity) {
5252
CachingCapabilityLookup lookup = new CachingCapabilityLookup(level, pos, state, blockEntity);
53-
return ISecurityUtils.INSTANCE.canAccess(player, lookup::securityCapability, lookup::ownerCapability, level.isClientSide());
53+
return ISecurityUtils.INSTANCE.canAccess(player, lookup, CachingCapabilityLookup::securityCapability, CachingCapabilityLookup::ownerCapability, level.isClientSide());
5454
}
5555

5656
@Override
5757
public SecurityMode getSecurityMode(Level level, BlockPos pos, @Nullable BlockState state, @Nullable BlockEntity blockEntity) {
5858
CachingCapabilityLookup lookup = new CachingCapabilityLookup(level, pos, state, blockEntity);
59-
return ISecurityUtils.INSTANCE.getSecurityMode(lookup::securityCapability, lookup::ownerCapability, level.isClientSide());
59+
return ISecurityUtils.INSTANCE.getSecurityMode(lookup, CachingCapabilityLookup::securityCapability, CachingCapabilityLookup::ownerCapability, level.isClientSide());
6060
}
6161

6262
public void securityChanged(Set<Player> playersUsing, Level level, BlockPos pos, @Nullable BlockEntity target, SecurityMode old, SecurityMode mode) {
63-
SecurityUtils.get().securityChanged(playersUsing, player -> canAccess(player, level, pos, target), old, mode);
63+
//If the mode changed and the new security mode is more restrictive than the old one
64+
// and there are players using the security object
65+
if (!playersUsing.isEmpty() && ISecurityUtils.INSTANCE.moreRestrictive(old, mode)) {
66+
//then double check that all the players are actually supposed to be able to access the GUI
67+
//Note: We directly store and then query our caching capability lookup so that we don't have to re-look up
68+
// the capabilities of the block for each player
69+
CachingCapabilityLookup lookup = new CachingCapabilityLookup(level, pos, null, target);
70+
for (Player player : new ObjectOpenHashSet<>(playersUsing)) {
71+
if (!ISecurityUtils.INSTANCE.canAccess(player, lookup, CachingCapabilityLookup::securityCapability, CachingCapabilityLookup::ownerCapability)) {
72+
//and if they can't then boot them out
73+
player.closeContainer();
74+
}
75+
}
76+
}
6477
}
6578

6679
/**
6780
* Used to allow caching the block state and block entity lookup between security capability and owner capability lookup. That way if the block queried does not
6881
* expose a security capability at the given position we don't have to do more world lookups when querying if the block exposes an owner capability.
82+
*
83+
* @implNote As this caches the security and owner objects, this is not suitable for persisting between calls.
6984
*/
7085
private static class CachingCapabilityLookup {
7186

@@ -74,12 +89,26 @@ private record BlockTarget(BlockState state, @Nullable BlockEntity blockEntity)
7489

7590
private final Level level;
7691
private final BlockPos pos;
77-
private final Lazy<BlockTarget> lazyTarget;
92+
@Nullable
93+
private final BlockState knownState;
94+
@Nullable
95+
private final BlockEntity knownBlockEntity;
96+
@Nullable
97+
private BlockTarget target;
98+
@Nullable
99+
private ISecurityObject securityObject;
100+
@Nullable
101+
private IOwnerObject ownerObject;
78102

79103
CachingCapabilityLookup(Level level, BlockPos pos, @Nullable BlockState knownState, @Nullable BlockEntity knownBlockEntity) {
80104
this.level = level;
81105
this.pos = pos;
82-
this.lazyTarget = Lazy.of(() -> {
106+
this.knownState = knownState;
107+
this.knownBlockEntity = knownBlockEntity;
108+
}
109+
110+
private BlockTarget getTarget() {
111+
if (target == null) {
83112
// Get block state and block entity if they were not provided
84113
BlockState state = knownState;
85114
BlockEntity blockEntity = knownBlockEntity;
@@ -96,20 +125,27 @@ private record BlockTarget(BlockState state, @Nullable BlockEntity blockEntity)
96125
} else if (state == null) {
97126
state = blockEntity.getBlockState();
98127
}
99-
return new BlockTarget(state, blockEntity);
100-
});
128+
target = new BlockTarget(state, blockEntity);
129+
}
130+
return target;
101131
}
102132

103133
@Nullable
104134
ISecurityObject securityCapability() {
105-
BlockTarget blockTarget = lazyTarget.get();
106-
return IBlockSecurityUtils.INSTANCE.securityCapability(level, pos, blockTarget.state(), blockTarget.blockEntity());
135+
if (securityObject == null) {
136+
BlockTarget blockTarget = getTarget();
137+
securityObject = IBlockSecurityUtils.INSTANCE.securityCapability(level, pos, blockTarget.state(), blockTarget.blockEntity());
138+
}
139+
return securityObject;
107140
}
108141

109142
@Nullable
110143
IOwnerObject ownerCapability() {
111-
BlockTarget blockTarget = lazyTarget.get();
112-
return IBlockSecurityUtils.INSTANCE.ownerCapability(level, pos, blockTarget.state(), blockTarget.blockEntity());
144+
if (ownerObject == null) {
145+
BlockTarget blockTarget = getTarget();
146+
ownerObject = IBlockSecurityUtils.INSTANCE.ownerCapability(level, pos, blockTarget.state(), blockTarget.blockEntity());
147+
}
148+
return ownerObject;
113149
}
114150
}
115151
}

src/main/java/mekanism/common/lib/security/EntitySecurityUtils.java

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
package mekanism.common.lib.security;
22

3+
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
34
import java.util.Set;
45
import mekanism.api.annotations.NothingNullByDefault;
56
import mekanism.api.security.IEntitySecurityUtils;
67
import mekanism.api.security.IOwnerObject;
78
import mekanism.api.security.ISecurityObject;
9+
import mekanism.api.security.ISecurityUtils;
810
import mekanism.api.security.SecurityMode;
911
import mekanism.common.capabilities.Capabilities;
1012
import net.minecraft.world.entity.Entity;
1113
import net.minecraft.world.entity.player.Player;
1214
import net.neoforged.neoforge.capabilities.EntityCapability;
15+
import org.jetbrains.annotations.Nullable;
1316

1417
/**
1518
* @apiNote Do not instantiate this class directly as it will be done via the service loader. Instead, access instances of this via {@link IEntitySecurityUtils#INSTANCE}
@@ -35,6 +38,46 @@ public EntityCapability<ISecurityObject, Void> securityCapability() {
3538
}
3639

3740
public void securityChanged(Set<Player> playersUsing, Entity target, SecurityMode old, SecurityMode mode) {
38-
SecurityUtils.get().securityChanged(playersUsing, player -> canAccess(player, target), old, mode);
41+
//If the mode changed and the new security mode is more restrictive than the old one
42+
// and there are players using the security object
43+
if (!playersUsing.isEmpty() && ISecurityUtils.INSTANCE.moreRestrictive(old, mode)) {
44+
//then double check that all the players are actually supposed to be able to access the GUI
45+
CachingCapabilityLookup lookup = new CachingCapabilityLookup(target);
46+
for (Player player : new ObjectOpenHashSet<>(playersUsing)) {
47+
if (!ISecurityUtils.INSTANCE.canAccess(player, lookup, CachingCapabilityLookup::securityCapability, CachingCapabilityLookup::ownerCapability)) {
48+
//and if they can't then boot them out
49+
player.closeContainer();
50+
}
51+
}
52+
}
53+
}
54+
55+
private static class CachingCapabilityLookup {
56+
57+
private final Entity target;
58+
@Nullable
59+
private ISecurityObject securityObject;
60+
@Nullable
61+
private IOwnerObject ownerObject;
62+
63+
public CachingCapabilityLookup(Entity target) {
64+
this.target = target;
65+
}
66+
67+
@Nullable
68+
ISecurityObject securityCapability() {
69+
if (securityObject == null) {
70+
securityObject = IEntitySecurityUtils.INSTANCE.securityCapability(target);
71+
}
72+
return securityObject;
73+
}
74+
75+
@Nullable
76+
IOwnerObject ownerCapability() {
77+
if (ownerObject == null) {
78+
ownerObject = IEntitySecurityUtils.INSTANCE.ownerCapability(target);
79+
}
80+
return ownerObject;
81+
}
3982
}
4083
}

0 commit comments

Comments
 (0)