Skip to content

Commit

Permalink
1.0.0.-ALPHA4 with EngineX and Synchronization.
Browse files Browse the repository at this point in the history
  • Loading branch information
ShindouMihou committed Feb 21, 2022
1 parent e9ed109 commit d756e4a
Show file tree
Hide file tree
Showing 31 changed files with 1,039 additions and 123 deletions.
129 changes: 129 additions & 0 deletions examples/synchronization/Main.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package pw.mihou.nexus;

import org.javacord.api.DiscordApiBuilder;
import org.javacord.api.util.logging.ExceptionLogger;
import pw.mihou.nexus.core.threadpool.NexusThreadPool;
import pw.mihou.nexus.features.command.facade.NexusCommand;

import java.util.Arrays;
import java.util.concurrent.TimeUnit;

public class Test {

private static final Nexus nexus = Nexus.builder().build();

public static void main(String[] args) {
nexus.createCommandFrom(new AGlobalCommand());
nexus.createCommandFrom(new ASpecificServerCommand());
NexusCommand dynamic = nexus.createCommandFrom(new ADynamicCommand());

new DiscordApiBuilder()
.setToken(System.getenv("token"))
.setAllIntents()
.setTotalShards(4)
.addListener(nexus)
.loginAllShards()
.forEach(future -> future.thenAccept(discordApi -> {
System.out.println("Shard " + discordApi.getCurrentShard() + " is now online.");

//--------------------
// IMPORTANT IMPORTANT IMPORTANT IMPORTANT
// Always remember to include this line when starting up your shard.
// This allows the Nexus Engine to start performing tasks dedicated to a specific shard.
// and also allows Nexus to function more completely.
// IMPORTANT IMPORTANT IMPORTANT IMPORTANT
// ------------------
nexus.getShardManager().put(discordApi);

}).exceptionally(ExceptionLogger.get()));

//---------------------
// Global synchronization of all commands, recommended at startup.
// This updates, creates or removes any commands that are missing, outdated or removed.
//----------------------
nexus.getSynchronizer()
.synchronize(4)
.thenAccept(unused -> System.out.println("Synchronization with Discord's and Nexus' command repository is now complete."))
.exceptionally(ExceptionLogger.get());

//------------------
// Demonstration of dynamic server command updating.
//-----------------

NexusThreadPool.schedule(() -> {
dynamic.addSupportFor(853911163355922434L, 858685857511112736L);
System.out.println("Attempting to perform dynamic updates...");

// We recommend using batch update if you performed more than 1 `addSupportFor` methods.
// As batch update will update all of those command using only one request.
// batchUpdate(853911163355922434L, 4);
// batchUpdate(858685857511112736L, 4);

// Single update, on the otherwise, allows multiple server ids but sends a single create or update
// request for a command and doesn't scale well when done with many commands.
singleUpdate(dynamic, 4, 853911163355922434L, 858685857511112736L);
}, 1, TimeUnit.MINUTES);

NexusThreadPool.schedule(() -> {
dynamic.removeSupportFor(853911163355922434L);
System.out.println("Attempting to perform dynamic updates...");

// The same information as earlier, batch update will update the entire server slash command list
// which means it will remove any slash commands that are no longer supporting that server
// and will update or create any slash commands that still support that server.
// batchUpdate(853911163355922434L, 4);

// Single delete is fine when you are only deleting one command on a pile of servers.
singleDelete(dynamic, 4, 853911163355922434L);
}, 2, TimeUnit.MINUTES);
}

/**
* Updates, removes or creates any commands that are outdated, removed or missing. This is recommended
* especially when you recently added support to a lot of servers. Not recommended on startup since
* {@link pw.mihou.nexus.features.command.synchronizer.NexusSynchronizer#synchronize(int)} is more recommended for
* startup-related synchronization.
*
* @param serverId The server id to synchronize commands to.
* @param totalShards The total shards of the server.
*/
private static void batchUpdate(long serverId, int totalShards) {
nexus.getSynchronizer()
.batchUpdate(serverId, totalShards)
.thenAccept(unused -> System.out.println("A batch update was complete. [server="+serverId+"]"))
.exceptionally(ExceptionLogger.get());
}

/**
* Updates a single command on one or many servers. This is practically the same as batch update but utilizes a more
* update or create approach whilst {@link Test#batchUpdate(long, int)} overrides the entire server slash command list
* with what Nexus knows.
*
* @param command The command to update on the specified servers.
* @param totalShards The total amount of shards of the bot, this is used for sharding formula.
* @param serverIds The server ids to update the bot on.
*/
private static void singleUpdate(NexusCommand command, int totalShards, long... serverIds) {
nexus.getSynchronizer()
.upsert(command, totalShards, serverIds)
.thenAccept(unused -> System.out.println("A batch upsert was complete. [servers="+ Arrays.toString(serverIds) +"]"))
.exceptionally(ExceptionLogger.get());
}

/**
* Deletes a single command on one or many servers. This is practically the same as batch update but utilizes a more
* delete approach whilst {@link Test#batchUpdate(long, int)} overrides the entire server slash command list
* with what Nexus knows.
*
* @param command The command to update on the specified servers.
* @param totalShards The total amount of shards of the bot, this is used for sharding formula.
* @param serverIds The server ids to update the bot on.
*/
private static void singleDelete(NexusCommand command, int totalShards, long... serverIds) {
nexus.getSynchronizer()
.delete(command, totalShards, serverIds)
.thenAccept(unused -> System.out.println("A batch delete was complete. [servers="+ Arrays.toString(serverIds) +"]"))
.exceptionally(ExceptionLogger.get());
}

}
16 changes: 16 additions & 0 deletions examples/synchronization/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# 🍓 Synchronization and Dynamic Commands
Nexus now supports dynamic updating of server slash commands and also a better synchronization that doesn't require
you to find a spot to find when the final shard starts before you could start synchronizing which is powered by Nexus's
entire new shard wrapping engine: EngineX.

