-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit d76117f
Showing
27 changed files
with
1,084 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
.DS_Store | ||
.gradle | ||
.idea | ||
build |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
all: | clean jar | ||
|
||
jar: | ||
@./gradlew :jar | ||
|
||
clean: | ||
@./gradlew clean |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
# clientcontext | ||
A BungeeCord plugin that adds [LuckPerms](https://luckperms.net/) contexts for the client version and client type. | ||
|
||
## Contexts | ||
|
||
### client-version | ||
This context specifies the version of Minecraft client that the player is using. | ||
Two values will be assigned: an exact version (e.g. `1.2.1`), and a rough version (e.g. `1.2.x`). | ||
|
||
### client-type | ||
This context specifies the type of Minecraft client being used. | ||
Currently, only two types are supported: `java`, and `bedrock` (through [Geyser](https://geysermc.org/) for Spigot) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
plugins { | ||
id 'java-library' | ||
} | ||
|
||
apply from: 'common.gradle' | ||
|
||
// Parent project should not compile anything. | ||
build.enabled = false | ||
sourceSets { | ||
main { | ||
java { srcDirs = [] } | ||
} | ||
test { | ||
java { srcDirs = [] } | ||
} | ||
} | ||
|
||
// Include all subprojects. | ||
jar { | ||
from { | ||
subprojects.collect { | ||
it.sourceSets.main.output | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
plugins { | ||
id 'java-library' | ||
} | ||
|
||
apply from: '../common.gradle' | ||
apply from: '../common-java.gradle' | ||
|
||
dependencies { | ||
implementation project(':common') | ||
|
||
// Bukkit | ||
implementation 'org.spigotmc:spigot-api:1.16.1-R0.1-SNAPSHOT' | ||
} |
133 changes: 133 additions & 0 deletions
133
bukkit/src/main/java/dev/ethp/clientcontext/BukkitPlugin.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
package dev.ethp.clientcontext; | ||
|
||
import net.luckperms.api.LuckPerms; | ||
import net.luckperms.api.LuckPermsProvider; | ||
import net.luckperms.api.context.ContextCalculator; | ||
import net.luckperms.api.context.ContextConsumer; | ||
import net.luckperms.api.context.ContextManager; | ||
import org.bukkit.entity.Player; | ||
import org.bukkit.event.EventHandler; | ||
import org.bukkit.event.Listener; | ||
import org.bukkit.event.player.PlayerJoinEvent; | ||
import org.bukkit.metadata.FixedMetadataValue; | ||
import org.bukkit.plugin.java.JavaPlugin; | ||
import org.bukkit.plugin.messaging.PluginMessageListener; | ||
import org.jetbrains.annotations.NotNull; | ||
|
||
import java.io.ByteArrayInputStream; | ||
import java.io.ByteArrayOutputStream; | ||
import java.io.DataInputStream; | ||
import java.io.DataOutputStream; | ||
import java.util.HashSet; | ||
import java.util.Set; | ||
import java.util.logging.Level; | ||
|
||
public class BukkitPlugin extends JavaPlugin implements PluginMessageListener, Listener, ContextCalculator<Player> { | ||
|
||
@Override | ||
public void onEnable() { | ||
LuckPerms api = LuckPermsProvider.get(); | ||
ContextManager manager = api.getContextManager(); | ||
|
||
manager.registerCalculator(this); | ||
getServer().getPluginManager().registerEvents(this, this); | ||
getServer().getMessenger().registerIncomingPluginChannel(this, Constants.CHANNEL, this); | ||
getServer().getMessenger().registerOutgoingPluginChannel(this, Constants.CHANNEL); | ||
|
||
// Sync all client info. | ||
for (var player : getServer().getOnlinePlayers()) { | ||
this.requestContexts(player); | ||
} | ||
} | ||
|
||
public void onDisable() { | ||
LuckPerms api = LuckPermsProvider.get(); | ||
ContextManager manager = api.getContextManager(); | ||
|
||
manager.unregisterCalculator(this); | ||
getServer().getMessenger().unregisterIncomingPluginChannel(this); | ||
getServer().getMessenger().unregisterOutgoingPluginChannel(this); | ||
} | ||
|
||
@EventHandler | ||
public void onConnect(PlayerJoinEvent event) { | ||
requestContexts(event.getPlayer()); | ||
} | ||
|
||
/** | ||
* Called by LuckPerms to get the player's context info. | ||
* This will read the context set from the player's metadata. | ||
* | ||
* @param target The player. | ||
* @param consumer The context consumer. | ||
*/ | ||
@SuppressWarnings({"ConstantConditions", "unchecked"}) | ||
@Override | ||
public void calculate(@NotNull Player target, @NotNull ContextConsumer consumer) { | ||
for (var metadata : target.getMetadata(Constants.METADATA_KEY)) { | ||
if (metadata.getOwningPlugin() != this) continue; | ||
for (var context : (Set<ContextData>) metadata.value()) { | ||
consumer.accept(context); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Sends a plugin message to the BungeeCord plugin, asking for it to send over the player's client information. | ||
* | ||
* @param player The player. | ||
*/ | ||
public void requestContexts(@NotNull Player player) { | ||
try { | ||
ByteArrayOutputStream buffer = new ByteArrayOutputStream(); | ||
DataOutputStream out = new DataOutputStream(buffer); | ||
out.writeUTF("fetch"); | ||
|
||
player.sendPluginMessage(this, Constants.CHANNEL, buffer.toByteArray()); | ||
} catch (Exception ex) { | ||
getLogger().log( | ||
Level.WARNING, | ||
"Unable to request contexts for player " + player.getName() + "(" + player.getUniqueId() + ")", | ||
ex | ||
); | ||
} | ||
} | ||
|
||
@Override | ||
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, byte[] message) { | ||
if (!channel.equals(Constants.CHANNEL)) return; | ||
Set<ContextData> contexts = new HashSet<>(); | ||
|
||
// Read the contexts from the BungeeCord plugin. | ||
try { | ||
DataInputStream in = new DataInputStream(new ByteArrayInputStream(message)); | ||
String command = in.readUTF(); | ||
if (!command.equalsIgnoreCase("sync")) { | ||
return; | ||
} | ||
|
||
for (int i = 0, max = in.readShort(); i < max; i++) { | ||
String key = in.readUTF(); | ||
String value = in.readUTF(); | ||
|
||
if (!key.startsWith("client-")) { | ||
// If it doesn't start with "client-", don't set the context for security reasons. | ||
// All contexts from the plugin should start with that. | ||
getLogger().warning("Something tried to send a context that isn't managed by ClientContext."); | ||
getLogger().warning("The user in question is " + player.getName() + " (" + player.getUniqueId() + ")"); | ||
continue; | ||
} | ||
|
||
contexts.add(new ContextData(key, value)); | ||
} | ||
} catch (Exception ex) { | ||
getLogger().log(Level.WARNING, "Unable to read contexts from ClientContext BungeeCord plugin.", ex); | ||
return; | ||
} | ||
|
||
// Update the player's contexts. | ||
player.setMetadata(Constants.METADATA_KEY, new FixedMetadataValue(this, contexts)); | ||
LuckPermsProvider.get().getContextManager().signalContextUpdate(player); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
name: ClientContext | ||
author: eth-p | ||
version: ${version} | ||
main: dev.ethp.clientcontext.BukkitPlugin | ||
api-version: 1.16 | ||
depend: | ||
- LuckPerms |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
plugins { | ||
id 'java-library' | ||
} | ||
|
||
apply from: '../common.gradle' | ||
apply from: '../common-java.gradle' | ||
|
||
dependencies { | ||
implementation project(':common') | ||
|
||
implementation 'org.geysermc:geyser-api:2.0.0-SNAPSHOT' | ||
implementation 'org.geysermc.floodgate:api:2.0-SNAPSHOT' | ||
|
||
// Bungeecord | ||
implementation 'net.md-5:bungeecord-api:1.16-R0.5-SNAPSHOT' | ||
} |
82 changes: 82 additions & 0 deletions
82
bungee/src/main/java/dev/ethp/clientcontext/BungeePlugin.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package dev.ethp.clientcontext; | ||
|
||
import net.luckperms.api.LuckPermsProvider; | ||
import net.luckperms.api.context.ContextCalculator; | ||
import net.luckperms.api.context.ContextManager; | ||
import net.md_5.bungee.api.ProxyServer; | ||
import net.md_5.bungee.api.connection.ProxiedPlayer; | ||
import net.md_5.bungee.api.plugin.Plugin; | ||
|
||
import java.util.HashSet; | ||
import java.util.Set; | ||
import java.util.logging.Level; | ||
|
||
public class BungeePlugin extends Plugin { | ||
private Set<ContextCalculator<ProxiedPlayer>> calculators; | ||
private ContextManager manager; | ||
|
||
@Override | ||
public void onEnable() { | ||
this.calculators = new HashSet<>(); | ||
this.manager = LuckPermsProvider.get().getContextManager(); | ||
|
||
// Register contexts. | ||
registerClientTypeCalculator(); | ||
registerClientVersionCalculator(); | ||
|
||
// Register listener to forward contexts to Bukkit servers. | ||
ProxyServer server = getProxy(); | ||
server.registerChannel(Constants.CHANNEL); | ||
server.getPluginManager().registerListener(this, new ContextSync(this, this.calculators)); | ||
} | ||
|
||
@Override | ||
public void onDisable() { | ||
// Unregister contexts. | ||
for (var calculator : this.calculators) { | ||
this.manager.unregisterCalculator(calculator); | ||
} | ||
|
||
// Unregister listeners. | ||
ProxyServer server = getProxy(); | ||
server.unregisterChannel(Constants.CHANNEL); | ||
server.getPluginManager().unregisterListeners(this); | ||
} | ||
|
||
private void registerCalculator(ContextCalculator<ProxiedPlayer> calculator) { | ||
this.manager.registerCalculator(calculator); | ||
this.calculators.add(calculator); | ||
} | ||
|
||
protected void registerClientTypeCalculator() { | ||
try { | ||
registerCalculator(new ClientTypeContextViaFloodgate()); | ||
getLogger().info("Using Floodgate for client type detection."); | ||
return; | ||
} catch (Throwable t) { | ||
} | ||
|
||
try { | ||
registerCalculator(new ClientTypeContextViaGeyser()); | ||
getLogger().info("Using Geyser for client type detection."); | ||
return; | ||
} catch (Throwable t) { | ||
} | ||
|
||
registerCalculator(new ClientTypeContextFallback()); | ||
getLogger().info("Using fallback for client type detection."); | ||
getLogger().warning("All clients will appear as Java clients."); | ||
} | ||
|
||
protected void registerClientVersionCalculator() { | ||
try { | ||
VersionResolver resolver = new VersionResolver(); | ||
registerCalculator(new ClientVersionContext(resolver)); | ||
} catch (ReflectiveOperationException t) { | ||
getLogger().log(Level.WARNING, "Unable to generate client version map.", t); | ||
} catch (Throwable t) { | ||
getLogger().log(Level.WARNING, "Unable to register client version context.", t); | ||
} | ||
} | ||
|
||
} |
30 changes: 30 additions & 0 deletions
30
bungee/src/main/java/dev/ethp/clientcontext/ClientTypeContextFallback.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package dev.ethp.clientcontext; | ||
|
||
import net.luckperms.api.context.ContextCalculator; | ||
import net.luckperms.api.context.ContextConsumer; | ||
import net.luckperms.api.context.ContextSet; | ||
import net.luckperms.api.context.ImmutableContextSet; | ||
import net.md_5.bungee.api.connection.ProxiedPlayer; | ||
import org.jetbrains.annotations.NotNull; | ||
|
||
/** | ||
* A fallback LuckPerms {@link ContextCalculator} that always specifies "java" as the client type. | ||
* This is used when Geyser fails to load. | ||
*/ | ||
public class ClientTypeContextFallback implements ContextCalculator<ProxiedPlayer> { | ||
|
||
@Override | ||
public void calculate(ProxiedPlayer target, ContextConsumer contextConsumer) { | ||
contextConsumer.accept("client-type", "java"); | ||
} | ||
|
||
@Override | ||
public @NotNull ContextSet estimatePotentialContexts() { | ||
ImmutableContextSet.Builder builder = ImmutableContextSet.builder(); | ||
|
||
builder.add("client-type", "java"); | ||
|
||
return builder.build(); | ||
} | ||
|
||
} |
33 changes: 33 additions & 0 deletions
33
bungee/src/main/java/dev/ethp/clientcontext/ClientTypeContextViaFloodgate.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package dev.ethp.clientcontext; | ||
|
||
import net.luckperms.api.context.ContextCalculator; | ||
import net.luckperms.api.context.ContextConsumer; | ||
import net.luckperms.api.context.ContextSet; | ||
import net.luckperms.api.context.ImmutableContextSet; | ||
import net.md_5.bungee.api.connection.ProxiedPlayer; | ||
import org.geysermc.floodgate.api.FloodgateApi; | ||
import org.jetbrains.annotations.NotNull; | ||
|
||
/** | ||
* A LuckPerms {@link ContextCalculator} that checks if the player is connected to the server as a Java client, or | ||
* as a Bedrock client via GeyserMC. This requires Floodgate to be loaded. | ||
*/ | ||
public class ClientTypeContextViaFloodgate implements ContextCalculator<ProxiedPlayer> { | ||
|
||
@Override | ||
public void calculate(ProxiedPlayer target, ContextConsumer contextConsumer) { | ||
FloodgateApi api = FloodgateApi.getInstance(); | ||
contextConsumer.accept("client-type", api.isFloodgatePlayer(target.getUniqueId()) ? "bedrock" : "java"); | ||
} | ||
|
||
@Override | ||
public @NotNull ContextSet estimatePotentialContexts() { | ||
ImmutableContextSet.Builder builder = ImmutableContextSet.builder(); | ||
|
||
builder.add("client-type", "java"); | ||
builder.add("client-type", "bedrock"); | ||
|
||
return builder.build(); | ||
} | ||
|
||
} |
Oops, something went wrong.