Plugin for the management of a complex network and created to simplify the work of developers who can use optimized and simplified functions for their plugins, based on a single core that will never be changed in order to centralize everything.
- MySQL library with custom query builder
- Advanced Redis support with Redisson and built in messages¹
- Server scoreboard adapter
- Simple YAML config library
- Add server dynamically to Velocity (only with BasementLib on velocity)¹ ²
- Shared servers¹
- Global player counter¹ ² (for servers and networks)
- Execute velocity methods directly from bukkit¹ ²
1 Velocity nedded, 2 Redis nedded
Adding BasementLib to your project The API artifact is published to the GitHub Packages Maven repository. You can add it to your project by adding the following to your build script.
If you're using Gradle, you need to add these lines to your build script.
repositories {
maven { url 'https://jitpack.io' }
}
dependencies {
# General API
compileOnly 'it.ohalee.basementlib:api:1.0'
# Bukkit API
compileOnly 'it.ohalee.basementlib:api-bukkit:1.0'
# Velocity API
compileOnly 'it.ohalee.basementlib:api-velocity:1.0'
}repositories {
maven { url 'https://jitpack.io' }
}
dependencies {
# General API
compileOnly("com.github.ServerBasement:BasementLib:api:1.0")
# Bukkit API
compileOnly("it.ohalee.basementlib:api-bukkit:1.0")
# Velocity API
compileOnly("it.ohalee.basementlib:api-velocity:1.0")
}The root API interface is BasementLib. You need to obtain an instance of this interface in order to do anything.
When the plugin is enabled, an instance of BasementLib will be provided in the Bukkit ServicesManager. (obviously you need to be writing your plugin for Bukkit!)
RegisteredServiceProvider<BasementLib> provider=Bukkit.getServicesManager().getRegistration(BasementLib.class);
if(provider!=null){
BasementLib api=provider.getProvider();
}When the plugin is enabled, an instance of BasementLib can be obtained statically from the BasementProvider class. (this will work on all platforms)
Note: this method will throw an IllegalStateException if the API is not loaded.
BasementLib api=BasementProvider.get();private final ConfigurationAdapter configuration;
public ConfigExample(BasementLib basement,String fileName){
configuration=basement.plugin().provideConfigurationAdapter(ConfigExample.class,new File(fileName),true);
}
public List<String> getNames(){
return configuration.getStringList("names",List.of());
}
public void reload(){
configuration.reload();
}In the it.ohalee.basementlib.api.plugin package there is a generic interface that can be implemented in your plugin if you wish. Under it.ohalee.basementlib.api.plugin.logging there is another interface that you can use in your plugin to implement your own logger, the BasementLib one can be obtained via basementLib.plugin().logger()
basementLib.plugin().logger().warn("Test");It may seem that the database APIs are complicated but in reality it is enough to understand and learn how they work to know how to use them in all their power. If the database from the config is not enabled, all the methods we are going to see now will return null
To use a database from the basementLib API you need to "load" it: when the plugin is enabled, the database inserted in the config is already loaded. It is then possible to perform operations external to this database by specifying it in the queries.
basementLib.holder().createDatabase("name").ifNotExists(true).build();NOTE: A database is loaded if a create database query is run (even if the database already exists) and is unloaded if a drop database query is run (queries must be run on the server in question for this to happen, is not synchronized between all servers)
To query a database just do:
AbstractMariaDatabase customDatabase=basementLib.database("name");
AbstractMariaDatabase defaultDatabase=basementLib.database();
defaultDatabase.insert()
defaultDatabase.select()
defaultDatabase.update()
defaultDatabase.replace()
defaultDatabase.delete()
defaultDatabase.useTable()
defaultDatabase.createTable()
defaultDatabase.dropTable()Differences between methods without "NQ" and with "NQ": NQ stands for "not quoted" and is used to indicate whether or not to quote quotes (obviously you need to know a minimum of sql)
"Where" clause:
something.where(WhereBuilder.builder().equalsNQ("uuid","?").close())At the end of each query (when you actually use it) you need to call the build() method. It is also possible to save queries (generally not built) in variables. When using them it is necessary to use the patternClone() method to copy the query and not modify the original one (this only if the operation is performed in another thread).
You can create another pool with the following method in case you need it:
Connector connector=basementLib.createConnector(minPool,maxPool,poolName)- Batch queries:
try (PreparedStatement prepared = someUpdate.patternClone().build().asPrepared()) { ... prepared.executeBatch(); // Close connection prepared.getConnection().close(); } catch (SQLException e) { e.printStackTrace(); }
You can send messages via redis and receive them anywhere:
https://github.com/redisson/redisson/wiki/6.-Distributed-objects#67-topic
// Publish message
basementLib.redisManager().publishMessage(BasementMessage message);
// Register Topic Listener
basementLib.redisManager().registerTopicListener(String name,BasementMessageHandler<T extends BasementMessage> basementMessageHandler);
// Unregister Topic Listeners
basementLib.redisManager().unregisterTopicListener(String name,Integer...listenerId);
// Clear Topic Listeners
basementLib.redisManager().clearTopicListeners(String name);Message Example
// Custom Message class
public class ServerShutdownMessage extends BasementMessage {
// Channel
public static final String TOPIC = "server-shutdown";
// Data
private final String sender;
private final String receiver;
public ServerShutdownMessage() {
super(TOPIC);
this.sender = null;
this.receiver = null;
}
public ServerShutdownMessage(String sender, String receiver) {
super(TOPIC);
this.sender = sender;
this.receiver = receiver;
}
}
// Handler
public class ServerShutdownHandler implements BasementMessageHandler<ServerShutdownMessage> {
private final ProxyServer server;
@Override
public void execute(ServerShutdownMessage message) {
// Do something
}
@Override
public Class<ServerShutdownMessage> getCommandClass() {
return ServerShutdownMessage.class;
}
}
// Register
basementLib.redisManager().registerTopicListener(ServerShutdownMessage.TOPIC,new ServerShutdownHandler(this));
// Publish message
basementLib.redisManager().publishMessage(new ServerShutdownMessage("banana","moon"));Use redisson client: https://github.com/redisson/redisson
RedissonClient client=basementLib.redisManager().redissonClient();These services need Redis to work.
RemoteCerebrumService allows via MineDock (software that manages servers in a modern and dynamic way via docker, available soon) to create and initialize servers even when the other servers are turned on
public interface RemoteCerebrumService {
/**
* Create a new server
*
* @param name the server name
*/
void createServer(String name);
}RemoteVelocityService allows you to access the velocity functions from bukkit and check in real time if a player is in a server or in a section. Or if we want to send it to another server, or if we want to record a server directly on the velocity
public interface RemoteVelocityService {
/**
* Gets if a player is in a server that names start with {@param server}
*
* @param player the player name
* @param server the server ranch name
* @return true if player is in a server ranch, false otherwise
*/
boolean isOnRanch(String player, String server);
/**
* Gets if a player is in a server that names start with {@param server}
*
* @param player the player uuid
* @param server the server ranch name
* @return true if player is in a server ranch, false otherwise
*/
boolean isOnRanch(UUID player, String server);
/**
* Gets the server name of a player
*
* @param player the player name
* @return the server name
*/
String getServer(String player);
/**
* Send a player to a server
*
* @param player the player name
* @param server the server name
*/
void sendToServer(String player, String server);
/**
* Send a player to a server
*
* @param uuid the player uuid
* @param server the server name
*/
void sendToServer(UUID uuid, String server);
/**
* Send same messages to a player
*
* @param player player name
* @param messages the messages to send to the player
*/
void sendMessage(String player, String... messages);
/**
* Send same messages to a player only if it has a determinate permission
*
* @param player player name
* @param permissionNode the permission the player need to receive the messages
* @param messages the messages to send to the player
*/
void sendMessageWithPermission(String player, String permissionNode, String... messages);
/**
* Send same messages to a player, serialized in json string {@link net.kyori.adventure.text.Component}
*
* @param player player name
* @param messages the messages to send to the player serialized in Json String
*/
void sendMessageComponent(String player, String... messages);
/**
* Register a new server to velocity
*
* @param serverName the server name
* @param port the server port
*/
void registerServer(String serverName, int port);
}The server id is the name of the server that is used in case the BasementLib plugin for velocity is prenste, in order to dynamically register the servers.
it is obtainable and modifiable via the BasementLib API:
BasementBukkitPlugin basementLib=(BasementBukkitPlugin)BasementProvider.get();
basementLib.setServerID("someName");
String serverID=basementLib.getServerID();Using the Colorizer class you can directly transform messages into colored messages
public final class Colorizer {
@Setter
private static ColorAdapter adapter;
public static String colorize(String string) {
string = ChatColor.translateAlternateColorCodes('&', string);
return adapter.translateHex(string);
}
public static List<String> colorize(List<String> strings) {
return strings.stream().map(Colorizer::colorize).collect(Collectors.toList());
}
public static List<String> colorize(String... strings) {
return Arrays.stream(strings).map(Colorizer::colorize).collect(Collectors.toList());
}
public static String translateHex(String msg) {
return adapter.translateHex(msg);
}
public interface ColorAdapter {
String translateHex(String msg);
}
}On bukkit there are two events managed by Basement: BasementNewServerFound: Called when a server is added to the list of servers in memory BasementServerRemoved: Called when a server is removed or expires in the in-memory server list
BasementLib is able to offer a basic scoreboard to players. The integration is simple.
Example:
public class HubScoreboardAdapter implements ScoreboardProvider {
private BasementBukkitPlugin basementLib;
private JavaPlugin plugin;
private String title = "&d&lEXAMPLE";
private List<String> lines = Arrays.asList(
"",
" &8› &fOnline: &b%basement_counter_%",
" &8› &fLobby: &b#1",
"",
" &8› &fRank: &b%luckperms_highest_group_by_weight%",
"",
" &8› &fLivello: &b0",
" &8› &fExp: &b0&7/&b1200%",
"",
"&d play.example.com"
);
public HubScoreboardAdapter(JavaPlugin plugin) {
this.plugin = plugin;
this.basementLib = (BasementBukkitPlugin) BasementProvider.get();
basementLib.registerScoreboard(this, 10);
basementLib.getScoreboardManager().start();
}
@Override
public String getTitle(Player player) {
return this.title.replace("&", "§");
}
@Override
public List<ScoreboardLine> getLines(Player player) {
return this.build(player, PlaceholderAPI.setPlaceholders(player, new ArrayList<>(this.lines.stream().map(line -> line.replace("&", "§")).toList())));
}
@Override
public JavaPlugin getPlugin() {
return plugin;
}
@Override
public ScoreboardAdapter getAdapter() {
return basementLib.getScoreboardAdapter();
}
}Nothing special for now
-
Create the query
private final QueryBuilderUpdate queryUpdateUserData; public UserManager() { queryUpdateUserData = basement.database().update().table("players") .setNQ("xp", "?") .setNQ("level", "?").setNQ("coins", "?") .setNQ("language", "?") .where(WhereBuilder.builder().equalsNQ("uuid", "?").close()); }
-
Sync
private void save(UserData data) { queryUpdateUserData.patternClone().clearSet() .setNQ("xp", data.getXp()).setNQ("level", data.getNetworkLevel()) .setNQ("coins", data.getNetworkCoins()) .where(WhereBuilder.builder().equals("uuid", data.getUuid()).close()) .build().exec(); }
-
Async
private CompletableFuture<QueryBuilderUpdate> saveAsync(UserData data) { return queryUpdateUserData.patternClone().clearSet() .setNQ("xp", data.getXp()).setNQ("level", data.getNetworkLevel()) .setNQ("coins", data.getNetworkCoins()) .where(WhereBuilder.builder().equals("uuid", data.getUuid()).close()) .build().execAsync(); }
protected final RSetCache<Data> data;
public SomeClass(){
data=basement.redisManager().redissonClient().getSetCache("some_data");
}
public Optional<Data> getData(UUID uuid){
return data.stream().filter(data->data.getUuid().equals(uuid)).findFirst();
}Suggest me
- Additional minecraft version support for scoreboard (newer)
- TAB