9
0
mirror of https://github.com/Xiao-MoMi/Custom-Fishing.git synced 2025-12-19 15:09:24 +00:00

New API Events

This commit is contained in:
XiaoMoMi
2025-03-04 01:54:32 +08:00
parent d2c99c1c12
commit 4391e54dca
21 changed files with 191 additions and 30 deletions

View File

@@ -27,6 +27,7 @@ import net.momirealms.customfishing.api.mechanic.effect.EffectManager;
import net.momirealms.customfishing.api.mechanic.entity.EntityManager; import net.momirealms.customfishing.api.mechanic.entity.EntityManager;
import net.momirealms.customfishing.api.mechanic.event.EventManager; import net.momirealms.customfishing.api.mechanic.event.EventManager;
import net.momirealms.customfishing.api.mechanic.fishing.FishingManager; import net.momirealms.customfishing.api.mechanic.fishing.FishingManager;
import net.momirealms.customfishing.api.mechanic.game.AbstractGamingPlayer;
import net.momirealms.customfishing.api.mechanic.game.GameManager; import net.momirealms.customfishing.api.mechanic.game.GameManager;
import net.momirealms.customfishing.api.mechanic.hook.HookManager; import net.momirealms.customfishing.api.mechanic.hook.HookManager;
import net.momirealms.customfishing.api.mechanic.item.ItemManager; import net.momirealms.customfishing.api.mechanic.item.ItemManager;

View File

