mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-08-14 23:57:35 +00:00
Fabric improvements
Mainly in commands - the old permissions file no longer needs to exist.
This commit is contained in:
parent
a612be60aa
commit
254f0da03c
11 changed files with 81 additions and 158 deletions
|
@ -19,6 +19,9 @@ dependencies {
|
|||
// Fabric API. This is technically optional, but you probably want it anyway.
|
||||
modImplementation(libs.fabric.api)
|
||||
|
||||
// This should be in the libs TOML, but something about modImplementation AND include just doesn't work
|
||||
include(modImplementation("me.lucko", "fabric-permissions-api", "0.2-SNAPSHOT"))
|
||||
|
||||
// PSA: Some older mods, compiled on Loom 0.2.1, might have outdated Maven POMs.
|
||||
// You may need to force-disable transitiveness on them.
|
||||
|
||||
|
|
|
@ -25,12 +25,14 @@
|
|||
|
||||
package org.geysermc.geyser.platform.fabric;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.geysermc.geyser.GeyserLogger;
|
||||
import org.geysermc.geyser.text.ChatColor;
|
||||
|
||||
public class GeyserFabricLogger implements GeyserLogger {
|
||||
|
||||
private final Logger logger = LogManager.getLogger("geyser-fabric");
|
||||
|
||||
private boolean debug;
|
||||
|
@ -69,6 +71,16 @@ public class GeyserFabricLogger implements GeyserLogger {
|
|||
logger.info(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(Component message) {
|
||||
// As of Java Edition 1.19.2, Fabric's console doesn't natively support legacy format
|
||||
String flattened = LegacyComponentSerializer.legacySection().serialize(message);
|
||||
// Add the reset at the end, or else format will persist... forever.
|
||||
// https://cdn.discordapp.com/attachments/573909525132738590/1033904509170225242/unknown.png
|
||||
String text = ChatColor.toANSI(flattened) + ChatColor.ANSI_RESET;
|
||||
info(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String message) {
|
||||
if (debug) {
|
||||
|
|
|
@ -29,6 +29,7 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
|||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.fabricmc.loader.api.ModContainer;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
|
@ -55,29 +56,21 @@ import org.geysermc.geyser.util.FileUtils;
|
|||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
public class GeyserFabricMod implements ModInitializer, GeyserBootstrap {
|
||||
|
||||
private static GeyserFabricMod instance;
|
||||
|
||||
private boolean reloading;
|
||||
|
||||
private GeyserImpl connector;
|
||||
private GeyserImpl geyser;
|
||||
private ModContainer mod;
|
||||
private Path dataFolder;
|
||||
private MinecraftServer server;
|
||||
|
||||
/**
|
||||
* Commands that don't require any permission level to ran
|
||||
*/
|
||||
private List<String> playerCommands;
|
||||
private final List<GeyserFabricCommandExecutor> commandExecutors = new ArrayList<>();
|
||||
|
||||
private GeyserCommandManager geyserCommandManager;
|
||||
private GeyserFabricConfiguration geyserConfig;
|
||||
private GeyserFabricLogger geyserLogger;
|
||||
|
@ -111,8 +104,6 @@ public class GeyserFabricMod implements ModInitializer, GeyserBootstrap {
|
|||
File configFile = FileUtils.fileOrCopiedFromResource(dataFolder.resolve("config.yml").toFile(), "config.yml",
|
||||
(x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
|
||||
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserFabricConfiguration.class);
|
||||
File permissionsFile = fileOrCopiedFromResource(dataFolder.resolve("permissions.yml").toFile(), "permissions.yml");
|
||||
this.playerCommands = Arrays.asList(FileUtils.loadConfig(permissionsFile, GeyserFabricPermissions.class).getCommands());
|
||||
} catch (IOException ex) {
|
||||
LogManager.getLogger("geyser-fabric").error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
||||
ex.printStackTrace();
|
||||
|
@ -123,10 +114,14 @@ public class GeyserFabricMod implements ModInitializer, GeyserBootstrap {
|
|||
|
||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
||||
|
||||
this.geyser = GeyserImpl.load(PlatformType.FABRIC, this);
|
||||
|
||||
if (server == null) {
|
||||
// Server has yet to start
|
||||
// Register onDisable so players are properly kicked
|
||||
ServerLifecycleEvents.SERVER_STOPPING.register((server) -> onDisable());
|
||||
|
||||
ServerPlayConnectionEvents.JOIN.register((handler, $, $$) -> GeyserFabricUpdateListener.onPlayReady(handler));
|
||||
} else {
|
||||
// Server has started and this is a reload
|
||||
startGeyser(this.server);
|
||||
|
@ -170,38 +165,37 @@ public class GeyserFabricMod implements ModInitializer, GeyserBootstrap {
|
|||
|
||||
geyserConfig.loadFloodgate(this, floodgate.orElse(null));
|
||||
|
||||
this.connector = GeyserImpl.load(PlatformType.FABRIC, this);
|
||||
GeyserImpl.start(); // shrug
|
||||
GeyserImpl.start();
|
||||
|
||||
this.geyserPingPassthrough = GeyserLegacyPingPassthrough.init(connector);
|
||||
this.geyserPingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
|
||||
|
||||
this.geyserCommandManager = new GeyserCommandManager(connector);
|
||||
this.geyserCommandManager = new GeyserCommandManager(geyser);
|
||||
this.geyserCommandManager.init();
|
||||
|
||||
this.geyserWorldManager = new GeyserFabricWorldManager(server);
|
||||
|
||||
// Start command building
|
||||
// Set just "geyser" as the help command
|
||||
GeyserFabricCommandExecutor helpExecutor = new GeyserFabricCommandExecutor(connector,
|
||||
(GeyserCommand) connector.commandManager().getCommands().get("help"), !playerCommands.contains("help"));
|
||||
commandExecutors.add(helpExecutor);
|
||||
GeyserFabricCommandExecutor helpExecutor = new GeyserFabricCommandExecutor(geyser,
|
||||
(GeyserCommand) geyser.commandManager().getCommands().get("help"));
|
||||
LiteralArgumentBuilder<CommandSourceStack> builder = Commands.literal("geyser").executes(helpExecutor);
|
||||
|
||||
// Register all subcommands as valid
|
||||
for (Map.Entry<String, Command> command : connector.commandManager().getCommands().entrySet()) {
|
||||
GeyserFabricCommandExecutor executor = new GeyserFabricCommandExecutor(connector, (GeyserCommand) command.getValue(),
|
||||
!playerCommands.contains(command.getKey()));
|
||||
commandExecutors.add(executor);
|
||||
builder.then(Commands.literal(command.getKey()).executes(executor));
|
||||
for (Map.Entry<String, Command> command : geyser.commandManager().getCommands().entrySet()) {
|
||||
GeyserFabricCommandExecutor executor = new GeyserFabricCommandExecutor(geyser, (GeyserCommand) command.getValue());
|
||||
builder.then(Commands.literal(command.getKey())
|
||||
.executes(executor)
|
||||
// Could also test for Bedrock but depending on when this is called it may backfire
|
||||
.requires(executor::testPermission));
|
||||
}
|
||||
server.getCommands().getDispatcher().register(builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
if (connector != null) {
|
||||
connector.shutdown();
|
||||
connector = null;
|
||||
if (geyser != null) {
|
||||
geyser.shutdown();
|
||||
geyser = null;
|
||||
}
|
||||
if (!reloading) {
|
||||
this.server = null;
|
||||
|
@ -267,34 +261,6 @@ public class GeyserFabricMod implements ModInitializer, GeyserBootstrap {
|
|||
this.reloading = reloading;
|
||||
}
|
||||
|
||||
private File fileOrCopiedFromResource(File file, String name) throws IOException {
|
||||
if (!file.exists()) {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
file.createNewFile();
|
||||
FileOutputStream fos = new FileOutputStream(file);
|
||||
InputStream input = getResource(name);
|
||||
|
||||
byte[] bytes = new byte[input.available()];
|
||||
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
input.read(bytes);
|
||||
|
||||
for(char c : new String(bytes).toCharArray()) {
|
||||
fos.write(c);
|
||||
}
|
||||
|
||||
fos.flush();
|
||||
input.close();
|
||||
fos.close();
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
public List<GeyserFabricCommandExecutor> getCommandExecutors() {
|
||||
return commandExecutors;
|
||||
}
|
||||
|
||||
public static GeyserFabricMod getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
|
|
@ -25,26 +25,19 @@
|
|||
|
||||
package org.geysermc.geyser.platform.fabric;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import me.lucko.fabric.api.permissions.v0.Permissions;
|
||||
import net.minecraft.server.network.ServerGamePacketListenerImpl;
|
||||
import org.geysermc.geyser.Constants;
|
||||
import org.geysermc.geyser.platform.fabric.command.FabricCommandSender;
|
||||
import org.geysermc.geyser.util.VersionCheckUtils;
|
||||
|
||||
/**
|
||||
* A class outline of the permissions.yml file
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class GeyserFabricPermissions {
|
||||
public final class GeyserFabricUpdateListener {
|
||||
public static void onPlayReady(ServerGamePacketListenerImpl handler) {
|
||||
if (Permissions.check(handler.player, Constants.UPDATE_PERMISSION, 2)) {
|
||||
VersionCheckUtils.checkForGeyserUpdate(() -> new FabricCommandSender(handler.player.createCommandSourceStack()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The minimum permission level a command source must have in order for it to run commands that are restricted
|
||||
*/
|
||||
@JsonIgnore
|
||||
public static final int RESTRICTED_MIN_LEVEL = 2;
|
||||
|
||||
@JsonProperty("commands")
|
||||
private String[] commands;
|
||||
|
||||
public String[] getCommands() {
|
||||
return this.commands;
|
||||
private GeyserFabricUpdateListener() {
|
||||
}
|
||||
}
|
|
@ -25,12 +25,13 @@
|
|||
|
||||
package org.geysermc.geyser.platform.fabric.command;
|
||||
|
||||
import me.lucko.fabric.api.permissions.v0.Permissions;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
import org.geysermc.geyser.platform.fabric.GeyserFabricMod;
|
||||
import org.geysermc.geyser.text.ChatColor;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
@ -57,22 +58,23 @@ public class FabricCommandSender implements GeyserCommandSource {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(net.kyori.adventure.text.Component message) {
|
||||
if (source.getEntity() instanceof ServerPlayer player) {
|
||||
String decoded = GsonComponentSerializer.gson().serialize(message);
|
||||
player.displayClientMessage(Component.Serializer.fromJson(decoded), false);
|
||||
return;
|
||||
}
|
||||
GeyserCommandSource.super.sendMessage(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConsole() {
|
||||
return !(source.getEntity() instanceof ServerPlayer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String s) {
|
||||
// Mostly copied from fabric's world manager since the method there takes a GeyserSession
|
||||
|
||||
// Workaround for our commands because fabric doesn't have native permissions
|
||||
for (GeyserFabricCommandExecutor executor : GeyserFabricMod.getInstance().getCommandExecutors()) {
|
||||
if (executor.getCommand().permission().equals(s)) {
|
||||
return executor.canRun(source);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
public boolean hasPermission(String permission) {
|
||||
return Permissions.check(source, permission);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,12 +27,12 @@ package org.geysermc.geyser.platform.fabric.command;
|
|||
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import me.lucko.fabric.api.permissions.v0.Permissions;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.command.GeyserCommand;
|
||||
import org.geysermc.geyser.command.GeyserCommandExecutor;
|
||||
import org.geysermc.geyser.platform.fabric.GeyserFabricMod;
|
||||
import org.geysermc.geyser.platform.fabric.GeyserFabricPermissions;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.text.ChatColor;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
|
@ -40,27 +40,15 @@ import org.geysermc.geyser.text.GeyserLocale;
|
|||
import java.util.Collections;
|
||||
|
||||
public class GeyserFabricCommandExecutor extends GeyserCommandExecutor implements Command<CommandSourceStack> {
|
||||
|
||||
private final GeyserCommand command;
|
||||
/**
|
||||
* Whether the command requires an OP permission level of 2 or greater
|
||||
*/
|
||||
private final boolean requiresPermission;
|
||||
|
||||
public GeyserFabricCommandExecutor(GeyserImpl connector, GeyserCommand command, boolean requiresPermission) {
|
||||
public GeyserFabricCommandExecutor(GeyserImpl connector, GeyserCommand command) {
|
||||
super(connector, Collections.singletonMap(command.name(), command));
|
||||
this.command = command;
|
||||
this.requiresPermission = requiresPermission;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether or not a command source is allowed to run a given executor.
|
||||
*
|
||||
* @param source The command source attempting to run the command
|
||||
* @return True if the command source is allowed to
|
||||
*/
|
||||
public boolean canRun(CommandSourceStack source) {
|
||||
return !requiresPermission() || source.hasPermission(GeyserFabricPermissions.RESTRICTED_MIN_LEVEL);
|
||||
public boolean testPermission(CommandSourceStack source) {
|
||||
return Permissions.check(source, command.permission(), command.isSuggestedOpOnly() ? 2 : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -68,8 +56,8 @@ public class GeyserFabricCommandExecutor extends GeyserCommandExecutor implement
|
|||
CommandSourceStack source = (CommandSourceStack) context.getSource();
|
||||
FabricCommandSender sender = new FabricCommandSender(source);
|
||||
GeyserSession session = getGeyserSession(sender);
|
||||
if (!canRun(source)) {
|
||||
sender.sendMessage(GeyserLocale.getLocaleStringLog("geyser.bootstrap.command.permission_fail"));
|
||||
if (!testPermission(source)) {
|
||||
sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", sender.locale()));
|
||||
return 0;
|
||||
}
|
||||
if (this.command.name().equals("reload")) {
|
||||
|
@ -83,15 +71,4 @@ public class GeyserFabricCommandExecutor extends GeyserCommandExecutor implement
|
|||
command.execute(session, sender, new String[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
public GeyserCommand getCommand() {
|
||||
return command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the command requires permission level of {@link GeyserFabricPermissions#RESTRICTED_MIN_LEVEL} or higher to be ran
|
||||
*/
|
||||
public boolean requiresPermission() {
|
||||
return requiresPermission;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import com.nukkitx.math.vector.Vector3i;
|
|||
import com.nukkitx.nbt.NbtMap;
|
||||
import com.nukkitx.nbt.NbtMapBuilder;
|
||||
import com.nukkitx.nbt.NbtType;
|
||||
import me.lucko.fabric.api.permissions.v0.Permissions;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
|
@ -39,8 +40,6 @@ import net.minecraft.world.item.WrittenBookItem;
|
|||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.LecternBlockEntity;
|
||||
import org.geysermc.geyser.level.GeyserWorldManager;
|
||||
import org.geysermc.geyser.platform.fabric.GeyserFabricMod;
|
||||
import org.geysermc.geyser.platform.fabric.command.GeyserFabricCommandExecutor;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.inventory.LecternInventoryTranslator;
|
||||
import org.geysermc.geyser.util.BlockEntityUtils;
|
||||
|
@ -124,14 +123,8 @@ public class GeyserFabricWorldManager extends GeyserWorldManager {
|
|||
|
||||
@Override
|
||||
public boolean hasPermission(GeyserSession session, String permission) {
|
||||
// Workaround for our commands because fabric doesn't have native permissions
|
||||
for (GeyserFabricCommandExecutor executor : GeyserFabricMod.getInstance().getCommandExecutors()) {
|
||||
if (executor.getCommand().permission().equals(permission)) {
|
||||
return executor.canRun(getPlayer(session).createCommandSourceStack());
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
ServerPlayer player = getPlayer(session);
|
||||
return Permissions.check(player, permission);
|
||||
}
|
||||
|
||||
private ServerPlayer getPlayer(GeyserSession session) {
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
"depends": {
|
||||
"fabricloader": ">=0.14.8",
|
||||
"fabric": "*",
|
||||
"minecraft": ">=1.19"
|
||||
"minecraft": ">=1.19",
|
||||
"fabric-permissions-api-v0": "*"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
# Uncomment any commands that you wish to be run by clients
|
||||
# Commented commands require an OP permission of 2 or greater
|
||||
commands:
|
||||
- help
|
||||
- advancements
|
||||
- statistics
|
||||
- settings
|
||||
- offhand
|
||||
- tooltips
|
||||
# - list
|
||||
# - reload
|
||||
# - version
|
||||
# - dump
|
|
@ -26,6 +26,7 @@
|
|||
package org.geysermc.geyser.text;
|
||||
|
||||
public class ChatColor {
|
||||
public static final String ANSI_RESET = (char) 0x1b + "[0m";
|
||||
|
||||
public static final char ESCAPE = '§';
|
||||
public static final String BLACK = ESCAPE + "0";
|
||||
|
@ -64,7 +65,7 @@ public class ChatColor {
|
|||
string = string.replace(ITALIC, (char) 0x1b + "[3m");
|
||||
string = string.replace(UNDERLINE, (char) 0x1b + "[4m");
|
||||
string = string.replace(STRIKETHROUGH, (char) 0x1b + "[9m");
|
||||
string = string.replace(RESET, (char) 0x1b + "[0m");
|
||||
string = string.replace(RESET, ANSI_RESET);
|
||||
string = string.replace(BLACK, (char) 0x1b + "[0;30m");
|
||||
string = string.replace(DARK_BLUE, (char) 0x1b + "[0;34m");
|
||||
string = string.replace(DARK_GREEN, (char) 0x1b + "[0;32m");
|
||||
|
@ -83,19 +84,4 @@ public class ChatColor {
|
|||
string = string.replace(WHITE, (char) 0x1b + "[37;1m");
|
||||
return string;
|
||||
}
|
||||
|
||||
public String translateAlternateColorCodes(char color, String message) {
|
||||
return message.replace(color, ESCAPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all colour formatting tags from a message
|
||||
*
|
||||
* @param message Message to remove colour tags from
|
||||
*
|
||||
* @return The sanitised message
|
||||
*/
|
||||
public static String stripColors(String message) {
|
||||
return message = message.replaceAll("(&([a-fk-or0-9]))","").replaceAll("(§([a-fk-or0-9]))","").replaceAll("s/\\x1b\\[[0-9;]*[a-zA-Z]//g","");
|
||||
}
|
||||
}
|
|
@ -26,6 +26,9 @@ commodore = "2.2"
|
|||
bungeecord = "a7c6ede"
|
||||
velocity = "3.0.0"
|
||||
sponge = "8.0.0"
|
||||
fabric-minecraft = "1.19.1"
|
||||
fabric-loader = "0.14.8"
|
||||
fabric-api = "0.58.5+1.19.1"
|
||||
|
||||
[libraries]
|
||||
jackson-annotations = { group = "com.fasterxml.jackson.core", name = "jackson-annotations", version.ref = "jackson" }
|
||||
|
@ -63,9 +66,9 @@ paper-api = { group = "io.papermc.paper", name = "paper-api", version.ref = "pap
|
|||
paper-mojangapi = { group = "io.papermc.paper", name = "paper-mojangapi", version.ref = "paper" }
|
||||
|
||||
# check these on https://modmuss50.me/fabric.html
|
||||
fabric-minecraft = { group = "com.mojang", name = "minecraft", version = "1.19.1" }
|
||||
fabric-loader = { group = "net.fabricmc", name = "fabric-loader", version = "0.14.8" }
|
||||
fabric-api = { group = "net.fabricmc.fabric-api", name = "fabric-api", version = "0.58.5+1.19.1" }
|
||||
fabric-minecraft = { group = "com.mojang", name = "minecraft", version.ref = "fabric-minecraft" }
|
||||
fabric-loader = { group = "net.fabricmc", name = "fabric-loader", version.ref = "fabric-loader" }
|
||||
fabric-api = { group = "net.fabricmc.fabric-api", name = "fabric-api", version.ref = "fabric-api" }
|
||||
|
||||
adapters-spigot = { group = "org.geysermc.geyser.adapters", name = "spigot-all", version.ref = "adapters" }
|
||||
bungeecord-proxy = { group = "com.github.SpigotMC.BungeeCord", name = "bungeecord-proxy", version.ref = "bungeecord" }
|
||||
|
|
Loading…
Reference in a new issue