Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Paper plugin #219

Merged
merged 23 commits into from
Nov 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
ceff88e
Initial work for paper-plugin
Siroshun09 Mar 27, 2023
30e41d7
More work for paper-plugin
Siroshun09 Jun 7, 2023
94b206b
chore(bundle): improve description
Siroshun09 Jun 7, 2023
f4c358a
More more more work for paper-plugin
Siroshun09 Jun 28, 2023
0f42882
Even more work
Siroshun09 Oct 16, 2023
a68bafb
refactor: rewrite storage migrator
Siroshun09 Nov 6, 2023
d10a901
Even more work (Item Loading)
Siroshun09 Nov 23, 2023
fb8ea04
build: update paper-api to 1.20.2
Siroshun09 Nov 27, 2023
9afc023
feat(bundle,version): add renamer for short_grass in 1.20.3+
Siroshun09 Nov 27, 2023
6a43e33
feat(bundle,version): rename potion items
Siroshun09 Nov 27, 2023
c8c1e6a
feat(versions): rename old spigot potion type to current minecraft names
Siroshun09 Nov 28, 2023
b110ba0
feat(api): expose ItemNameGenerator
Siroshun09 Nov 28, 2023
530861e
refactor(version-common): use ItemNameGenerator
Siroshun09 Nov 28, 2023
30b52c7
clean(version-common): remove ItemProviderBase
Siroshun09 Nov 28, 2023
8542cfb
test(api): add tests for ItemNameGenerator
Siroshun09 Nov 28, 2023
2068b89
fix(bundle): set api-version to 1.19 and add folia supported
Siroshun09 Nov 28, 2023
4f97232
fix(bundle,storage): startup fixes
Siroshun09 Nov 29, 2023
71aa813
build(bundle): replace project version in paper-plugin.yml
Siroshun09 Nov 29, 2023
ed993b9
clean(bundle): remove plugin.yml
Siroshun09 Nov 29, 2023
d072e6e
feat(bundle,core,version-common): implements command registration
Siroshun09 Nov 29, 2023
f4e111d
improve(api,bundle,version-common): cleanup MCDataVersion usages
Siroshun09 Nov 29, 2023
f0327e2
clean(bundle,version-common): move FoliaSchedulerWrapper
Siroshun09 Nov 30, 2023
2049fca
Cleanup before merging
Siroshun09 Nov 30, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/okocraft/Box/gradle.yml?branch=main)
![GitHub](https://img.shields.io/github/license/okocraft/Box)

Box is a plugin for Paper that allows users to store any items in a virtual inventory.
A Paper plugin to provide virtual containers that can store 2.1 billion items per item.

<!--- TODO English
## 特徴
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
import org.bukkit.entity.Entity;
import org.jetbrains.annotations.NotNull;

import java.time.Duration;
import java.util.concurrent.TimeUnit;
import java.util.function.BooleanSupplier;

/**
* An interface to schedule tasks.
* <p>
Expand All @@ -25,4 +29,8 @@ public interface BoxScheduler {
*/
void runEntityTask(@NotNull Entity entity, @NotNull Runnable task);

void scheduleAsyncTask(@NotNull Runnable task, long delay, @NotNull TimeUnit unit);

void scheduleRepeatingAsyncTask(@NotNull Runnable task, @NotNull Duration interval, @NotNull BooleanSupplier condition);

}
132 changes: 132 additions & 0 deletions api/src/main/java/net/okocraft/box/api/util/ItemNameGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package net.okocraft.box.api.util;

import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.Keyed;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.VisibleForTesting;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HexFormat;
import java.util.Locale;
import java.util.Objects;

