Skip to content

Commit 6848043

Browse files
0.0.9-alpha, more dev tools...
T-T
1 parent 14e7839 commit 6848043

5 files changed

Lines changed: 425 additions & 107 deletions

File tree

README.md

Lines changed: 122 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,30 @@
11
# CommandAPI
22

3+
4+
5+
A lightweight and extensible command framework for Bukkit / Spigot plugins, designed to simplify command handling, subcommands, tab completion, and debugging.
6+
7+
CommandAPI focuses on clean structure, developer ergonomics, and future scalability.
8+
39
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/jonagamerpro1234/CommandAPI)
410

5-
A flexible Bukkit/Spigot command system with built-in subcommands, permissions, and tab completion.
11+
### ✨ Features
12+
13+
✅ Clean BaseCommand system
14+
15+
✅ Modular SubCommand architecture
616

7-
Features
17+
✅ Built-in tab completion
818

9-
Define a main command with optional custom logic.
19+
✅ Optional main command logic (no subcommand required)
1020

11-
Add subcommands with their own permissions, console rules, and tab completion.
21+
✅ Centralized debug & logging system
1222

13-
Automatic tab suggestions for main commands and subcommands.
23+
✅ Execution and performance logging
1424

15-
Easy registration via your JavaPlugin.
25+
Easy to extend and maintain
1626

17-
Quick Example
27+
### Quick Example
1828
```java
1929
new BaseCommand() {
2030
{
@@ -36,7 +46,7 @@ new BaseCommand() {
3646
}.register(this); // automatically registers the command
3747
```
3848

39-
How it works
49+
### How it works
4050

4151
/mycommand → runs onCommandMain.
4252

@@ -46,7 +56,7 @@ Tab completion works automatically for both main command and subcommands.
4656

4757
Each subcommand can define permissions, console access, and custom tab lists.
4858

49-
##Maven
59+
### Maven
5060
```xml
5161
<dependency>
5262
<groupId>jss.commandapi</groupId>
@@ -55,16 +65,117 @@ Each subcommand can define permissions, console access, and custom tab lists.
5565
</dependency>
5666
```
5767

58-
## Gradle (Groovy DSL)
68+
### Gradle (Groovy DSL)
5969
```groovy
6070
dependencies {
6171
implementation 'jss.commandapi:command-api:0.0.8-alpha'
6272
}
6373
```
6474

65-
## Gradle (Kotlin DSL)
75+
### Gradle (Kotlin DSL)
6676
```kotlin
6777
dependencies {
6878
implementation("jss.commandapi:command-api:0.0.8-alpha")
6979
}
7080
```
81+
82+
## 🚀 Getting Started
83+
1️⃣ Initialize CommandAPI
84+
Call this once in your plugin onEnable():
85+
86+
``` java
87+
@Override
88+
public void onEnable() {
89+
CommandApi.init(this);
90+
CommandApi.setDebug(true);
91+
CommandApi.setLogExecution(true);
92+
CommandApi.setLogPerformance(true);
93+
}
94+
```
95+
2️⃣ Create a Base Command
96+
``` java
97+
public class MainCommand extends BaseCommand {
98+
99+
public MainCommand() {
100+
name("example")
101+
.addSubCommand(new HelpSubCommand())
102+
.addSubCommand(new ReloadSubCommand());
103+
}
104+
105+
@Override
106+
public boolean onCommandMain(CommandSender sender, String[] args) {
107+
sender.sendMessage("§eUse /example help");
108+
return true;
109+
}
110+
}
111+
```
112+
3️⃣ Register the Command
113+
```java
114+
@Override
115+
public void onEnable() {
116+
new MainCommand().register(this);
117+
}
118+
```
119+
⚠️ The command must exist in plugin.yml.
120+
121+
4️⃣ Create a SubCommand
122+
``` java
123+
public class HelpSubCommand extends SubCommand {
124+
125+
@Override
126+
public String name() {
127+
return "help";
128+
}
129+
130+
@Override
131+
public boolean onCommand(CommandSender sender, String[] args) {
132+
sender.sendMessage("§aThis is the help command!");
133+
return true;
134+
}
135+
}
136+
```
137+
138+
## 🧩 SubCommand Capabilities
139+
Each SubCommand can define:
140+
141+
- Permissions
142+
- Aliases
143+
- Tab completion
144+
- Console usage
145+
- Player-only restriction
146+
- Usage & description
147+
- Enable / disable logic
148+
149+
Example:
150+
```java
151+
@Override
152+
public List<String> aliases() {
153+
return List.of("h", "?");
154+
}
155+
```
156+
157+
### 🛠 Debug & Logging
158+
- CommandAPI includes a built-in utility for debugging and logging:
159+
- Command execution logs
160+
- Sender type (Player / Console)
161+
- Execution time
162+
- Error handling with stack traces (debug mode)
163+
164+
This helps a lot during development and production debugging.
165+
166+
## 🧪 Current Status
167+
- Version: 0.0.x-alpha
168+
- API is stable for testing
169+
- Breaking changes may occur until 1.0.0
170+
171+
## 🔮 Roadmap
172+
- Command execution hooks
173+
- Context-based command handling
174+
- Better permission resolvers
175+
- Metrics & analytics
176+
- Optional annotations support
177+
178+
## 📜 License
179+
MIT License – free to use, modify, and distribute.
180+
181+