@@ -27,7 +27,6 @@ import org.jetbrains.annotations.NotNull;
* It is triggered when the state of a fishing competition changes. * It is triggered when the state of a fishing competition changes.
*/ */
public class CompetitionEvent extends Event { public class CompetitionEvent extends Event {
private static final HandlerList handlerList = new HandlerList(); private static final HandlerList handlerList = new HandlerList();
private final State state; private final State state;

View File

@@ -26,7 +26,6 @@ import org.jetbrains.annotations.NotNull;
* This class represents an event that is triggered when the Custom Fishing plugin is reloaded. * This class represents an event that is triggered when the Custom Fishing plugin is reloaded.
*/ */
public class CustomFishingReloadEvent extends Event { public class CustomFishingReloadEvent extends Event {
private static final HandlerList handlerList = new HandlerList(); private static final HandlerList handlerList = new HandlerList();
private final BukkitCustomFishingPlugin plugin; private final BukkitCustomFishingPlugin plugin;

View File

@@ -30,7 +30,6 @@ import org.jetbrains.annotations.NotNull;
* It can be cancelled to prevent the item from being collected. * It can be cancelled to prevent the item from being collected.
*/ */
public class FishingBagPreCollectEvent extends PlayerEvent implements Cancellable { public class FishingBagPreCollectEvent extends PlayerEvent implements Cancellable {
private static final HandlerList handlerList = new HandlerList(); private static final HandlerList handlerList = new HandlerList();
private final ItemStack itemStack; private final ItemStack itemStack;

View File

@@ -27,7 +27,6 @@ import org.jetbrains.annotations.NotNull;
* This class provides * This class provides
*/ */
public class FishingEffectApplyEvent extends Event { public class FishingEffectApplyEvent extends Event {
private static final HandlerList handlerList = new HandlerList(); private static final HandlerList handlerList = new HandlerList();
private final Stage stage; private final Stage stage;

View File

@@ -0,0 +1,56 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customfishing.api.event;
import net.momirealms.customfishing.api.mechanic.fishing.CustomFishingHook;
import net.momirealms.customfishing.api.mechanic.game.GameSetting;
import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerEvent;
import org.jetbrains.annotations.NotNull;
public class FishingGamePreStartEvent extends PlayerEvent {
private static final HandlerList handlerList = new HandlerList();
private GameSetting setting;
private CustomFishingHook hook;
public FishingGamePreStartEvent(@NotNull CustomFishingHook hook, GameSetting setting) {
super(hook.getContext().holder());
this.setting = setting;
}
public CustomFishingHook hook() {
return hook;
}
public GameSetting setting() {
return setting;
}
public void setting(GameSetting setting) {
this.setting = setting;
}
@Override
public @NotNull HandlerList getHandlers() {
return handlerList;
}
public static HandlerList getHandlerList() {
return handlerList;
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customfishing.api.event;
import net.momirealms.customfishing.api.mechanic.fishing.CustomFishingHook;
import net.momirealms.customfishing.api.mechanic.game.GameSetting;
import net.momirealms.customfishing.api.mechanic.game.GamingPlayer;
import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerEvent;
import org.jetbrains.annotations.NotNull;
public class FishingGameStartEvent extends PlayerEvent {
private static final HandlerList handlerList = new HandlerList();
private final GamingPlayer gamingPlayer;
private final CustomFishingHook hook;
public FishingGameStartEvent(@NotNull CustomFishingHook hook, GamingPlayer gamingPlayer) {
super(hook.getContext().holder());
this.gamingPlayer = gamingPlayer;
this.hook = hook;
}
public CustomFishingHook hook() {
return hook;
}
public GamingPlayer gamingPlayer() {
return gamingPlayer;
}
@Override
public @NotNull HandlerList getHandlers() {
return handlerList;
}
public static HandlerList getHandlerList() {
return handlerList;
}
}

View File

@@ -28,7 +28,6 @@ import org.jetbrains.annotations.NotNull;
* It is triggered by various states of the fishing hook such as when a fish bites, escapes, is lured, or is landed. * It is triggered by various states of the fishing hook such as when a fish bites, escapes, is lured, or is landed.
*/ */
public class FishingHookStateEvent extends PlayerEvent { public class FishingHookStateEvent extends PlayerEvent {
private static final HandlerList handlerList = new HandlerList(); private static final HandlerList handlerList = new HandlerList();
private final FishHook fishHook; private final FishHook fishHook;

View File

@@ -31,7 +31,6 @@ import org.jetbrains.annotations.Nullable;
* This class represents an event that is triggered when fishing loot is spawned. * This class represents an event that is triggered when fishing loot is spawned.
*/ */
public class FishingLootSpawnEvent extends PlayerEvent { public class FishingLootSpawnEvent extends PlayerEvent {
private static final HandlerList handlerList = new HandlerList(); private static final HandlerList handlerList = new HandlerList();
private final Location location; private final Location location;
private final Entity entity; private final Entity entity;

View File

@@ -33,7 +33,6 @@ import java.util.Optional;
* This class represents an event that is triggered when a fishing result is determined. * This class represents an event that is triggered when a fishing result is determined.
*/ */
public class FishingResultEvent extends PlayerEvent implements Cancellable { public class FishingResultEvent extends PlayerEvent implements Cancellable {
private static final HandlerList handlerList = new HandlerList(); private static final HandlerList handlerList = new HandlerList();
private boolean isCancelled; private boolean isCancelled;
private final Result result; private final Result result;

View File

@@ -28,7 +28,6 @@ import org.jetbrains.annotations.NotNull;
* This class represents an event that occurs when a player casts a fishing rod. * This class represents an event that occurs when a player casts a fishing rod.
*/ */
public class RodCastEvent extends PlayerEvent implements Cancellable { public class RodCastEvent extends PlayerEvent implements Cancellable {
private final FishingGears gears; private final FishingGears gears;
private boolean isCancelled; private boolean isCancelled;
private final PlayerFishEvent event; private final PlayerFishEvent event;

View File

@@ -29,7 +29,6 @@ import org.jetbrains.annotations.NotNull;
* This class represents an event that occurs when a player activates a totem. * This class represents an event that occurs when a player activates a totem.
*/ */
public class TotemActivateEvent extends PlayerEvent implements Cancellable { public class TotemActivateEvent extends PlayerEvent implements Cancellable {
private static final HandlerList handlerList = new HandlerList(); private static final HandlerList handlerList = new HandlerList();
private final Location coreLocation; private final Location coreLocation;
private boolean isCancelled; private boolean isCancelled;

View File

@@ -18,10 +18,7 @@
package net.momirealms.customfishing.api.mechanic.fishing; package net.momirealms.customfishing.api.mechanic.fishing;
import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import net.momirealms.customfishing.api.event.FishingEffectApplyEvent; import net.momirealms.customfishing.api.event.*;
import net.momirealms.customfishing.api.event.FishingHookStateEvent;
import net.momirealms.customfishing.api.event.FishingLootSpawnEvent;
import net.momirealms.customfishing.api.event.FishingResultEvent;
import net.momirealms.customfishing.api.mechanic.MechanicType; import net.momirealms.customfishing.api.mechanic.MechanicType;
import net.momirealms.customfishing.api.mechanic.action.ActionTrigger; import net.momirealms.customfishing.api.mechanic.action.ActionTrigger;
import net.momirealms.customfishing.api.mechanic.competition.CompetitionGoal; import net.momirealms.customfishing.api.mechanic.competition.CompetitionGoal;
@@ -345,6 +342,8 @@ public class CustomFishingHook {
BukkitCustomFishingPlugin.getInstance().debug("Freezing current mechanic"); BukkitCustomFishingPlugin.getInstance().debug("Freezing current mechanic");
this.hookMechanic.freeze(); this.hookMechanic.freeze();
} }
FishingGameStartEvent event = new FishingGameStartEvent(this, gamingPlayer);
EventUtils.fireAndForget(event);
} else { } else {
plugin.debug("Next game: " + "`null`"); plugin.debug("Next game: " + "`null`");
handleSuccessfulFishing(); handleSuccessfulFishing();

View File

@@ -18,8 +18,10 @@
package net.momirealms.customfishing.api.mechanic.game; package net.momirealms.customfishing.api.mechanic.game;
import net.momirealms.customfishing.api.BukkitCustomFishingPlugin; import net.momirealms.customfishing.api.BukkitCustomFishingPlugin;
import net.momirealms.customfishing.api.event.FishingGamePreStartEvent;
import net.momirealms.customfishing.api.mechanic.effect.Effect; import net.momirealms.customfishing.api.mechanic.effect.Effect;
import net.momirealms.customfishing.api.mechanic.fishing.CustomFishingHook; import net.momirealms.customfishing.api.mechanic.fishing.CustomFishingHook;
import net.momirealms.customfishing.api.util.EventUtils;
import java.util.function.BiFunction; import java.util.function.BiFunction;
@@ -42,6 +44,11 @@ public abstract class AbstractGame implements Game {
this.id = id; this.id = id;
} }
@Override
public GameBasics basics() {
return basics;
}
/** /**
* Gets the identifier of the game. * Gets the identifier of the game.
* *
@@ -62,7 +69,10 @@ public abstract class AbstractGame implements Game {
@Override @Override
public GamingPlayer start(CustomFishingHook hook, Effect effect) { public GamingPlayer start(CustomFishingHook hook, Effect effect) {
BukkitCustomFishingPlugin.getInstance().debug(effect); BukkitCustomFishingPlugin.getInstance().debug(effect);
return gamingPlayerProvider().apply(hook, basics.toGameSetting(hook.getContext(), effect)); GameSetting setting = this.basics.toGameSetting(hook.getContext(), effect);
FishingGamePreStartEvent event = new FishingGamePreStartEvent(hook, setting);
EventUtils.fireAndForget(event);
return gamingPlayerProvider().apply(hook, event.setting());
} }
/** /**

View File

@@ -31,7 +31,6 @@ import java.util.concurrent.TimeUnit;
* Provides the basic structure and functionalities for a gaming player. * Provides the basic structure and functionalities for a gaming player.
*/ */
public abstract class AbstractGamingPlayer implements GamingPlayer, Runnable { public abstract class AbstractGamingPlayer implements GamingPlayer, Runnable {
protected long deadline; protected long deadline;
protected boolean success; protected boolean success;
protected SchedulerTask task; protected SchedulerTask task;
@@ -40,6 +39,12 @@ public abstract class AbstractGamingPlayer implements GamingPlayer, Runnable {
protected boolean isTimeOut; protected boolean isTimeOut;
private boolean valid = true; private boolean valid = true;
private boolean firstFlag = true; private boolean firstFlag = true;
protected Boolean forcedGameResult;
@Override
public void setGameResult(Boolean forcedGameResult) {
this.forcedGameResult = forcedGameResult;
}
/** /**
* Constructs an AbstractGamingPlayer instance. * Constructs an AbstractGamingPlayer instance.
@@ -54,6 +59,11 @@ public abstract class AbstractGamingPlayer implements GamingPlayer, Runnable {
this.arrangeTask(); this.arrangeTask();
} }
@Override
public GameSetting settings() {
return settings;
}
/** /**
* Arranges the task for the gaming player. * Arranges the task for the gaming player.
*/ */
@@ -206,7 +216,8 @@ public abstract class AbstractGamingPlayer implements GamingPlayer, Runnable {
/** /**
* Ends the game for the gaming player. * Ends the game for the gaming player.
*/ */
protected void endGame() { @Override
public void endGame() {
if (!isValid()) return; if (!isValid()) return;
destroy(); destroy();
boolean success = isSuccessful(); boolean success = isSuccessful();

View File

@@ -25,6 +25,13 @@ import net.momirealms.customfishing.api.mechanic.fishing.CustomFishingHook;
*/ */
public interface Game { public interface Game {
/**
* Gets the basic settings of the game
*
* @return the basic settings
*/
GameBasics basics();
/** /**
* Gets the identifier of the game. * Gets the identifier of the game.
* *

View File

@@ -31,6 +31,20 @@ public interface GamingPlayer {
*/ */
boolean isValid(); boolean isValid();
/**
* Sets the game result
*
* @param result result, true for success, false for failure
*/
void setGameResult(Boolean result);
/**
* Gets the current game settings
*
* @return game settings
*/
GameSetting settings();
/** /**
* Destroys the gaming player, performing any necessary cleanup * Destroys the gaming player, performing any necessary cleanup
*/ */
@@ -93,4 +107,9 @@ public interface GamingPlayer {
* @return the player. * @return the player.
*/ */
Player getPlayer(); Player getPlayer();
/**
* Ends the game
*/
void endGame();
} }

View File

@@ -924,7 +924,6 @@ public class BukkitGameManager implements GameManager {
int maxSuccess = Integer.parseInt(barSuccess.split("~")[1]); int maxSuccess = Integer.parseInt(barSuccess.split("~")[1]);
return (customFishingHook, gameSetting) -> new AbstractGamingPlayer(customFishingHook, gameSetting) { return (customFishingHook, gameSetting) -> new AbstractGamingPlayer(customFishingHook, gameSetting) {
private final int totalWidth = RandomUtils.generateRandomInt(minWidth, maxWidth); private final int totalWidth = RandomUtils.generateRandomInt(minWidth, maxWidth);
private final int successWidth = RandomUtils.generateRandomInt(minSuccess, maxSuccess); private final int successWidth = RandomUtils.generateRandomInt(minSuccess, maxSuccess);
private final int successPosition = ThreadLocalRandom.current().nextInt((totalWidth - successWidth + 1)) + 1; private final int successPosition = ThreadLocalRandom.current().nextInt((totalWidth - successWidth + 1)) + 1;

View File

@@ -29,6 +29,8 @@ import net.momirealms.customfishing.api.mechanic.market.MarketGUIHolder;
import net.momirealms.customfishing.api.mechanic.market.MarketManager; import net.momirealms.customfishing.api.mechanic.market.MarketManager;
import net.momirealms.customfishing.api.mechanic.misc.value.MathValue; import net.momirealms.customfishing.api.mechanic.misc.value.MathValue;
import net.momirealms.customfishing.api.mechanic.misc.value.TextValue; import net.momirealms.customfishing.api.mechanic.misc.value.TextValue;
import net.momirealms.customfishing.api.mechanic.requirement.Requirement;
import net.momirealms.customfishing.api.mechanic.requirement.RequirementManager;
import net.momirealms.customfishing.api.storage.data.EarningData; import net.momirealms.customfishing.api.storage.data.EarningData;
import net.momirealms.customfishing.api.storage.user.UserData; import net.momirealms.customfishing.api.storage.user.UserData;
import net.momirealms.customfishing.bukkit.config.BukkitConfigManager; import net.momirealms.customfishing.bukkit.config.BukkitConfigManager;
@@ -91,6 +93,9 @@ public class BukkitMarketManager implements MarketManager, Listener {
protected Action<Player>[] sellAllAllowActions; protected Action<Player>[] sellAllAllowActions;
protected Action<Player>[] sellAllLimitActions; protected Action<Player>[] sellAllLimitActions;
protected Requirement<Player>[] allowBundleRequirements;
protected Requirement<Player>[] allowShulkerBoxRequirements;
private SchedulerTask resetEarningsTask; private SchedulerTask resetEarningsTask;
private int cachedDate; private int cachedDate;
@@ -144,7 +149,9 @@ public class BukkitMarketManager implements MarketManager, Listener {
this.itemSlot = config.getString("item-slot.symbol", "I").charAt(0); this.itemSlot = config.getString("item-slot.symbol", "I").charAt(0);
this.allowItemWithNoPrice = config.getBoolean("item-slot.allow-items-with-no-price", true); this.allowItemWithNoPrice = config.getBoolean("item-slot.allow-items-with-no-price", true);
this.allowBundle = config.getBoolean("allow-bundle", true); this.allowBundle = config.getBoolean("allow-bundle", true);
this.allowBundleRequirements = plugin.getRequirementManager().parseRequirements(config.getSection("allow-bundle-requirements"), false);
this.allowShulkerBox = config.getBoolean("allow-shulker-box", true); this.allowShulkerBox = config.getBoolean("allow-shulker-box", true);
this.allowShulkerBoxRequirements = plugin.getRequirementManager().parseRequirements(config.getSection("allow-shulker-box-requirements"), false);
Section sellAllSection = config.getSection("sell-all-icons"); Section sellAllSection = config.getSection("sell-all-icons");
if (sellAllSection != null) { if (sellAllSection != null) {
@@ -477,12 +484,12 @@ public class BukkitMarketManager implements MarketManager, Listener {
return price * itemStack.getAmount(); return price * itemStack.getAmount();
} }
if (allowBundle && itemStack.getItemMeta() instanceof BundleMeta bundleMeta) { if (allowBundle && itemStack.getItemMeta() instanceof BundleMeta bundleMeta && RequirementManager.isSatisfied(context, allowBundleRequirements) ) {
Pair<Integer, Double> pair = getItemsToSell(context, bundleMeta.getItems()); Pair<Integer, Double> pair = getItemsToSell(context, bundleMeta.getItems());
return pair.right(); return pair.right();
} }
if (allowShulkerBox && itemStack.getItemMeta() instanceof BlockStateMeta stateMeta) { if (allowShulkerBox && itemStack.getItemMeta() instanceof BlockStateMeta stateMeta && RequirementManager.isSatisfied(context, allowShulkerBoxRequirements) ) {
if (stateMeta.getBlockState() instanceof ShulkerBox shulkerBox) { if (stateMeta.getBlockState() instanceof ShulkerBox shulkerBox) {
Pair<Integer, Double> pair = getItemsToSell(context, Arrays.stream(shulkerBox.getInventory().getStorageContents()).filter(Objects::nonNull).toList()); Pair<Integer, Double> pair = getItemsToSell(context, Arrays.stream(shulkerBox.getInventory().getStorageContents()).filter(Objects::nonNull).toList());
return pair.right(); return pair.right();
@@ -535,7 +542,7 @@ public class BukkitMarketManager implements MarketManager, Listener {
for (ItemStack itemStack : itemStacks) { for (ItemStack itemStack : itemStacks) {
double price = getItemPrice(context, itemStack); double price = getItemPrice(context, itemStack);
if (price > 0 && itemStack != null) { if (price > 0 && itemStack != null) {
if (allowBundle && itemStack.getItemMeta() instanceof BundleMeta bundleMeta) { if (allowBundle && itemStack.getItemMeta() instanceof BundleMeta bundleMeta && RequirementManager.isSatisfied(context, allowBundleRequirements)) {
clearWorthyItems(context, bundleMeta.getItems()); clearWorthyItems(context, bundleMeta.getItems());
List<ItemStack> newItems = new ArrayList<>(bundleMeta.getItems()); List<ItemStack> newItems = new ArrayList<>(bundleMeta.getItems());
newItems.removeIf(item -> item.getAmount() == 0 || item.getType() == Material.AIR); newItems.removeIf(item -> item.getAmount() == 0 || item.getType() == Material.AIR);
@@ -543,7 +550,7 @@ public class BukkitMarketManager implements MarketManager, Listener {
itemStack.setItemMeta(bundleMeta); itemStack.setItemMeta(bundleMeta);
continue; continue;
} }
if (allowShulkerBox && itemStack.getItemMeta() instanceof BlockStateMeta stateMeta) { if (allowShulkerBox && itemStack.getItemMeta() instanceof BlockStateMeta stateMeta && RequirementManager.isSatisfied(context, allowShulkerBoxRequirements)) {
if (stateMeta.getBlockState() instanceof ShulkerBox shulkerBox) { if (stateMeta.getBlockState() instanceof ShulkerBox shulkerBox) {
clearWorthyItems(context, Arrays.stream(shulkerBox.getInventory().getStorageContents()).filter(Objects::nonNull).toList()); clearWorthyItems(context, Arrays.stream(shulkerBox.getInventory().getStorageContents()).filter(Objects::nonNull).toList());
stateMeta.setBlockState(shulkerBox); stateMeta.setBlockState(shulkerBox);

View File

@@ -19,9 +19,12 @@ mechanics:
- blacklist_world - blacklist_world
# If you want to allow some players to skip the game, set skip requirements here # If you want to allow some players to skip the game, set skip requirements here
skip-game-requirements: skip-game-requirements:
bedrock_requirement: or_requirement:
type: 'is-bedrock-player' type: "||"
value: true value:
bedrock_requirement:
type: 'is-bedrock-player'
value: true
# Conditions for enabling auto-fishing # Conditions for enabling auto-fishing
auto-fishing-requirements: auto-fishing-requirements:
impossible_requirement: impossible_requirement:
@@ -213,10 +216,14 @@ mechanics:
- 'AAAABAAAA' - 'AAAABAAAA'
# Price formula for custom fishing loot # Price formula for custom fishing loot
price-formula: '{base} + {bonus} * {size}' price-formula: '{base} + {bonus} * {size}'
# Allow players to sell fish in bundles # Requirements for players to sell fish in bundles
allow-bundle: false allow-bundle-requirements:
# Allow players to sell fish in shulker boxes impossible_requirement:
allow-shulker-box: false type: 'impossible'
# Requirements for players to sell fish in shulker boxes
allow-shulker-box-requirements:
impossible_requirement:
type: 'impossible'
# Prices for vanilla and other plugin items with CustomModelData # Prices for vanilla and other plugin items with CustomModelData
item-price: item-price:
# Vanilla items # Vanilla items

View File

@@ -1,6 +1,6 @@
# Project settings # Project settings
# Rule: [major update].[feature update].[bug fix] # Rule: [major update].[feature update].[bug fix]
project_version=2.3.6 project_version=2.3.7
config_version=38 config_version=38
project_group=net.momirealms project_group=net.momirealms
@@ -20,7 +20,7 @@ h2_driver_version=2.3.232
sqlite_driver_version=3.48.0.0 sqlite_driver_version=3.48.0.0
adventure_bundle_version=4.18.0 adventure_bundle_version=4.18.0
adventure_platform_version=4.3.4 adventure_platform_version=4.3.4
sparrow_heart_version=0.50 sparrow_heart_version=0.52
cloud_core_version=2.0.0 cloud_core_version=2.0.0
cloud_services_version=2.0.0 cloud_services_version=2.0.0
cloud_brigadier_version=2.0.0-beta.10 cloud_brigadier_version=2.0.0-beta.10