/**
* A utility class for generating item names.
*/
@ApiStatus.Experimental
public final class ItemNameGenerator {

/**
* Creates the name from the {@link Key}.
* <p>
* If {@link Key#namespace()} is {@link Key#MINECRAFT_NAMESPACE}, this method returns only uppercase {@link Key#value()}.
* Otherwise, returns the name in the format {@code NAMESPACE_VALUE}.
*
* @param key the {@link Key} to create the name
* @return the created name
*/
public static @NotNull String key(@NotNull Key key) {
if (key.namespace().equals(Key.MINECRAFT_NAMESPACE)) {
return key.value().toUpperCase(Locale.ENGLISH);
} else {
return key.namespace().toUpperCase(Locale.ENGLISH) + "_" + key.value().toUpperCase(Locale.ENGLISH);
}
}

/**
* Creates the name from the {@link Keyed}.
*
* @param key the {@link Keyed} to create the name
* @return the created name
* @see #key(Key)
*/
public static @NotNull String key(@NotNull Keyed key) {
return key(key.key());
}

/**
* Creates the name from the {@link Keyed}s.
* <p>
* This method returns the name of two {@link #key(Keyed)} results joined by {@code _}.
*
* @param key1 the first {@link Keyed} to create the name
* @param key2 the second {@link Keyed} to create the name
* @return the created name
* @see #key(Key)
*/
public static @NotNull String keys(@NotNull Keyed key1, @NotNull Keyed key2) {
return key(key1) + "_" + key(key2);
}

/**
* Creates the name from the {@link Keyed}s.
* <p>
* This method returns the name of {@link #key(Keyed)} results joined by {@code _}.
*
* @param key1 the first {@link Keyed} to create the name
* @param key2 the second {@link Keyed} to create the name
* @param keys the other {@link Keyed}s to create the name
* @return the created name
* @see #key(Key)
*/
public static @NotNull String keys(@NotNull Keyed key1, @NotNull Keyed key2, @NotNull Keyed @NotNull ... keys) {
if (keys.length == 0) {
return keys(key1, key2);
}

StringBuilder builder = new StringBuilder().append(key(key1)).append('_').append(key(key2));

for (Keyed key : keys) {
builder.append('_').append(key(key));
}

return builder.toString();
}

/**
* Creates the name from the {@link ItemStack}.
* <p>
* This method passes {@link ItemStack#getType()} and {@link ItemStack#serializeAsBytes()} to {@link #itemStack(Keyed, byte[])}.
*
* @param itemStack the {@link ItemStack} to create the name
* @return the created name
* @see #itemStack(Keyed, byte[])
*/
public static @NotNull String itemStack(@NotNull ItemStack itemStack) {
return itemStack(itemStack.getType(), itemStack.serializeAsBytes());
}

/**
* Creates the name from the {@link ItemStack}.
* <p>
* This method returns the name in the format {@code key_sha1}.
*
* @param key the {@link Keyed} to create the name
* @param bytes the bytes to create the hash value by SHA-1
* @return the created name
*/
public static @NotNull String itemStack(@NotNull Keyed key, byte @NotNull [] bytes) {
return key(key) + "_" + sha1(bytes);
}

@VisibleForTesting
static @NotNull String sha1(byte @NotNull [] bytes) {
Objects.requireNonNull(bytes);
return HexFormat.of().withLowerCase().formatHex(sha1().digest(bytes)).substring(0, 8);
}

private static @NotNull MessageDigest sha1() {
try {
return MessageDigest.getInstance("SHA-1");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}

private ItemNameGenerator() {
throw new UnsupportedOperationException();
}
}
10 changes: 10 additions & 0 deletions api/src/main/java/net/okocraft/box/api/util/MCDataVersion.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,16 @@ public record MCDataVersion(int dataVersion) {
*/
public static final MCDataVersion MC_1_20_2 = new MCDataVersion(3578);

/**
* A {@link MCDataVersion} that represents Minecraft 1.20.3
*/
public static final MCDataVersion MC_1_20_3 = new MCDataVersion(3695); // 1.20.3-pre3

/**
* A {@link MCDataVersion} that represents Minecraft 1.21
*/
public static final MCDataVersion MC_1_21 = new MCDataVersion(9999); // Unknown

/**
* Creates a {@link MCDataVersion} from the specified data version
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package net.okocraft.box.api.util;

import net.kyori.adventure.key.Key;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

class ItemNameGeneratorTest {

private static final Key MINECRAFT_KEY_1 = Key.key(Key.MINECRAFT_NAMESPACE, "key1");
private static final Key MINECRAFT_KEY_2 = Key.key(Key.MINECRAFT_NAMESPACE, "key2");
private static final Key CUSTOM_KEY = Key.key("custom", "key");
private static final byte[] BYTES = new byte[]{0, 1, 2, 3, 4};
private static final String SHA_1 = ItemNameGenerator.sha1(BYTES);

@Test
void testKey() {
Assertions.assertEquals("KEY1", ItemNameGenerator.key(MINECRAFT_KEY_1));
Assertions.assertEquals("CUSTOM_KEY", ItemNameGenerator.key(CUSTOM_KEY));
}

@Test
void testKeys() {
Assertions.assertEquals("KEY1_KEY2", ItemNameGenerator.keys(MINECRAFT_KEY_1, MINECRAFT_KEY_2));
Assertions.assertEquals("KEY1_CUSTOM_KEY", ItemNameGenerator.keys(MINECRAFT_KEY_1, CUSTOM_KEY));
Assertions.assertEquals("CUSTOM_KEY_KEY1", ItemNameGenerator.keys(CUSTOM_KEY, MINECRAFT_KEY_1));
Assertions.assertEquals("KEY1_KEY2_CUSTOM_KEY", ItemNameGenerator.keys(MINECRAFT_KEY_1, MINECRAFT_KEY_2, CUSTOM_KEY));
}

@Test
void testItemStack() {
Assertions.assertEquals("KEY1_" + SHA_1, ItemNameGenerator.itemStack(MINECRAFT_KEY_1, BYTES));
}
}
2 changes: 1 addition & 1 deletion build-logic/src/main/kotlin/box.publication.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ publishing {

pom {
name.set(project.name)
description.set("A Paper plugin that provide virtual chests.")
description.set("A Paper plugin to provide virtual containers that can store 2.1 billion items per item.")
url.set("https://github.com/okocraft/Box")

licenses {
Expand Down
4 changes: 3 additions & 1 deletion bundle/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ dependencies {
rootProject.childProjects.values
.filterNot { project -> project.name == "box-bundle" }
.forEach { project -> implementation(project) }

implementation(libs.translationloader)
}

tasks {
Expand All @@ -15,7 +17,7 @@ tasks {
}

processResources {
filesMatching(listOf("plugin.yml", "en.yml", "ja_JP.yml")) {
filesMatching(listOf("paper-plugin.yml", "plugin.yml", "en.yml", "ja_JP.yml")) {
expand("projectVersion" to project.version)
}
}
Expand Down
51 changes: 51 additions & 0 deletions bundle/src/main/java/net/okocraft/box/bootstrap/BoxBootstrap.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package net.okocraft.box.bootstrap;

import io.papermc.paper.plugin.bootstrap.BootstrapContext;
import io.papermc.paper.plugin.bootstrap.PluginBootstrap;
import io.papermc.paper.plugin.bootstrap.PluginProviderContext;
import net.okocraft.box.bundle.BuiltinFeatures;
import net.okocraft.box.bundle.BuiltinStorages;
import net.okocraft.box.bundle.BuiltinTranslations;
import net.okocraft.box.plugin.BoxPlugin;
import org.bukkit.plugin.java.JavaPlugin;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.jetbrains.annotations.NotNull;

@SuppressWarnings({"UnstableApiUsage", "unused"})
public final class BoxBootstrap implements PluginBootstrap {

private static BoxBootstrap instance = null;

public static @NotNull BoxBootstrap get() { // This method exists for external access.
if (instance == null) {
throw new IllegalStateException("There is no active BoxBootstrap.");
}

return instance;
}

@MonotonicNonNull
private BoxBootstrapContext boxBootstrapContext;

@Override
public void bootstrap(@NotNull BootstrapContext context) {
BoxBootstrap.instance = this;
boxBootstrapContext = BoxBootstrapContext.create(context);

BuiltinFeatures.addToContext(boxBootstrapContext);
BuiltinStorages.addToRegistry(boxBootstrapContext.getStorageRegistry());

boxBootstrapContext.onLanguageDirectoryCreated().add(directory -> BuiltinTranslations.saveDefaultTranslationFiles(boxBootstrapContext.getJarFile(), directory));
boxBootstrapContext.getTranslationLoaderCreators().addCreator(locale -> BuiltinTranslations.loadDefaultTranslation(boxBootstrapContext.getJarFile(), locale));
}

@Override
public @NotNull JavaPlugin createPlugin(@NotNull PluginProviderContext context) { // PluginProviderContext is immutable, so this argument can be ignored.
BoxBootstrap.instance = null;
return new BoxPlugin(boxBootstrapContext);
}

public @NotNull BoxBootstrapContext getBootstrapContext() { // This method exists for external access.
return boxBootstrapContext;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package net.okocraft.box.bootstrap;

import com.github.siroshun09.event4j.bus.EventBus;
import io.papermc.paper.plugin.bootstrap.BootstrapContext;
import net.kyori.adventure.text.logger.slf4j.ComponentLogger;
import net.okocraft.box.api.event.BoxEvent;
import net.okocraft.box.api.feature.BoxFeature;
import net.okocraft.box.storage.api.registry.StorageRegistry;
import net.okocraft.box.util.TranslationDirectoryUtil;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;

import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;

public final class BoxBootstrapContext {

@Contract("_ -> new")
@SuppressWarnings("UnstableApiUsage")
public static @NotNull BoxBootstrapContext create(@NotNull BootstrapContext context) {
return new BoxBootstrapContext(
context.getDataDirectory(),
context.getLogger(),
context.getPluginSource(),
context.getConfiguration().getVersion()
);
}

private final Path dataDirectory;
private final ComponentLogger logger;
private final Path jarFile;
private final String version;
private final StorageRegistry storageRegistry;
private final EventBus<BoxEvent> eventBus;
private final List<Supplier<? extends BoxFeature>> boxFeatureList = new ArrayList<>();
private final TranslationDirectoryUtil.PathConsumerWrapper onLanguageDirectoryCreated;
private final TranslationDirectoryUtil.TranslationLoaderCreatorHolder translationLoaderCreators;

private BoxBootstrapContext(@NotNull Path pluginDirectory, @NotNull ComponentLogger logger, @NotNull Path jarFile, @NotNull String version) {
this.dataDirectory = pluginDirectory;
this.logger = logger;
this.jarFile = jarFile;
this.version = version;
this.storageRegistry = new StorageRegistry();
this.eventBus = EventBus.create(BoxEvent.class);
this.onLanguageDirectoryCreated = TranslationDirectoryUtil.createPathConsumer();
this.translationLoaderCreators = TranslationDirectoryUtil.createCreatorHolder();
}

public @NotNull Path getPluginDirectory() {
return dataDirectory;
}

public @NotNull ComponentLogger getLogger() {
return logger;
}

public @NotNull Path getJarFile() {
return jarFile;
}

public @NotNull String getVersion() {
return version;
}

public @NotNull StorageRegistry getStorageRegistry() {
return storageRegistry;
}

public @NotNull EventBus<BoxEvent> getEventBus() {
return eventBus;
}

public @NotNull List<Supplier<? extends BoxFeature>> getBoxFeatureList() {
return boxFeatureList;
}

@Contract("_ -> this")
public @NotNull BoxBootstrapContext addFeature(@NotNull Supplier<? extends BoxFeature> featureSupplier) {
boxFeatureList.add(featureSupplier);
return this;
}

public @NotNull TranslationDirectoryUtil.PathConsumerWrapper onLanguageDirectoryCreated() {
return onLanguageDirectoryCreated;
}

public @NotNull TranslationDirectoryUtil.TranslationLoaderCreatorHolder getTranslationLoaderCreators() {
return translationLoaderCreators;
}
}
Loading