The new synchronization function of Nexus works by utilizing the power of batch override for slash commands to synchronize Discord's
database of the bot's slash commands with what Nexus repositories knows. There are methods such as `upsert` and `delete` which goes against
the batch override and performs single updates for cases where you aren't sure about doing a batch update.

You can view a full example of how this synchronization looks from the `Main.java` file on this folder.

# 🥞 EngineX & Synchronization
EngineX is a critical factor into the new synchronization system as it allows Nexus to queue any synchronization requests for a specific shard
or for any shard available to take without the end-user finding a way where to place the synchronization method. It's workings are a bit complicated but
you can view the full source code here.
- [View Source](https://github.com/ShindouMihou/Nexus/tree/master/src/main/java/pw/mihou/nexus/core/enginex)
27 changes: 27 additions & 0 deletions examples/synchronization/commands/ADynamicCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package pw.mihou.nexus.commands;

import pw.mihou.nexus.features.command.annotation.NexusAttach;
import pw.mihou.nexus.features.command.facade.NexusCommandEvent;
import pw.mihou.nexus.features.command.facade.NexusHandler;

import java.util.List;

@NexusAttach
public class ADynamicCommand implements NexusHandler {

private final String name = "dynamic";
private final String description = "A dynamic server slash command!";

// 0L is recognized by Nexus as a switch to recognize this command as
// a server slash command. It is ignored in any sort of updates.
private final List<Long> serverIds = List.of(
0L
);

@Override
public void onEvent(NexusCommandEvent event) {
event.respondNow()
.setContent("Dyna-dynam-iteee!")
.respond();
}
}
19 changes: 19 additions & 0 deletions examples/synchronization/commands/AGlobalCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package pw.mihou.nexus.commands;

import pw.mihou.nexus.features.command.annotation.NexusAttach;
import pw.mihou.nexus.features.command.facade.NexusCommandEvent;
import pw.mihou.nexus.features.command.facade.NexusHandler;

@NexusAttach
public class AGlobalCommand implements NexusHandler {

private final String name = "globalCommand";
private final String description = "This is a global command that every server can use.";

@Override
public void onEvent(NexusCommandEvent event) {
event.respondNow()
.setContent("Pong! Ping! Hello Global!")
.respond();
}
}
24 changes: 24 additions & 0 deletions examples/synchronization/commands/ASpecificServerCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package pw.mihou.nexus.commands;

import pw.mihou.nexus.features.command.annotation.NexusAttach;
import pw.mihou.nexus.features.command.facade.NexusCommandEvent;
import pw.mihou.nexus.features.command.facade.NexusHandler;

import java.util.List;

@NexusAttach
public class ASpecificServerCommand implements NexusHandler {

private final String name = "specificServer";
private final String description = "This is a command dedicated to a specific server!";
private final List<Long> serverIds = List.of(
807084089013174272L
);

@Override
public void onEvent(NexusCommandEvent event) {
event.respondNow()
.setContent("This command is dedicated to this server!")
.respond();
}
}
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>pw.mihou</groupId>
<artifactId>Nexus</artifactId>
<version>1.0.0</version>
<version>1.0.0-ALPHA4</version>
<name>Nexus</name>
<description>Nexus is the next-generation Javacord framework that aims to create Discord bots with less code, dynamic, more simplicity and beauty.</description>
<url>https://github.com/ShindouMihou/Nexus</url>
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/pw/mihou/nexus/Nexus.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import pw.mihou.nexus.core.managers.NexusShardManager;
import pw.mihou.nexus.core.managers.facade.NexusCommandManager;
import pw.mihou.nexus.features.command.facade.NexusCommand;
import pw.mihou.nexus.features.command.synchronizer.NexusSynchronizer;
import pw.mihou.nexus.features.ratelimiter.facade.NexusRatelimiter;

public interface Nexus extends SlashCommandCreateListener {
Expand Down Expand Up @@ -40,6 +41,15 @@ static void setLogger(NexusLoggingAdapter adapter) {
*/
NexusCommandManager getCommandManager();

/**
* Retrieves the command synchronizer that is available for
* this {@link Nexus} instance.
*
* @return The command synchronizer that is usable by this
* {@link Nexus} instance.
*/
NexusSynchronizer getSynchronizer();

/**
* Retrieves the shard manager that is being utilized by
* this {@link Nexus} instance.
Expand Down
22 changes: 21 additions & 1 deletion src/main/java/pw/mihou/nexus/core/NexusCore.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import org.javacord.api.event.interaction.SlashCommandCreateEvent;
import pw.mihou.nexus.Nexus;
import pw.mihou.nexus.core.configuration.core.NexusConfiguration;
import pw.mihou.nexus.core.enginex.core.NexusEngineXCore;
import pw.mihou.nexus.core.enginex.facade.NexusEngineX;
import pw.mihou.nexus.core.logger.adapters.NexusLoggingAdapter;
import pw.mihou.nexus.core.logger.adapters.defaults.NexusDefaultLoggingAdapter;
import pw.mihou.nexus.core.managers.core.NexusCommandManagerCore;
Expand All @@ -15,6 +17,7 @@
import pw.mihou.nexus.features.command.core.NexusBaseCommandImplementation;
import pw.mihou.nexus.features.command.core.NexusCommandCore;
import pw.mihou.nexus.features.command.facade.NexusCommand;
import pw.mihou.nexus.features.command.synchronizer.NexusSynchronizer;
import pw.mihou.nexus.features.messages.defaults.NexusDefaultMessageConfiguration;
import pw.mihou.nexus.features.messages.facade.NexusMessageConfiguration;
import pw.mihou.nexus.features.ratelimiter.core.NexusRatelimiterCore;
Expand All @@ -35,6 +38,8 @@ public class NexusCore implements Nexus {
private final DiscordApiBuilder builder;
private final Consumer<DiscordApi> onShardLogin;
private final NexusConfiguration nexusConfiguration;
private final NexusEngineX engineX = new NexusEngineXCore(this);
private final NexusSynchronizer synchronizer = new NexusSynchronizer(this);

/**
* Creates a new Nexus Core with a customized {@link NexusMessageConfiguration} and
Expand All @@ -52,7 +57,7 @@ public NexusCore(
) {
this.builder = builder;
this.onShardLogin = onShardLogin;
this.shardManager = new NexusShardManager();
this.shardManager = new NexusShardManager(this);
this.nexusConfiguration = nexusConfiguration;
this.messageConfiguration = Objects.requireNonNullElseGet(messageConfiguration, NexusDefaultMessageConfiguration::new);
}
Expand All @@ -62,6 +67,11 @@ public NexusCommandManager getCommandManager() {
return commandManager;
}

@Override
public NexusSynchronizer getSynchronizer() {
return synchronizer;
}

@Override
public NexusShardManager getShardManager() {
return shardManager;
Expand All @@ -77,6 +87,15 @@ public NexusConfiguration getConfiguration() {
return nexusConfiguration;
}

/**
* Gets the queueing engine for this {@link Nexus} instance.
*
* @return The queueing engine of this instance.
*/
public NexusEngineX getEngineX() {
return engineX;
}

@Override
public NexusCommand createCommandFrom(Object model) {
return NexusReflectiveCore.accept(model, NexusCommandCore.class, this);
Expand Down Expand Up @@ -123,6 +142,7 @@ public Nexus start() {
.forEach(future -> future.thenAccept(shards::add).join());

this.shardManager = new NexusShardManager(
this,
shards.stream()
.sorted(Comparator.comparingInt(DiscordApi::getCurrentShard))
.toArray(DiscordApi[]::new)
Expand Down
5 changes: 4 additions & 1 deletion src/main/java/pw/mihou/nexus/core/builder/NexusBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import pw.mihou.nexus.core.configuration.core.NexusConfiguration;
import pw.mihou.nexus.features.messages.facade.NexusMessageConfiguration;

import java.time.Duration;
import java.util.function.Consumer;
import java.util.function.Function;

Expand All @@ -15,7 +16,9 @@ public class NexusBuilder {
private NexusMessageConfiguration messageConfiguration;
private DiscordApiBuilder builder;
private Consumer<DiscordApi> onShardLogin;
private NexusConfiguration nexusConfiguration = new NexusConfiguration(true);
private NexusConfiguration nexusConfiguration = new NexusConfiguration(
Duration.ofMinutes(10)
);

/**
* Sets the {@link NexusMessageConfiguration} that {@link Nexus} uses whenever
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package pw.mihou.nexus.core.configuration.core;

import java.time.Duration;

/**
* {@link NexusConfiguration} is a record that contains all the configuration
* settings of a {@link pw.mihou.nexus.Nexus} instance and is recommended to have.
*/
public record NexusConfiguration(
boolean autoApplySupportedServerChangesForServers
Duration timeBeforeExpiringEngineRequests
) {}
Loading

0 comments on commit d756e4a

Please sign in to comment.