Skip to content

Commit

Permalink
GH-307 Implement Fabric platform support (#376)
Browse files Browse the repository at this point in the history
* Fix errors in the findByInstanceOf method

* Fabric Support

* Remove unused message

* Fix ci, full string argument

* update build.gradle.kts

* Update examples/fabric/src/main/java/dev/rollczi/example/fabric/ExampleFabric.java

Co-authored-by: Norbert Dejlich <ndejlich5@gmail.com>

* Update litecommands-fabric/build.gradle.kts

Co-authored-by: Norbert Dejlich <ndejlich5@gmail.com>

* resolve all

* Add fabric.mod.json

* Update litecommands-fabric/src/main/java/dev/rollczi/litecommands/fabric/FabricPlatform.java

* Update litecommands-fabric/src/main/java/dev/rollczi/litecommands/fabric/FabricPlatform.java

* update build.gradle.kts

* update fabric.mod.json, remove unused

* update fabric.mod.json

* Add litecommands icon.svg

* Move some dependencies.

* Rename player only context. Add private constructor to LiteFabricFactory

* Update example build.gradle.kts

* Update example build.gradle.kts

* update fabric.mod.json

* Simplify fabric command. Fix issue with string argument.

* Update icons.

* update fabric.mod.json

* Update fabric.mod.json

* Update README.md

* Update README.md

* Change icons.

* Remove minecraft version from publication.

---------

Co-authored-by: Norbert Dejlich <ndejlich5@gmail.com>
  • Loading branch information
huanmeng-qwq and Rollczi committed Feb 17, 2024
1 parent f294f9b commit e353cdf
Show file tree
Hide file tree
Showing 24 changed files with 599 additions and 0 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@ Annotation based command framework for Velocity, Bukkit, Paper, BungeeCord, Mine
- Adventure Kyori support. [(extension)](https://litedevelopers.github.io/LiteDevelopers-documentation/adventure-kyori.html)
- and [more](https://litedevelopers.github.io/LiteDevelopers-documentation/introdution.html)! ✨

## 📦 Platforms
- Velocity
- Bukkit, Spigot, Paper
- BungeeCord, Waterfall
- Minestom _(by [Codestech1](https://github.com/Codestech1))_
- Sponge _(by [BlackBaroness](https://github.com/BlackBaroness))_
- Fabric _(by [huanmeng_qwq](https://github.com/huanmeng-qwq))_
- JDA

## 💡 Command Example
This is an example of `/hello <name> <amount>` command:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ plugins {
repositories {
mavenCentral()

maven("https://maven.fabricmc.net/") // fabric
maven("https://repo.dmulloy2.net/repository/public/") // protocol lib
maven("https://papermc.io/repo/repository/maven-public/") // paper, adventure, velocity
maven("https://repo.opencollab.dev/maven-snapshots") // nukkit
Expand Down
32 changes: 32 additions & 0 deletions examples/fabric/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
plugins {
id("java")
id("fabric-loom") version "1.5-SNAPSHOT"
}

java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}

repositories {
mavenCentral()
maven("https://repo.panda-lang.org/releases/")
maven("https://maven.fabricmc.net/")
}

dependencies {
minecraft("com.mojang:minecraft:1.20.4")
mappings("net.fabricmc:yarn:1.20.4+build.3:v2")

modImplementation("net.fabricmc:fabric-loader:0.15.6")
modImplementation("net.fabricmc.fabric-api:fabric-api:0.96.1+1.20.4")

// modImplementation("dev.rollczi:litecommands-fabric:3.3.4") // <-- uncomment in your project
implementation(project(path = ":litecommands-fabric", configuration = "namedElements"))

}

sourceSets.test {
java.setSrcDirs(emptyList<String>())
resources.setSrcDirs(emptyList<String>())
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package dev.rollczi.example.fabric;

import dev.rollczi.example.fabric.command.BanCommand;
import dev.rollczi.litecommands.fabric.LiteFabricFactory;
import net.fabricmc.api.ModInitializer;


public class ExampleFabric implements ModInitializer {
@Override
public void onInitialize() {
LiteFabricFactory.create()
.commands(new BanCommand())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package dev.rollczi.example.fabric.command;

import dev.rollczi.litecommands.annotations.argument.Arg;
import dev.rollczi.litecommands.annotations.command.Command;
import dev.rollczi.litecommands.annotations.context.Context;
import dev.rollczi.litecommands.annotations.execute.Execute;
import dev.rollczi.litecommands.annotations.join.Join;
import dev.rollczi.litecommands.annotations.quoted.Quoted;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text;


@Command(name = "ban")
public class BanCommand {
@Execute
void execute(@Arg("player") ServerPlayerEntity player, @Join("reason") String reason) {
player.networkHandler.disconnect(Text.of(reason));
}

@Execute(name = "warnSelf")
Text execute(@Quoted @Arg String reason) {
return Text.of("You have been warned: " + reason);
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 22 additions & 0 deletions examples/fabric/src/main/resources/fabric.mod.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"schemaVersion": 1,
"id": "litecommands_example",
"version": "3.3.4",
"name": "LiteCommands Fabric Example",
"description": "Annotation based command framework for Fabric.",
"authors": [
"Rollczi",
"huanmeng_qwq"
],
"contact": {
"homepage": "https://github.com/Rollczi/LiteCommands",
"issues": "https://github.com/Rollczi/LiteCommands/issues"
},
"license": "Apache-2.0",
"icon": "assets/litecommands/icon.png",
"entrypoints": {
"main": [
"dev.rollczi.example.fabric.ExampleFabric"
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ protected ParseResult<String> parse(Invocation<SENDER> invocation, Argument<Stri
@Override
public SuggestionResult suggest(Invocation<SENDER> invocation, Argument<String> objectStringArgument, SuggestionContext context) {
String first = context.getCurrent().multilevel();

if (first.isEmpty()) {
return SuggestionResult.of("<" + objectStringArgument.getName() + ">");
}

return SuggestionResult.of("<" + objectStringArgument.getName() + ">", first);
}

Expand Down
33 changes: 33 additions & 0 deletions litecommands-fabric/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
plugins {
`litecommands-java-17`
`litecommands-repositories`
`litecommands-publish`
id("fabric-loom") version "1.5-SNAPSHOT"
}

val minecraft_version: String by project
val yarn_mappings: String by project
val fabric_loader_version: String by project
val fabric_api_version: String by project

repositories {
maven("https://libraries.minecraft.net")
}

dependencies {
// LiteCommands
api(project(":litecommands-framework"))
include(project(":litecommands-framework"))

// Minecraft and mappings
minecraft("com.mojang:minecraft:${minecraft_version}")
mappings("net.fabricmc:yarn:${yarn_mappings}:v2")

// Fabric loader and API
modImplementation("net.fabricmc:fabric-loader:${fabric_loader_version}")
modImplementation("net.fabricmc.fabric-api:fabric-api:${fabric_api_version}")
}

litecommandsPublish {
artifactId = "litecommands-fabric"
}
8 changes: 8 additions & 0 deletions litecommands-fabric/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Fabric Properties
# check these on https://fabricmc.net/develop
minecraft_version=1.20.4
yarn_mappings=1.20.4+build.3

# Fabric loader and API
fabric_loader_version=0.15.6
fabric_api_version=0.96.1+1.20.4
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package dev.rollczi.litecommands.fabric;

import com.mojang.brigadier.Command;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import dev.rollczi.litecommands.argument.parser.input.ParseableInput;
import dev.rollczi.litecommands.argument.suggester.input.SuggestionInput;
import dev.rollczi.litecommands.command.CommandRoute;
import dev.rollczi.litecommands.input.raw.RawCommand;
import dev.rollczi.litecommands.invocation.Invocation;
import dev.rollczi.litecommands.platform.PlatformInvocationListener;
import dev.rollczi.litecommands.platform.PlatformSuggestionListener;
import dev.rollczi.litecommands.suggestion.SuggestionResult;
import net.minecraft.server.command.ServerCommandSource;
import org.jetbrains.annotations.NotNull;

import java.util.List;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;

class FabricCommand {

private static final String FULL_ARGUMENTS = "[...]";

private final CommandRoute<ServerCommandSource> baseRoute;
private final PlatformInvocationListener<ServerCommandSource> invocationHook;
private final PlatformSuggestionListener<ServerCommandSource> suggestionHook;

FabricCommand(CommandRoute<ServerCommandSource> baseRoute, PlatformInvocationListener<ServerCommandSource> invocationHook, PlatformSuggestionListener<ServerCommandSource> suggestionHook) {
this.baseRoute = baseRoute;
this.invocationHook = invocationHook;
this.suggestionHook = suggestionHook;
}

LiteralArgumentBuilder<ServerCommandSource> toLiteral() {
LiteralArgumentBuilder<ServerCommandSource> baseArgument = LiteralArgumentBuilder.literal(baseRoute.getName());

this.appendRoute(baseArgument, baseRoute);
return baseArgument;
}

private void appendRoute(LiteralArgumentBuilder<ServerCommandSource> baseLiteral, CommandRoute<ServerCommandSource> route) {
boolean isBase = route == baseRoute;
LiteralArgumentBuilder<ServerCommandSource> literal = isBase
? baseLiteral
: LiteralArgumentBuilder.literal(route.getName());

literal.then(this.createArguments());

for (CommandRoute<ServerCommandSource> child : route.getChildren()) {
this.appendRoute(literal, child);
}
if (!isBase) {
baseLiteral.then(literal);
}
}

@NotNull
private RequiredArgumentBuilder<ServerCommandSource, String> createArguments() {
return RequiredArgumentBuilder
.<ServerCommandSource, String>argument(FULL_ARGUMENTS, StringArgumentType.greedyString())
.executes(context -> {
RawCommand rawCommand = RawCommand.from(context.getInput());
ParseableInput<?> parseableInput = rawCommand.toParseableInput();
FabricSender platformSender = new FabricSender(context.getSource());
Invocation<ServerCommandSource> invocation = new Invocation<>(context.getSource(), platformSender, baseRoute.getName(), rawCommand.getLabel(), parseableInput);

invocationHook.execute(invocation, parseableInput);
return Command.SINGLE_SUCCESS;
})
.suggests((context, builder) -> CompletableFuture.supplyAsync(() -> {
String input = context.getInput();
RawCommand rawCommand = RawCommand.from(input);
SuggestionInput<?> suggestionInput = rawCommand.toSuggestionInput();
FabricSender platformSender = new FabricSender(context.getSource());
Invocation<ServerCommandSource> invocation = new Invocation<>(context.getSource(), platformSender, baseRoute.getName(), rawCommand.getLabel(), suggestionInput);

SuggestionResult suggest = suggestionHook.suggest(invocation, suggestionInput);

List<String> arguments = suggestionInput.asList();
int start = input.length() - arguments.get(arguments.size() - 1).length();
SuggestionsBuilder suggestionsBuilder = builder.createOffset(start);

for (String suggestion : suggest.asMultiLevelList()) {
suggestionsBuilder.suggest(suggestion);
}

return suggestionsBuilder.build();
}));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package dev.rollczi.litecommands.fabric;

import dev.rollczi.litecommands.command.CommandRoute;
import dev.rollczi.litecommands.platform.AbstractPlatform;
import dev.rollczi.litecommands.platform.Platform;
import dev.rollczi.litecommands.platform.PlatformInvocationListener;
import dev.rollczi.litecommands.platform.PlatformSettings;
import dev.rollczi.litecommands.platform.PlatformSuggestionListener;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
import net.minecraft.server.command.ServerCommandSource;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

class FabricPlatform extends AbstractPlatform<ServerCommandSource, PlatformSettings> implements Platform<ServerCommandSource, PlatformSettings> {

private final Map<UUID, FabricCommand> fabricCommands = new HashMap<>();

FabricPlatform(PlatformSettings settings) {
super(settings);
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
for (FabricCommand fabricCommand : fabricCommands.values()) {
dispatcher.register(fabricCommand.toLiteral());
}
});
}

@Override
protected void hook(CommandRoute<ServerCommandSource> commandRoute, PlatformInvocationListener<ServerCommandSource> invocationHook, PlatformSuggestionListener<ServerCommandSource> suggestionHook) {
fabricCommands.put(commandRoute.getUniqueId(), new FabricCommand(commandRoute, invocationHook, suggestionHook));
}

@Override
protected void unhook(CommandRoute<ServerCommandSource> commandRoute) {
fabricCommands.remove(commandRoute.getUniqueId());
// TODO: unregister command from dispatcher
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package dev.rollczi.litecommands.fabric;

import dev.rollczi.litecommands.identifier.Identifier;
import dev.rollczi.litecommands.platform.AbstractPlatformSender;
import net.minecraft.server.command.ServerCommandSource;

public class FabricSender extends AbstractPlatformSender {
private final ServerCommandSource source;

public FabricSender(ServerCommandSource source) {
this.source = source;
}

@Override
public String getName() {
return source.getName();
}

@Override
public Identifier getIdentifier() {
return Identifier.of(source.getEntity().getUuid());
}

@Override
public boolean hasPermission(String permission) {
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package dev.rollczi.litecommands.fabric;

import dev.rollczi.litecommands.LiteCommandsBuilder;
import dev.rollczi.litecommands.LiteCommandsFactory;
import dev.rollczi.litecommands.fabric.argument.PlayerArgument;
import dev.rollczi.litecommands.fabric.argument.WorldArgument;
import dev.rollczi.litecommands.fabric.context.FabricOnlyPlayerContext;
import dev.rollczi.litecommands.message.MessageRegistry;
import dev.rollczi.litecommands.platform.PlatformSettings;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.text.Text;
import net.minecraft.world.World;

public final class LiteFabricFactory {

private LiteFabricFactory() {
}

@SuppressWarnings("unchecked")
public static <B extends LiteCommandsBuilder<ServerCommandSource, PlatformSettings, B>> B create() {
return (B) LiteCommandsFactory.builder(ServerCommandSource.class, new FabricPlatform(new LiteFabricSettings()))
.selfProcessor((builder, internal) -> {
MessageRegistry<ServerCommandSource> messageRegistry = internal.getMessageRegistry();

builder
.context(ServerPlayerEntity.class, new FabricOnlyPlayerContext<>(messageRegistry))
.context(PlayerEntity.class, new FabricOnlyPlayerContext<>(messageRegistry))
.result(String.class, new StringHandler())
.result(Text.class, new TextHandler())
.argument(PlayerEntity.class, new PlayerArgument<>(messageRegistry))
.argument(ServerPlayerEntity.class, new PlayerArgument<>(messageRegistry))

.argument(World.class, new WorldArgument<>(messageRegistry))
.argument(ServerWorld.class, new WorldArgument<>(messageRegistry))
;
})
;
}

}
Loading

0 comments on commit e353cdf

Please sign in to comment.