src/main/java/jss/commandapi/BaseCommand.java

Lines changed: 60 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
* 3. Tab completion for both subcommands and the main command.
2121
*
2222
* @author jonagamerpro1234
23-
* @version 0.0.7-alpha
23+
* @version 0.0.9-alpha
2424
*/
2525
public abstract class BaseCommand implements TabExecutor {
2626

@@ -29,11 +29,6 @@ public abstract class BaseCommand implements TabExecutor {
2929

3030
/**
3131
* Registers this command automatically using the provided JavaPlugin.
32-
* <p>
33-
* This method sets this BaseCommand instance as both the executor and
34-
* the tab completer for the command defined in the plugin's plugin.yml.
35-
* It will log a warning if the command is not defined in plugin.yml.
36-
* </p>
3732
*
3833
* @param plugin the JavaPlugin instance used to register the command
3934
* @throws IllegalStateException if the command name has not been set or is empty
@@ -43,16 +38,18 @@ public void register(@NotNull JavaPlugin plugin) {
4338
if (name == null || name.isEmpty()) {
4439
throw new IllegalStateException("Command name cannot be null or empty!");
4540
}
41+
4642
PluginCommand command = plugin.getCommand(name);
4743
if (command == null) {
48-
plugin.getLogger().warning("Command '" + name + "' is not defined in plugin.yml!");
44+
CommandApi.log("Command '" + name + "' is not defined in plugin.yml!");
4945
return;
5046
}
47+
5148
command.setExecutor(this);
5249
command.setTabCompleter(this);
53-
plugin.getLogger().info("Registered command: " + name);
54-
}
5550

51+
CommandApi.log("Registered command: " + name);
52+
}
5653

5754
/**
5855
* Sets the main command name.
@@ -92,7 +89,6 @@ public BaseCommand addSubCommand(SubCommand... subCommands) {
9289

9390
/**
9491
* Logic executed when no subcommand is provided.
95-
* Override this in the concrete class if needed.
9692
*
9793
* @param sender the CommandSender executing the command
9894
* @param args the command arguments
@@ -105,8 +101,7 @@ public boolean onCommandMain(@NotNull CommandSender sender, String[] args) {
105101
}
106102

107103
/**
108-
* Tab completion suggestions for the main command
109-
* (when not completing a subcommand)
104+
* Tab completion suggestions for the main command.
110105
*
111106
* @param sender the CommandSender requesting tab completion
112107
* @param args the command arguments
@@ -118,84 +113,93 @@ public List<String> onTabMain(@NotNull CommandSender sender, String[] args) {
118113
}
119114

120115
/**
121-
* Handles command execution.
122-
* Routes to subcommands if one matches, otherwise calls onCommandMain.
123-
*
124-
* @param sender the CommandSender executing the command
125-
* @param command the Command object
126-
* @param label the command label
127-
* @param args the command arguments
128-
* @return true if execution was handled
129-
* @since 0.0.7-alpha
116+
* Handles command execution with debug, logging, and performance tracking.
117+
* @since 0.0.8-alpha
130118
*/
131119
@Override
132-
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String @NotNull [] args) {
133-
if (args.length < 1) {
134-
return onCommandMain(sender, args);
135-
}
120+
public boolean onCommand(
121+
@NotNull CommandSender sender,
122+
@NotNull Command command,
123+
@NotNull String label,
124+
String @NotNull [] args
125+
) {
126+
long startTime = System.nanoTime();
127+
128+
try {
129+
130+
if (args.length < 1) {
131+
return onCommandMain(sender, args);
132+
}
136133

137-
String subName = args[0];
138-
for (SubCommand sub : subCommands) {
139-
if (subName.equalsIgnoreCase(sub.name()) || sub.aliases().contains(subName)) {
134+
String subName = args[0];
140135

141-
if (!sub.isEnabled()) {
142-
sender.sendMessage(sub.disabledMessage());
143-
return true;
144-
}
136+
for (SubCommand sub : subCommands) {
137+
if (subName.equalsIgnoreCase(sub.name()) || sub.aliases().contains(subName)) {
145138

146-
if (sub.requiresPermission() && !sender.hasPermission("plugin." + sub.permission())) {
147-
sender.sendMessage("§cYou do not have permission to execute this command!");
148-
return true;
149-
}
139+
if (!sub.isEnabled()) {
140+
sender.sendMessage(sub.disabledMessage());
141+
return true;
142+
}
150143

151-
if (!sub.allowConsole() && !(sender instanceof Player)) {
152-
sender.sendMessage(cThis command cannot be executed from console!");
153-
return true;
154-
}
144+
if (sub.requiresPermission() && !sender.hasPermission("plugin." + sub.permission())) {
145+
sender.sendMessage(cYou do not have permission to execute this command!");
146+
return true;
147+
}
155148

156-
return sub.onCommand(sender, args);
149+
if (!sub.allowConsole() && !(sender instanceof Player)) {
150+
sender.sendMessage("§cThis command cannot be executed from console!");
151+
return true;
152+
}
153+
154+
return sub.onCommand(sender, args);
155+
}
157156
}
158-
}
159157

160-
return onCommandMain(sender, args);
158+
return onCommandMain(sender, args);
159+
160+
} catch (Exception exception) {
161+
162+
CommandApi.handleError(name, sender, exception);
163+
return true;
164+
165+
} finally {
166+
167+
long executionTimeMs = (System.nanoTime() - startTime) / 1_000_000;
168+
CommandApi.logCommandExecution(name, sender, executionTimeMs);
169+
}
161170
}
162171

163172
/**
164173
* Handles tab completion for the main command and its subcommands.
165174
*
166-
* @param sender the CommandSender requesting tab completion
167-
* @param command the Command object
168-
* @param alias the command alias
169-
* @param args the command arguments
170-
* @return a list of tab completion suggestions
171175
* @since 0.0.7-alpha
172176
*/
173177
@Override
174-
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String @NotNull [] args) {
178+
public List<String> onTabComplete(
179+
@NotNull CommandSender sender,
180+
@NotNull Command command,
181+
@NotNull String alias,
182+
String @NotNull [] args
183+
) {
175184

176185
List<String> suggestions = new ArrayList<>();
177186

178187
if (args.length == 1) {
179188
String start = args[0].toLowerCase();
180189

181-
// Subcommand suggestions
182190
List<String> subNames = subCommands.stream()
183191
.map(SubCommand::name)
184192
.filter(n -> n.toLowerCase().startsWith(start))
185193
.collect(Collectors.toList());
186194

187-
// Main command suggestions
188-
List<String> mainSuggestions = onTabMain(sender, args);
189-
190-
subNames.addAll(mainSuggestions);
195+
subNames.addAll(onTabMain(sender, args));
191196
suggestions.addAll(subNames);
192197

193198
} else if (args.length > 1) {
194199
for (SubCommand sub : subCommands) {
195200
if (args[0].equalsIgnoreCase(sub.name()) || sub.aliases().contains(args[0])) {
196201
List<String> tab = sub.onTabList(sender, args);
197-
if (tab != null) return tab;
198-
return new ArrayList<>();
202+
return tab != null ? tab : new ArrayList<>();
199203
}
200204
}
201205

@@ -205,22 +209,10 @@ public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Comman
205209
return suggestions;
206210
}
207211

208-
/**
209-
* Returns the list of registered subcommands.
210-
*
211-
* @return the list of subcommands
212-
* @since 0.0.7-alpha
213-
*/
214212
public List<SubCommand> getSubCommands() {
215213
return subCommands;
216214
}
217215

218-
/**
219-
* Returns the name of the main command.
220-
*
221-
* @return the command name
222-
* @since 0.0.7-alpha
223-
*/
224216
public String getName() {
225217
return name;
226218
}

0 commit comments

Comments
 (0)