Skip to content

Commit

Permalink
Fabric Platform, new badges and unit tests (#4)
Browse files Browse the repository at this point in the history
- Fabric Platform

- Made it working in bukkit 1.8.8-1.20.1

- LuckPerms (optional) support

- Added permissions to README.md

- Added a bypass permission

- craftinghomes logic full unit testing

- codecov badge add jacoco reports
  • Loading branch information
Crafter-Y authored Oct 25, 2023
1 parent c267fb2 commit 3a3f5bb
Show file tree
Hide file tree
Showing 41 changed files with 1,519 additions and 105 deletions.
Binary file added .github/media/craftinghomeslogic.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v3

- name: Setup Java 17
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'
cache: 'gradle'

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
Expand Down
12 changes: 6 additions & 6 deletions .github/workflows/gradle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,17 @@ permissions:

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: Build with Gradle
uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1
with:
arguments: build
- name: Install dependencies, run tests, and collect coverage
run: gradle build
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v3
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
41 changes: 36 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,23 @@ A simple Home plugin.

[![CodeQL](https://github.com/Crafter-Y/CraftingHomes/actions/workflows/codeql.yml/badge.svg)](https://github.com/Crafter-Y/CraftingHomes/actions/workflows/codeql.yml)
[![Java CI with Gradle](https://github.com/Crafter-Y/CraftingHomes/actions/workflows/gradle.yml/badge.svg)](https://github.com/Crafter-Y/CraftingHomes/actions/workflows/gradle.yml)
![GitHub contributors](https://img.shields.io/github/contributors/Crafter-Y/CraftingHomes)
![GitHub last commit (branch)](https://img.shields.io/github/last-commit/Crafter-Y/CraftingHomes/dev)
![codecov](https://codecov.io/gh/Crafter-Y/CraftingHomes/branch/dev/graph/badge.svg)

By itself, this is supposed to be a simple Home
plugin (for now) that has the basic commands `/home`, `/homes`,
`/sethome`, `/delhome` and 3 Homes per player.

## Versions

| Platform | Version(s) | Dependencies | Optional Dependencies |
|------------------------------|--------------|------------------------------------------------------------------------------|---------------------------------------------|
| Bukkit (Spigot, Paper, etc.) | 1.8.8-1.20.2 | none | none |
| Fabric | 1.20.1 | [sgui 1.2.2+1.20](https://github.com/Patbox/sgui/releases/tag/1.2.2%2B1.20) | [LuckPerms](https://luckperms.net/download) |

Platform or version missing? Just create an issue.

## Configuration

Several things can be configured in the `config.yml` file.
Expand All @@ -19,11 +31,29 @@ Several things can be configured in the `config.yml` file.

You can add your own language by creating a new file your_language.lang.yml

## Permissions

If there is no permission provider (Fabric without LuckPerms), op (level 3) is the default permission for admin
commands and no permission for normal commands.

| Permission Node | Description | Default Value |
|---------------------------|------------------------------------------|---------------|
| craftinghomes.* | All CraftingHomes permissions | op |
| craftinghomes.home | Access to /home [home] command | true |
| craftinghomes.sethome | Access to /sethome [home] command | true |
| craftinghomes.delhome | Access to /delhome [home] command | true |
| craftinghomes.bypass | Allows bypassing the maxHomes limit | op |
| craftinghomes.homes.* | Access to all /homes commands | op |
| craftinghomes.homes.use | Access to /homes command | true |
| craftinghomes.homes.other | Access to /homes <player> [home] command | op |

## Technical insight

I know that i over-complicated many things, but this is a kind of exercise for me.
I know that I over-complicated many things, but this is a kind of exercise for me.
You may look at the `craftinghomes` subproject that contains the whole logic.
Everything else is just to make it work.
Everything else is just there to make it work.

[![Logic Diagram](./.github/media/craftinghomeslogic.png)](https://github.com/Crafter-Y/CraftingHomes)

## Roadmap
- [x] Persistent Data Storage
Expand All @@ -36,7 +66,8 @@ Everything else is just to make it work.
- [x] /homes <player> for admins with permission
- [x] /homes <player> <home> for admins with permission
- [x] data storage templating
- [ ] other implementations like fabric or forge (once feature complete)
- [ ] more ci tests
- [ ] unit tests
- [x] fabric implementation
- [ ] other implementations like forge
- [x] more ci tests
- [x] unit tests
- [ ] more data storage providers (mysql, sqlite, mongodb)
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@
import java.io.PrintWriter;
import java.io.Writer;
import java.util.*;
import java.util.stream.Collectors;

@SupportedAnnotationTypes("de.craftery.craftinghomes.annotation.annotations.*")
@SupportedSourceVersion(SourceVersion.RELEASE_17)
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class CraftingAnnotationProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
Expand All @@ -38,10 +39,11 @@ private boolean processDataModelAnnotations(RoundEnvironment roundEnv) {
}
List<String> commands = new ArrayList<>();
for (Element element : elements) {
if (!(element instanceof TypeElement providerClass)) {
if (!(element instanceof TypeElement)) {
error("Target must be a class");
return false;
}
TypeElement providerClass = (TypeElement) element;

commands.add(providerClass.getQualifiedName().toString());
}
Expand Down Expand Up @@ -69,10 +71,11 @@ private boolean processCommandAnnotations(RoundEnvironment roundEnv) {

List<String> commands = new ArrayList<>();
for (Element element : elements) {
if (!(element instanceof TypeElement providerClass)) {
if (!(element instanceof TypeElement)) {
error("Target must be a class");
return false;
}
TypeElement providerClass = (TypeElement) element;

commands.add(providerClass.getQualifiedName().toString());
}
Expand Down Expand Up @@ -103,10 +106,11 @@ private boolean processI18nAnnotations(RoundEnvironment roundEnv) {
}

Element providerElement = elements.iterator().next();
if (!(providerElement instanceof TypeElement providerClass)) {
if (!(providerElement instanceof TypeElement)) {
error("Entry element must be a interface!");
return false;
}
TypeElement providerClass = (TypeElement) providerElement;

if (providerClass.getKind() != ElementKind.INTERFACE) {
error("Entry element must be a interface!");
Expand Down Expand Up @@ -136,9 +140,9 @@ private boolean processI18nAnnotations(RoundEnvironment roundEnv) {
for (VariableElement parameter : method.getParameters()) {
String paramType;
switch (parameter.asType().toString()) {
case "java.lang.String" -> paramType = "String";
case "java.lang.Integer" -> paramType = "Integer";
default -> {
case "java.lang.String": { paramType = "String"; break;}
case "java.lang.Integer": { paramType = "Integer"; break;}
default: {
error("Unimplemented parameter type: " + parameter.asType().toString() + " in method " + method.getSimpleName().toString());
return false;
}
Expand Down Expand Up @@ -203,7 +207,7 @@ private boolean processI18nAnnotations(RoundEnvironment roundEnv) {
out.print("(");

List<String> paramTypes = parameters.get(implementation.getKey()).stream()
.map(el -> el.getValue() + " " + el.getKey()).toList();
.map(el -> el.getValue() + " " + el.getKey()).collect(Collectors.toList());
out.print(String.join(", ", paramTypes));

out.println(") {");
Expand All @@ -214,9 +218,9 @@ private boolean processI18nAnnotations(RoundEnvironment roundEnv) {
for (Map.Entry<String, String> parameter : parameters.get(implementation.getKey())) {
String replaceMethod;
switch (parameter.getValue()) {
case "String" -> replaceMethod = "replaceString";
case "Integer" -> replaceMethod = "replaceInteger";
default -> {
case "String": { replaceMethod = "replaceString"; break; }
case "Integer": { replaceMethod = "replaceInteger"; break;}
default: {
error("Unimplemented parameter type: " + parameter.getValue());
return false;
}
Expand Down
9 changes: 5 additions & 4 deletions build-logic/src/main/groovy/craftinghomes.java.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,21 @@ tasks {
dependencies {
compileOnly 'org.projectlombok:lombok:1.18.28'
annotationProcessor 'org.projectlombok:lombok:1.18.28'
testAnnotationProcessor 'org.projectlombok:lombok:1.18.28'
}

def targetJavaVersion = findProperty('javaVersion') as Integer
def targetJavaVersion = "1.8"
java {
def javaVersion = JavaVersion.toVersion(targetJavaVersion)
sourceCompatibility = javaVersion
targetCompatibility = javaVersion
if (JavaVersion.current() < javaVersion) {
toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion)
toolchain.languageVersion = JavaLanguageVersion.of(11)
}
}

tasks.withType(JavaCompile).configureEach {
if (targetJavaVersion >= 10 || JavaVersion.current().isJava10Compatible()) {
options.release = targetJavaVersion
if (JavaVersion.current().isJava10Compatible()) {
options.release = 8
}
}
37 changes: 32 additions & 5 deletions bukkit/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ repositories {
}

dependencies {
compileOnly("io.papermc.paper:paper-api:1.20.1-R0.1-SNAPSHOT")
compileOnly("com.destroystokyo.paper:paper-api:1.15.2-R0.1-SNAPSHOT")
compileOnly 'org.jetbrains:annotations:24.0.1'

craftinghomesShadow(project(":craftinghomes"))
craftinghomesShadow('org.apache.commons:commons-io:1.3.2')
shadow(project(":common"))
}

Expand All @@ -51,8 +53,33 @@ tasks.register('buildCraftinghomes', com.github.jengelman.gradle.plugins.shadow.
configurations = [project.configurations.craftinghomesShadow, project.configurations.shadow]
}

runServer {
dependsOn("buildCraftinghomes")
minecraftVersion(findProperty('runPaperMinecraftVersion') as String)
pluginJars = files("$rootDir/build/CraftingHomes-bukkit-v${project.version}-1.8.8-${findProperty('latestMinecraftVersion') as String}.jar")
// https://papermc.io/downloads/all
def supportedVersions = ["1.20.2", "1.20.1", "1.20",
"1.19.4", "1.19.3", "1.19.2", "1.19.1", "1.19",
"1.18.2", "1.18.1", "1.18",
"1.17.1", "1.17",
"1.16.5", "1.16.4", "1.16.3", "1.16.2", "1.16.1",
"1.15.2", // "1.15.1", "1.15", not supported here, because nogui argument is not existing there
"1.14.4", "1.14.3", "1.14.2", "1.14.1", "1.14",
"1.13.2", "1.13", "1.13.1", "1.13-pre7",
"1.12.2", "1.12.1", "1.12",
"1.11.2",
"1.10.2",
"1.9.4",
"1.8.8"]

supportedVersions.forEach { version ->
tasks.register("runServer-$version", xyz.jpenilla.runpaper.task.RunServer) {
dependsOn("buildCraftinghomes")
minecraftVersion(version)
if (version.startsWith("1.13") || version.startsWith("1.14")) {
javaLauncher.set(javaToolchains.launcherFor {
languageVersion.set(JavaLanguageVersion.of(11))
})
}
runDirectory.set(file("run/$version"))
jvmArgs("-DPaper.IgnoreJavaVersion=true", "-Dcom.mojang.eula.agree=true")
group = "run paper"
pluginJars = files("$rootDir/build/CraftingHomes-bukkit-v${project.version}-1.8.8-${findProperty('latestMinecraftVersion') as String}.jar")
}
}
16 changes: 6 additions & 10 deletions bukkit/src/main/java/de/craftery/craftinghomes/BukkitPlatform.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
import de.craftery.craftinghomes.common.gui.GuiClickCallback;
import de.craftery.craftinghomes.common.gui.GuiItem;
import de.craftery.craftinghomes.helper.CommandStub;

import de.craftery.craftinghomes.impl.ConfigrationImpl;
import de.craftery.craftinghomes.impl.OfflinePlayerImpl;

import org.bukkit.*;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.PluginCommand;
Expand All @@ -21,28 +21,25 @@
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataType;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.Nullable;

import java.util.*;
import java.util.logging.Level;
import java.util.stream.Collectors;

public final class BukkitPlatform extends JavaPlugin implements ServerEntry {
private static BukkitPlatform instance;

public static NamespacedKey GUI_ITEM_KEY;

private final List<String> commands = new ArrayList<>();

public final Map<String, GuiClickCallback> guiClickCallbacks = new HashMap<>();
public final Map<ItemStack, GuiClickCallback> guiClickCallbacks = new HashMap<>();
public final Set<String> protectedWindowTitles = new HashSet<>();

@Override
public void onEnable() {
instance = this;
Platform.onEnable(this);
GUI_ITEM_KEY = new NamespacedKey(this, "gui_item");
this.getServer().getPluginManager().registerEvents(new InventoryProtector(), this);
this.saveDefaultConfig();
}
Expand Down Expand Up @@ -111,11 +108,10 @@ public void openGui(PlayerI player, GuiBuilder builder) {
//TODO: Use player heads for this

ItemMeta meta = invItem.getItemMeta();
meta.setLore(item.getValue().lores().stream().map(s -> ChatColor.translateAlternateColorCodes('&', s)).toList());
meta.setDisplayName(ChatColor.translateAlternateColorCodes('&', item.getValue().name()));
meta.getPersistentDataContainer().set(GUI_ITEM_KEY, PersistentDataType.STRING, item.getValue().identifier());
meta.setLore(item.getValue().getLores().stream().map(s -> ChatColor.translateAlternateColorCodes('&', s)).collect(Collectors.toList()));
meta.setDisplayName(ChatColor.translateAlternateColorCodes('&', item.getValue().getName()));
invItem.setItemMeta(meta);
this.guiClickCallbacks.put(item.getValue().identifier(), item.getValue().callback());
this.guiClickCallbacks.put(invItem, item.getValue().getCallback());

inv.setItem(item.getKey(), invItem);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.persistence.PersistentDataType;

public class InventoryProtector implements Listener {
@EventHandler
Expand All @@ -17,12 +16,9 @@ public void onInventoryClick(InventoryClickEvent event) {
if (clickedItem == null) {
return;
}
String key = clickedItem.getItemMeta().getPersistentDataContainer().get(BukkitPlatform.GUI_ITEM_KEY, PersistentDataType.STRING);
if (key == null) {
return;
}
GuiClickCallback callback = BukkitPlatform.getInstance().guiClickCallbacks.get(key);
callback.onClick();

GuiClickCallback callback = BukkitPlatform.getInstance().guiClickCallbacks.get(clickedItem);
if (callback != null) callback.onClick();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ public void set(String path, Object value) {
this.config.set(path, value);
}

@Override
public void delete(String path) {
this.config.set(path, null);
}

@Override
public Integer getInt(String path, int def) {
return this.config.getInt(path, def);
Expand All @@ -57,15 +62,20 @@ public String getString(String path, String def) {
}

@Override
public double getDouble(String path) {
public Double getDouble(String path) {
return this.config.getDouble(path);
}

@Override
public long getLong(String path) {
public Long getLong(String path) {
return this.config.getLong(path);
}

@Override
public Float getFloat(String path) {
return (float) this.config.getDouble(path);
}

@Override
public boolean exists(String path) {
return this.config.get(path) != null;
Expand Down
Loading

0 comments on commit 3a3f5bb

Please sign in to comment.