diff --git a/.gitea/workflows/test.yml b/.gitea/workflows/test.yml
index 91ff768..bcd8f0a 100644
--- a/.gitea/workflows/test.yml
+++ b/.gitea/workflows/test.yml
@@ -10,14 +10,16 @@ on:
pull_request_target:
branches: [ master, dev ]
+env:
+ REPO_USERNAME: Altarik
+ REPO_PASSWORD: ${{ secrets.REPO_PASSWORD }}
jobs:
build:
strategy:
matrix:
java: [ '17' ]
- # and run on both Linux and Windows
- os: [ubuntu-latest, windows-2022]
+ os: [ ubuntu-latest ]
runs-on: ${{ matrix.os }}
steps:
- name: checkout repository
@@ -31,17 +33,19 @@ jobs:
distribution: 'oracle'
- name: make gradle wrapper executable
if: ${{ runner.os != 'Windows' }}
- run: chmod +x ./gradlew
+ run: |
+ chmod +x ./gradlew
+ touch local.properties
+ echo $REPO_USERNAME
- name: build
- run: ./gradlew build --no-daemon
- - name: test
- run: ./gradlew test --no-daemon
+ run: ./gradlew build --no-daemon --max-workers 1
+ #- name: test
+ # run: ./gradlew test --no-daemon
deploy:
runs-on: ${{ ubuntu-latest }}
if: gitea.ref == 'refs/heads/master'
+ needs: build
steps:
- name: deploy
- uses: actions/upload-artifact@v3
- with:
- name: Artifacts
- path: build/libs/
+ run: ./gradlew publish
+
diff --git a/.gitignore b/.gitignore
index d037336..71baab6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,9 +3,11 @@ build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
+local.properties
-*/run
-*/logs
+run/*
+**/run
+**/logs
### IntelliJ IDEA ###
.idea
diff --git a/Core/src/main/java/fr/altarik/toolbox/core/builder/EmptyCollectionException.java b/Core/src/main/java/fr/altarik/toolbox/core/builder/EmptyCollectionException.java
new file mode 100644
index 0000000..798e6c3
--- /dev/null
+++ b/Core/src/main/java/fr/altarik/toolbox/core/builder/EmptyCollectionException.java
@@ -0,0 +1,8 @@
+package fr.altarik.toolbox.core.builder;
+
+public class EmptyCollectionException extends NullPointerException {
+
+ public EmptyCollectionException(String message) {
+ super(message);
+ }
+}
diff --git a/Core/src/main/java/fr/altarik/toolbox/core/builder/IBuilder.java b/Core/src/main/java/fr/altarik/toolbox/core/builder/IBuilder.java
new file mode 100644
index 0000000..64324a9
--- /dev/null
+++ b/Core/src/main/java/fr/altarik/toolbox/core/builder/IBuilder.java
@@ -0,0 +1,11 @@
+package fr.altarik.toolbox.core.builder;
+
+public interface IBuilder {
+
+ /**
+ * Build the builders parameters into T object
+ * @return The created objects thanks to given parameters
+ * @throws Exception if any error occur during creation of the built object
+ */
+ T build() throws Exception;
+}
diff --git a/Core/src/main/java/fr/altarik/toolbox/core/builder/IParamBuilder.java b/Core/src/main/java/fr/altarik/toolbox/core/builder/IParamBuilder.java
new file mode 100644
index 0000000..ffa1b5a
--- /dev/null
+++ b/Core/src/main/java/fr/altarik/toolbox/core/builder/IParamBuilder.java
@@ -0,0 +1,24 @@
+package fr.altarik.toolbox.core.builder;
+
+/**
+ * Builder parameter, for more flexibility
+ * @param the parameter type
+ * @see OptionalParamBuilder
+ * @see RequiredParamBuilder
+ * @see RequiredCollectionParameterBuilder
+ */
+public interface IParamBuilder {
+
+ /**
+ * Get the given object, may return {@link NullPointerException} depending on the policy of implemented class
+ * @return the parameter given by {@link IParamBuilder#set(Object)}
+ * @throws NullPointerException may throw this error depending on the policy of implemented class
+ */
+ T get() throws NullPointerException;
+
+ /**
+ * Change/insert the value of the parameter
+ * @param parameter the given parameter
+ */
+ void set(T parameter);
+}
diff --git a/Core/src/main/java/fr/altarik/toolbox/core/builder/OptionalParamBuilder.java b/Core/src/main/java/fr/altarik/toolbox/core/builder/OptionalParamBuilder.java
new file mode 100644
index 0000000..fa60979
--- /dev/null
+++ b/Core/src/main/java/fr/altarik/toolbox/core/builder/OptionalParamBuilder.java
@@ -0,0 +1,24 @@
+package fr.altarik.toolbox.core.builder;
+
+/**
+ * Doesn't throw a {@link NullPointerException} when using {@link IParamBuilder#get()} in any case
+ * @param the returned type
+ * @see IParamBuilder
+ */
+public class OptionalParamBuilder implements IParamBuilder {
+
+ private T parameter;
+
+ public OptionalParamBuilder(T param) {
+ this.parameter = param;
+ }
+ @Override
+ public T get() throws NullPointerException {
+ return parameter;
+ }
+
+ @Override
+ public void set(T parameter) {
+ this.parameter = parameter;
+ }
+}
diff --git a/Core/src/main/java/fr/altarik/toolbox/core/builder/RequiredCollectionParameterBuilder.java b/Core/src/main/java/fr/altarik/toolbox/core/builder/RequiredCollectionParameterBuilder.java
new file mode 100644
index 0000000..8a828bd
--- /dev/null
+++ b/Core/src/main/java/fr/altarik/toolbox/core/builder/RequiredCollectionParameterBuilder.java
@@ -0,0 +1,52 @@
+package fr.altarik.toolbox.core.builder;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collection;
+import java.util.Objects;
+
+/**
+ * Mostly same as {@link RequiredParamBuilder} but for list
+ * @param The type contained in the collection
+ * @param The returned type
+ */
+public class RequiredCollectionParameterBuilder> implements IParamBuilder {
+
+ private final T collection;
+ private final boolean canBeEmpty;
+
+ public RequiredCollectionParameterBuilder(@NotNull T collection, boolean canBeEmpty) {
+ this.collection = Objects.requireNonNull(collection);
+ this.canBeEmpty = canBeEmpty;
+ }
+
+ /**
+ * Return the list, if not empty
+ * If empty, return the collection if {@code canBeEmpty} if true, otherwise throw a {@link NullPointerException}
+ * @return the collection
+ * @throws NullPointerException if collection is empty and {@code canBeEmpty} is false
+ */
+ @Override
+ public T get() throws NullPointerException {
+ if(canBeEmpty) {
+ return collection;
+ } else if(!collection.isEmpty()) {
+ return collection;
+ } else {
+ throw new EmptyCollectionException("Collection cannot be empty");
+ }
+ }
+
+ @Override
+ public void set(T parameter) {
+ throw new UnsupportedOperationException("Use `add` in place of `set` for RequiredCollectionParameterBuilder");
+ }
+
+ /**
+ * Add an element to the collection
+ * @param element element to add to the list
+ */
+ public void add(E element) {
+ collection.add(element);
+ }
+}
diff --git a/Core/src/main/java/fr/altarik/toolbox/core/builder/RequiredParamBuilder.java b/Core/src/main/java/fr/altarik/toolbox/core/builder/RequiredParamBuilder.java
new file mode 100644
index 0000000..7d45fdc
--- /dev/null
+++ b/Core/src/main/java/fr/altarik/toolbox/core/builder/RequiredParamBuilder.java
@@ -0,0 +1,30 @@
+package fr.altarik.toolbox.core.builder;
+
+import java.util.Objects;
+
+/**
+ * Throw a {@link NullPointerException} when using {@link IParamBuilder#get()} if the parameter doesn't have been initialized
+ * @param the returned type
+ */
+public class RequiredParamBuilder implements IParamBuilder {
+
+ private T parameter;
+
+ public RequiredParamBuilder(T parameter) {
+ this.parameter = parameter;
+ }
+
+ public RequiredParamBuilder() {
+ this(null);
+ }
+
+ @Override
+ public T get() {
+ return Objects.requireNonNull(parameter);
+ }
+
+ @Override
+ public void set(T parameter) {
+ this.parameter = parameter;
+ }
+}
diff --git a/Core/src/main/java/fr/altarik/toolbox/core/data/DataTracker.java b/Core/src/main/java/fr/altarik/toolbox/core/data/DataTracker.java
new file mode 100644
index 0000000..aa9bcb9
--- /dev/null
+++ b/Core/src/main/java/fr/altarik/toolbox/core/data/DataTracker.java
@@ -0,0 +1,37 @@
+package fr.altarik.toolbox.core.data;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+public class DataTracker {
+
+ private final Map trackedData;
+
+ public DataTracker() {
+ this.trackedData = new HashMap<>();
+ }
+
+ public void startTracking(TrackedData data) {
+ trackedData.put(data, data.defaultValue());
+ }
+
+ public String getOrDefault(TrackedData data) {
+ return Objects.requireNonNull(trackedData.get(data));
+ }
+
+ public void set(TrackedData data, String value) {
+ String v = trackedData.get(data);
+ if(v != null) {
+ trackedData.putIfAbsent(data, value);
+ } else {
+ throw new IllegalArgumentException("Data " + data.name() + " is not tracked, please initialize it with DataTracker#startTracking(TrackedData, String) first");
+ }
+
+ }
+
+ public void saveToDb() {
+
+ }
+
+}
diff --git a/Core/src/main/java/fr/altarik/toolbox/core/data/TrackedData.java b/Core/src/main/java/fr/altarik/toolbox/core/data/TrackedData.java
new file mode 100644
index 0000000..c8bec33
--- /dev/null
+++ b/Core/src/main/java/fr/altarik/toolbox/core/data/TrackedData.java
@@ -0,0 +1,5 @@
+package fr.altarik.toolbox.core.data;
+
+public record TrackedData(String name, String defaultValue) {
+
+}
diff --git a/Core/src/main/resources/assets/core/icon.png b/Core/src/main/resources/assets/core/icon.png
new file mode 100644
index 0000000..1049a98
Binary files /dev/null and b/Core/src/main/resources/assets/core/icon.png differ
diff --git a/Core/src/main/resources/fabric.mod.json b/Core/src/main/resources/fabric.mod.json
new file mode 100644
index 0000000..65236f7
--- /dev/null
+++ b/Core/src/main/resources/fabric.mod.json
@@ -0,0 +1,29 @@
+{
+ "schemaVersion": 1,
+ "id": "toolbox-core",
+ "version": "${version}",
+ "name": "Core",
+ "description": "",
+ "authors": [
+ "Altarik"
+ ],
+ "contributors": [
+ "Legot Quentin"
+ ],
+ "contact": {
+ "homepage": "https://altarik.fr"
+ },
+ "license": "Altarik @ All-Rights-Reserved ",
+ "icon": "assets/core/icon.png",
+ "environment": "*",
+ "entrypoints": {
+ "main": []
+ },
+ "mixins": [],
+ "depends": {
+ "fabricloader": "^0.14.12",
+ "fabric-api": "*",
+ "minecraft": "1.19.3",
+ "java": ">=17"
+ }
+}
diff --git a/Core/src/test/java/BuilderImpl.java b/Core/src/test/java/BuilderImpl.java
new file mode 100644
index 0000000..a5a8850
--- /dev/null
+++ b/Core/src/test/java/BuilderImpl.java
@@ -0,0 +1,38 @@
+import fr.altarik.toolbox.core.builder.IBuilder;
+import fr.altarik.toolbox.core.builder.RequiredCollectionParameterBuilder;
+import fr.altarik.toolbox.core.builder.RequiredParamBuilder;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class BuilderImpl implements IBuilder {
+
+ private final RequiredCollectionParameterBuilder> collection;
+ private final RequiredParamBuilder numberOfSentences;
+
+ private BuilderImpl(boolean canBeEmpty) {
+ this.collection = new RequiredCollectionParameterBuilder<>(new ArrayList<>(), canBeEmpty);
+ this.numberOfSentences = new RequiredParamBuilder<>();
+ }
+
+ public BuilderImpl addSentence(String sentence) {
+ collection.add(sentence);
+ return this;
+ }
+
+ public BuilderImpl numberOfSentence(int i) {
+ this.numberOfSentences.set(i);
+ return this;
+ }
+
+ public static BuilderImpl builder(boolean canBeEmpty) {
+ return new BuilderImpl(canBeEmpty);
+ }
+
+
+
+ @Override
+ public BuilderResult build() throws Exception {
+ return new BuilderResult(collection.get(), numberOfSentences.get());
+ }
+}
diff --git a/Core/src/test/java/BuilderResult.java b/Core/src/test/java/BuilderResult.java
new file mode 100644
index 0000000..3952469
--- /dev/null
+++ b/Core/src/test/java/BuilderResult.java
@@ -0,0 +1,5 @@
+import java.util.List;
+
+public record BuilderResult(List sentences, int numberOfSentences) {
+
+}
diff --git a/Core/src/test/java/BuilderTest.java b/Core/src/test/java/BuilderTest.java
new file mode 100644
index 0000000..148efe2
--- /dev/null
+++ b/Core/src/test/java/BuilderTest.java
@@ -0,0 +1,32 @@
+import fr.altarik.toolbox.core.builder.EmptyCollectionException;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+
+public class BuilderTest {
+
+
+ @Test
+ void builderTest() throws Exception {
+ BuilderImpl builder = BuilderImpl.builder(true);
+ builder.addSentence("First sentence");
+ builder.addSentence("Second sentence");
+ builder.numberOfSentence(2);
+ BuilderResult res = builder.build();
+ Assertions.assertEquals(Arrays.asList("First sentence", "Second sentence"), res.sentences());
+ Assertions.assertEquals(res.numberOfSentences(), 2);
+
+ BuilderImpl builder1 = BuilderImpl.builder(false);
+ builder1.numberOfSentence(3);
+ Assertions.assertThrowsExactly(EmptyCollectionException.class, builder1::build);
+
+ BuilderImpl builder2 = BuilderImpl.builder(true);
+ builder2.numberOfSentence(3);
+ Assertions.assertDoesNotThrow(builder2::build);
+
+ BuilderImpl builder3 = BuilderImpl.builder(true);
+ Assertions.assertThrowsExactly(NullPointerException.class, builder3::build);
+ }
+
+}
diff --git a/Database/build.gradle b/Database/build.gradle
index 87ae4db..52c3f78 100644
--- a/Database/build.gradle
+++ b/Database/build.gradle
@@ -1,4 +1,9 @@
dependencies {
- implementation 'org.postgresql:postgresql:42.5.0'
+ implementation 'org.postgresql:postgresql:42.6.0'
testImplementation 'com.google.code.gson:gson:2.10'
+ implementation project(':Core')
}
+
+test {
+ exclude 'fr/altarik/toolbox/database/**' // exclude for runner
+}
\ No newline at end of file
diff --git a/Database/src/main/java/fr/altarik/toolbox/database/AbstractSqlConnection.java b/Database/src/main/java/fr/altarik/toolbox/database/AbstractSqlConnection.java
index 7fefcce..e2a4c15 100644
--- a/Database/src/main/java/fr/altarik/toolbox/database/AbstractSqlConnection.java
+++ b/Database/src/main/java/fr/altarik/toolbox/database/AbstractSqlConnection.java
@@ -25,16 +25,18 @@ public abstract class AbstractSqlConnection implements SqlConnection {
return connection;
}
+ @Override
public void closeConnection() {
try {
- if(!connection.isClosed()) {
- connection.close();
- connection = null;
- }
- } catch(SQLException ignored) {
- // no op
- }
+ close();
+ } catch (Exception ignored) {}
}
-
+ @Override
+ public void close() throws Exception {
+ if(!connection.isClosed()) {
+ connection.close();
+ connection = null;
+ }
+ }
}
diff --git a/Database/src/main/java/fr/altarik/toolbox/database/Connections.java b/Database/src/main/java/fr/altarik/toolbox/database/Connections.java
index 7d8f5a7..4f30e9b 100644
--- a/Database/src/main/java/fr/altarik/toolbox/database/Connections.java
+++ b/Database/src/main/java/fr/altarik/toolbox/database/Connections.java
@@ -1,14 +1,28 @@
package fr.altarik.toolbox.database;
+import fr.altarik.toolbox.database.keyValue.KeyValueBuilder;
+import fr.altarik.toolbox.database.keyValue.KeyValueTable;
+
import java.sql.SQLException;
public class Connections {
/**
* Create a new Connection object for a postgresql database server
- * @return
+ * @return postgresql connection
*/
public static SqlConnection newPostgresConnection(ConnectionConfig config) throws SQLException {
return new PostgresConnection(config);
}
+
+ /**
+ * Create a new (key, value) table if not exist and use it through {@link KeyValueTable} interface
+ * @param connection Postgresql connection
+ * @param tableName name of the table to use
+ * @return interface to control the table
+ * @throws SQLException if connection is lost
+ */
+ public static KeyValueTable newKeyValueTable(SqlConnection connection, String tableName) throws SQLException {
+ return KeyValueBuilder.builder().setConnection(connection).setTableName(tableName).build();
+ }
}
diff --git a/Database/src/main/java/fr/altarik/toolbox/database/PostgresConnection.java b/Database/src/main/java/fr/altarik/toolbox/database/PostgresConnection.java
index beac2bb..a20d381 100644
--- a/Database/src/main/java/fr/altarik/toolbox/database/PostgresConnection.java
+++ b/Database/src/main/java/fr/altarik/toolbox/database/PostgresConnection.java
@@ -5,7 +5,7 @@ import java.sql.SQLException;
public class PostgresConnection extends AbstractSqlConnection {
- PostgresConnection(ConnectionConfig config) throws SQLException {
+ public PostgresConnection(ConnectionConfig config) throws SQLException {
super(config);
}
diff --git a/Database/src/main/java/fr/altarik/toolbox/database/SqlConnection.java b/Database/src/main/java/fr/altarik/toolbox/database/SqlConnection.java
index 0b04fe4..a2839d7 100644
--- a/Database/src/main/java/fr/altarik/toolbox/database/SqlConnection.java
+++ b/Database/src/main/java/fr/altarik/toolbox/database/SqlConnection.java
@@ -3,14 +3,30 @@ package fr.altarik.toolbox.database;
import java.sql.Connection;
import java.sql.SQLException;
-public interface SqlConnection {
+public interface SqlConnection extends AutoCloseable {
+ /**
+ * Start the connection to sql database
+ * @throws SQLException if unable to connect to database
+ */
void connect() throws SQLException;
+ /**
+ * Get the sql connection
+ * @return the connection session
+ */
Connection getConnection();
+ /**
+ * Reconnect you to database if it has closed or lost.
+ * @throws SQLException if unable to reconnect you
+ */
void checkConnection() throws SQLException;
+ /**
+ * @deprecated replaced with {@link AutoCloseable#close()}
+ */
+ @Deprecated(forRemoval = true)
void closeConnection();
}
diff --git a/Database/src/main/java/fr/altarik/toolbox/database/keyValue/KeyValueBuilder.java b/Database/src/main/java/fr/altarik/toolbox/database/keyValue/KeyValueBuilder.java
new file mode 100644
index 0000000..b393cbb
--- /dev/null
+++ b/Database/src/main/java/fr/altarik/toolbox/database/keyValue/KeyValueBuilder.java
@@ -0,0 +1,39 @@
+package fr.altarik.toolbox.database.keyValue;
+
+import fr.altarik.toolbox.core.builder.IBuilder;
+import fr.altarik.toolbox.core.builder.RequiredParamBuilder;
+import fr.altarik.toolbox.database.SqlConnection;
+import org.jetbrains.annotations.NotNull;
+
+import java.sql.SQLException;
+
+
+public class KeyValueBuilder implements IBuilder {
+
+ private final RequiredParamBuilder tableName;
+ private final RequiredParamBuilder connection;
+
+ private KeyValueBuilder() {
+ this.tableName = new RequiredParamBuilder<>();
+ this.connection = new RequiredParamBuilder<>();
+ }
+
+ public static KeyValueBuilder builder() {
+ return new KeyValueBuilder();
+ }
+
+ public KeyValueBuilder setConnection(@NotNull SqlConnection connection) {
+ this.connection.set(connection);
+ return this;
+ }
+
+ public KeyValueBuilder setTableName(@NotNull String tableName) {
+ this.tableName.set(tableName);
+ return this;
+ }
+
+ public KeyValuePostgresql build() throws SQLException {
+ return new KeyValuePostgresql(connection.get(), tableName.get());
+ }
+
+}
diff --git a/Database/src/main/java/fr/altarik/toolbox/database/keyValue/KeyValuePostgresql.java b/Database/src/main/java/fr/altarik/toolbox/database/keyValue/KeyValuePostgresql.java
new file mode 100644
index 0000000..28a55bf
--- /dev/null
+++ b/Database/src/main/java/fr/altarik/toolbox/database/keyValue/KeyValuePostgresql.java
@@ -0,0 +1,78 @@
+package fr.altarik.toolbox.database.keyValue;
+
+import fr.altarik.toolbox.database.SqlConnection;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+public class KeyValuePostgresql implements KeyValueTable {
+
+ private final SqlConnection connection;
+ private final String tableName;
+
+ public KeyValuePostgresql(@NotNull SqlConnection connection, @NotNull String tableName) throws SQLException {
+ this.connection = connection;
+ this.tableName = tableName;
+ connection.checkConnection();
+ createTable(tableName);
+ }
+
+ private void createTable(String tableName) throws SQLException {
+ try(Statement statement = connection.getConnection().createStatement()) {
+ statement.executeUpdate("CREATE TABLE IF NOT EXISTS " + tableName + "(key VARCHAR(50) NOT NULL, value TEXT NOT NULL, PRIMARY KEY(key));");
+ }
+ }
+
+ @Override
+ public @Nullable String getValue(String key) throws SQLException {
+ connection.checkConnection();
+ try(PreparedStatement preparedStatement = connection.getConnection().prepareStatement("SELECT value FROM " + tableName + " WHERE key=?;")) {
+ preparedStatement.setString(1, key);
+ ResultSet resultSet = preparedStatement.executeQuery();
+ if(resultSet.next())
+ return resultSet.getString(1);
+ return null;
+ }
+ }
+
+ @Override
+ public void insertValue(String key, String value) throws SQLException {
+ connection.checkConnection();
+ try(PreparedStatement preparedStatement = connection.getConnection().prepareStatement("INSERT INTO " + tableName + "(key, value) VALUES (?, ?);")){
+ preparedStatement.setString(1, key);
+ preparedStatement.setString(2, value);
+ preparedStatement.executeUpdate();
+ }
+ }
+
+ @Override
+ public void updateValue(String key, String value) throws SQLException {
+ connection.checkConnection();
+ try(PreparedStatement preparedStatement = connection.getConnection().prepareStatement("UPDATE " + tableName + " SET value=? WHERE key=?;")) {
+ preparedStatement.setString(1, value);
+ preparedStatement.setString(2, key);
+ preparedStatement.executeUpdate();
+ }
+ }
+
+ @Override
+ public void deleteRow(String key) throws SQLException {
+ connection.checkConnection();
+ try(PreparedStatement preparedStatement = connection.getConnection().prepareStatement("DELETE FROM " + tableName + " WHERE key=?")) {
+ preparedStatement.setString(1, key);
+ preparedStatement.executeUpdate();
+ }
+ }
+
+ @Override
+ public void truncateTable() throws SQLException {
+ connection.checkConnection();
+ try(PreparedStatement preparedStatement = connection.getConnection().prepareStatement("TRUNCATE TABLE " + tableName)) {
+ preparedStatement.executeUpdate();
+ }
+ }
+}
diff --git a/Database/src/main/java/fr/altarik/toolbox/database/keyValue/KeyValueTable.java b/Database/src/main/java/fr/altarik/toolbox/database/keyValue/KeyValueTable.java
new file mode 100644
index 0000000..567c4be
--- /dev/null
+++ b/Database/src/main/java/fr/altarik/toolbox/database/keyValue/KeyValueTable.java
@@ -0,0 +1,54 @@
+package fr.altarik.toolbox.database.keyValue;
+
+import org.jetbrains.annotations.Nullable;
+
+import java.sql.SQLException;
+
+/**
+ * Implement of a key value table, abstract the actual representation of the table and its manipulation between this interface
+ * @see KeyValuePostgresql
+ */
+public interface KeyValueTable {
+
+ /**
+ * Return the first value associated with the unique key.
+ *
+ * @param key String key of associated to the value
+ * @return value associated with the key
+ * @throws SQLException if connection is lost
+ */
+ @Nullable String getValue(String key) throws SQLException;
+
+ /**
+ * Insert a new value in the table, associated with key
+ *
+ * @param key String key which will be associated with the value, is unique
+ * @param value String value which will be stored in the database
+ * @throws SQLException if connection is lost or if {@code key} is not unique (already exist in database)
+ */
+ void insertValue(String key, String value) throws SQLException;
+
+ /**
+ * Update value column of the row associated with the key by {@code value}
+ * If {@code key} doesn't exist in table, will update no row without warning
+ * @param key String key which will is associated with the value, is unique
+ * @param value new value
+ * @throws SQLException if connection is lost
+ */
+ void updateValue(String key, String value) throws SQLException;
+
+ /**
+ * Delete row with having {@code key} as unique key
+ * If key doesn't exist in database, will delete no row without warning
+ * @param key the key of the row to delete
+ * @throws SQLException if connection is lost
+ */
+ void deleteRow(String key) throws SQLException;
+
+ /**
+ * Will delete every data inside the table
+ * @throws SQLException if connection is lost
+ */
+ void truncateTable() throws SQLException;
+
+}
diff --git a/Database/src/main/resources/assets/database/icon.png b/Database/src/main/resources/assets/database/icon.png
new file mode 100644
index 0000000..1049a98
Binary files /dev/null and b/Database/src/main/resources/assets/database/icon.png differ
diff --git a/Database/src/main/resources/fabric.mod.json b/Database/src/main/resources/fabric.mod.json
new file mode 100644
index 0000000..6f9451d
--- /dev/null
+++ b/Database/src/main/resources/fabric.mod.json
@@ -0,0 +1,29 @@
+{
+ "schemaVersion": 1,
+ "id": "toolbox-database",
+ "version": "${version}",
+ "name": "Database",
+ "description": "",
+ "authors": [
+ "Altarik"
+ ],
+ "contributors": [
+ "Legot Quentin"
+ ],
+ "contact": {
+ "homepage": "https://altarik.fr"
+ },
+ "license": "Altarik @ All-Rights-Reserved ",
+ "icon": "assets/database/icon.png",
+ "environment": "*",
+ "entrypoints": {
+ "main": []
+ },
+ "mixins": [],
+ "depends": {
+ "fabricloader": "^0.14.12",
+ "fabric-api": "*",
+ "minecraft": "1.19.3",
+ "java": ">=17"
+ }
+}
diff --git a/Database/src/test/java/fr/altarik/toolbox/database/ConnectionTest.java b/Database/src/test/java/fr/altarik/toolbox/database/ConnectionTest.java
index 955151d..94759a5 100644
--- a/Database/src/test/java/fr/altarik/toolbox/database/ConnectionTest.java
+++ b/Database/src/test/java/fr/altarik/toolbox/database/ConnectionTest.java
@@ -30,6 +30,7 @@ class ConnectionTest {
try(PreparedStatement statement = connection.getConnection().prepareStatement("CREATE TABLE IF NOT EXISTS toolbox(id SERIAL, PRIMARY KEY (id));")) {
statement.executeUpdate();
}
+ connection.close();
});
}
diff --git a/Database/src/test/java/fr/altarik/toolbox/database/keyValue/KeyValueTest.java b/Database/src/test/java/fr/altarik/toolbox/database/keyValue/KeyValueTest.java
new file mode 100644
index 0000000..78990a6
--- /dev/null
+++ b/Database/src/test/java/fr/altarik/toolbox/database/keyValue/KeyValueTest.java
@@ -0,0 +1,68 @@
+package fr.altarik.toolbox.database.keyValue;
+
+import com.google.gson.Gson;
+import fr.altarik.toolbox.database.ConnectionConfig;
+import fr.altarik.toolbox.database.Connections;
+import fr.altarik.toolbox.database.SqlConnection;
+import org.junit.jupiter.api.Test;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class KeyValueTest {
+
+ @Test
+ void tableTest() {
+ System.out.println("Hello");
+ assertDoesNotThrow(() -> {InputStream configInput = getResource("config.yml");
+ String configStr = new BufferedReader(new InputStreamReader(Objects.requireNonNull(configInput)))
+ .lines().collect(Collectors.joining("\n"));
+ Gson gson = new Gson();
+ ConnectionConfig config = gson.fromJson(configStr, ConnectionConfig.class);
+ try(SqlConnection connection = Connections.newPostgresConnection(config)) {
+ KeyValueTable keyValueTable = Connections.newKeyValueTable(connection, "toolbox_keyvalue");
+ keyValueTable.truncateTable();
+ keyValueTable.insertValue("location", "here");
+ keyValueTable.insertValue("experience", "5");
+ assertEquals("here", keyValueTable.getValue("location"));
+ assertEquals("5", keyValueTable.getValue("experience"));
+ keyValueTable.updateValue("location", "Elsewhere");
+ assertEquals("Elsewhere", keyValueTable.getValue("location"));
+ assertEquals("5", keyValueTable.getValue("experience"));
+ keyValueTable.updateValue("experience", "10");
+ assertEquals("Elsewhere", keyValueTable.getValue("location"));
+ assertEquals("10", keyValueTable.getValue("experience"));
+ keyValueTable.deleteRow("experience");
+ assertEquals("Elsewhere", keyValueTable.getValue("location"));
+ assertNull(keyValueTable.getValue("experience"));
+ keyValueTable.truncateTable();
+ assertNull(keyValueTable.getValue("location"));
+ assertNull(keyValueTable.getValue("experience"));
+ }
+ });
+ }
+
+ // TODO: 08/06/2023 Move to Core module in a toolkit class
+ private InputStream getResource(String resourcePath) {
+ try {
+ URL url = this.getClass().getClassLoader().getResource(resourcePath);
+ if(url == null)
+ return null;
+
+ URLConnection connection = url.openConnection();
+ connection.setUseCaches(false);
+ return connection.getInputStream();
+ } catch (IOException e){
+ return null;
+ }
+ }
+
+}
diff --git a/Database/src/test/resources/config.yml b/Database/src/test/resources/config.yml
index 9e2feb9..4f42d65 100644
--- a/Database/src/test/resources/config.yml
+++ b/Database/src/test/resources/config.yml
@@ -3,5 +3,5 @@
"port": 5432,
"database": "postgres",
"username": "postgres",
- "password": "root"
+ "password": "Vaubadon1"
}
\ No newline at end of file
diff --git a/Pagination/build.gradle b/Pagination/build.gradle
deleted file mode 100644
index 75f7707..0000000
--- a/Pagination/build.gradle
+++ /dev/null
@@ -1,35 +0,0 @@
-plugins {
- id 'fabric-loom'
-}
-
-dependencies {
- minecraft "com.mojang:minecraft:${project.minecraft_version}"
- mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
- modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
-
- modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
-}
-
-processResources {
- inputs.property "version", project.version
-
- filesMatching("fabric.mod.json") {
- expand "version": project.version
- }
-}
-
-tasks.withType(JavaCompile).configureEach {
- // ensure that the encoding is set to UTF-8, no matter what the system default is
- // this fixes some edge cases with special characters not displaying correctly
- // see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html
- // If Javadoc is generated, this must be specified in that task too.
- it.options.encoding = "UTF-8"
-
- // The Minecraft launcher currently installs Java 8 for users, so your mod probably wants to target Java 8 too
- // JDK 9 introduced a new way of specifying this that will make sure no newer classes or methods are used.
- // We'll use that if it's available, but otherwise we'll use the older option.
- def targetVersion = 17
- if (JavaVersion.current().isJava9Compatible()) {
- it.options.release = targetVersion
- }
-}
\ No newline at end of file
diff --git a/Pagination/src/main/java/fr/altarik/toolbox/pagination/PaginatedContent.java b/Pagination/src/main/java/fr/altarik/toolbox/pagination/PaginatedContent.java
index ca07c3a..f6868c4 100644
--- a/Pagination/src/main/java/fr/altarik/toolbox/pagination/PaginatedContent.java
+++ b/Pagination/src/main/java/fr/altarik/toolbox/pagination/PaginatedContent.java
@@ -1,4 +1,113 @@
package fr.altarik.toolbox.pagination;
+import fr.altarik.toolbox.pagination.api.PageIndexOutOfBoundException;
+import net.minecraft.server.network.ServerPlayerEntity;
+import net.minecraft.text.ClickEvent;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
public class PaginatedContent {
+
+ private final List pages;
+ private final Text header;
+
+ public PaginatedContent(String header, String content) {
+ this.header = buildHeader(header);
+ pages = new ArrayList<>();
+ List secondSplit = new ArrayList<>();
+ for(String elem : Stream.of(content.split("\n")).collect(Collectors.toCollection(ArrayList::new))) {
+ if(elem.length() > 50) {
+ secondSplit.add(elem.substring(0, 50));
+ secondSplit.add(elem.substring(51, elem.length() - 1));
+ } else {
+ secondSplit.add(elem);
+ }
+ }
+ List currentPage = new ArrayList<>();
+ for(String elem : secondSplit) {
+ if(!elem.isEmpty()) {
+ currentPage.add(Text.literal(elem));
+ }
+ if(currentPage.size() == 8 || elem.isEmpty()) {
+ pages.add(new Page(currentPage));
+ currentPage = new ArrayList<>();
+ }
+ }
+ pages.add(new Page(currentPage));
+ }
+
+ public PaginatedContent(@Nullable Text header, List content) {
+ this.header = buildHeader(header);
+ this.pages = new ArrayList<>();
+ List currentPage = new ArrayList<>();
+ for(Text elem : content) {
+ if(elem != null)
+ currentPage.add(elem);
+ if(currentPage.size() == 8 || elem == null) {
+ pages.add(new Page(currentPage));
+ currentPage = new ArrayList<>();
+ }
+ }
+ pages.add(new Page(currentPage));
+ }
+
+ private Text buildHeader(@Nullable String header) {
+ int numberOfEq = (50 - (header != null ? header.length() : 0)) / 2;
+ return Text.literal("=".repeat(numberOfEq) + " " + header + " " + "=".repeat(numberOfEq));
+ }
+
+ private Text buildHeader(@Nullable Text header) {
+ int numberOfEq = (50 - (header != null ? header.getString().length() : 0)) / 2;
+ return Text.literal("=".repeat(numberOfEq) + " ").append(header).append(" " + "=".repeat(numberOfEq));
+ }
+
+ public void display(ServerPlayerEntity playerEntity, int page) throws PageIndexOutOfBoundException {
+ if(page >= this.pages.size()) {
+ throw new PageIndexOutOfBoundException("api.pagination.page_higher_than_expected", this.pages.size(), (page + 1));
+ } else if(page < 0) {
+ throw new PageIndexOutOfBoundException("api.pagination.page_lower_than_0");
+ } else {
+ playerEntity.sendMessage(header);
+ for(Text s : pages.get(page).lines) {
+ playerEntity.sendMessage(s);
+ }
+
+ playerEntity.sendMessage(buildFooter(page));
+ }
+ }
+
+ private Text buildFooter(int page) {
+ String strPage = String.valueOf(page + 1);
+ int numberOfEq = (46 - strPage.length()) / 2;
+ MutableText left = Text.literal(" <").styled(
+ style -> style
+ .withColor(Formatting.YELLOW)
+ .withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/table page " + (page - 1)))
+ );
+ MutableText middle = Text.literal(" " + strPage + " ").styled(style -> style.withColor(Formatting.RESET));
+ MutableText right = Text.literal("> ").styled(
+ style -> style
+ .withColor(Formatting.YELLOW)
+ .withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/table page " + (page + 1)))
+ );
+ return Text.literal("=".repeat(numberOfEq))
+ .append(left)
+ .append(middle)
+ .append(right)
+ .append(
+ Text.literal("=".repeat(numberOfEq))
+ .styled(style -> style.withColor(Formatting.RESET))
+ );
+ }
+
+ private record Page(List lines) {
+ }
+
}
diff --git a/Pagination/src/main/java/fr/altarik/toolbox/pagination/Pagination.java b/Pagination/src/main/java/fr/altarik/toolbox/pagination/Pagination.java
index 009e005..5ef76c5 100644
--- a/Pagination/src/main/java/fr/altarik/toolbox/pagination/Pagination.java
+++ b/Pagination/src/main/java/fr/altarik/toolbox/pagination/Pagination.java
@@ -19,13 +19,14 @@ public class Pagination implements ModInitializer {
@Override
public void onInitialize() {
- CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> new CommandsRegister().register(dispatcher, environment.dedicated));
+ CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> new CommandsRegister(this).register(dispatcher));
}
public @NotNull PaginationApi getApi() {
return api;
}
+ @SuppressWarnings("unused")
public static @NotNull Pagination getInstance() {
return instance;
}
diff --git a/Pagination/src/main/java/fr/altarik/toolbox/pagination/api/PageIndexOutOfBoundException.java b/Pagination/src/main/java/fr/altarik/toolbox/pagination/api/PageIndexOutOfBoundException.java
new file mode 100644
index 0000000..1d222f8
--- /dev/null
+++ b/Pagination/src/main/java/fr/altarik/toolbox/pagination/api/PageIndexOutOfBoundException.java
@@ -0,0 +1,20 @@
+package fr.altarik.toolbox.pagination.api;
+
+import net.minecraft.text.Text;
+
+public class PageIndexOutOfBoundException extends Exception {
+
+ private final Text text;
+
+ public PageIndexOutOfBoundException(String s) {
+ this.text = Text.translatable(s);
+ }
+
+ public PageIndexOutOfBoundException(String s, int size, int currentPage) {
+ this.text = Text.translatable(s, size, currentPage);
+ }
+
+ public Text getText() {
+ return text;
+ }
+}
diff --git a/Pagination/src/main/java/fr/altarik/toolbox/pagination/api/PaginationApi.java b/Pagination/src/main/java/fr/altarik/toolbox/pagination/api/PaginationApi.java
index 3adb373..cee4fcf 100644
--- a/Pagination/src/main/java/fr/altarik/toolbox/pagination/api/PaginationApi.java
+++ b/Pagination/src/main/java/fr/altarik/toolbox/pagination/api/PaginationApi.java
@@ -1,12 +1,16 @@
package fr.altarik.toolbox.pagination.api;
import net.minecraft.server.network.ServerPlayerEntity;
+import net.minecraft.text.Text;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
public interface PaginationApi {
/**
* Create a pagination table for player, content is separated into multiple pages.
- * You can separate yourself content by adding *\n\n* between two pages.
+ * You can separate yourself content between two pages by adding *\n\n*.
* Content have a time-to-live of 15 minutes (18,000 ticks)
* @param playerEntity The player who will be able to interact and see the paginated message
* @param content Content you want to paginate
@@ -14,12 +18,48 @@ public interface PaginationApi {
* Special values are:
*
- null if you doesn't want to add a header
* - empty String if you want just the header to be filled only with "="
+ * @param display true if you want the message to be displayed now, false otherwise if you want to display the
+ * message yourself
* @throws IllegalArgumentException if one of its conditions is met:
* - header length is more than 50 characters
- * - content is empty
+ * - content is empty/blank
* - playerEntity or content are null
*
*/
- void createTable(ServerPlayerEntity playerEntity, String content, String header);
+ void createTable(ServerPlayerEntity playerEntity, String content, String header, boolean display);
+
+ /**
+ * Create a pagination table for player the same way than
+ * {@link PaginationApi#createTable(ServerPlayerEntity, String, String, boolean)},
+ * content is separated into multiple pages.
+ * You can separate yourself content between 2 pages by adding a null instance of Text in content list.
+ * Content have a time-to-live of 15 minutes (18,000 ticks)
+ * @param playerEntity The player who will be able to interact and see the paginated message
+ * @param content Content you want to paginate
+ * @param header header/title you want to add to every page, empty space is filled with "=".
+ * Special values are:
+ * - null if you doesn't want to add a header
+ * - Empty textif you want just the header to be filled only with "="
+ * @param display true if you want the message to be displayed now, false otherwise if you want to display the
+ * message yourself
+ * @throws IllegalArgumentException if one of its conditions is met:
+ * - header length is more than 50 characters
+ * - content is empty/blank
+ * - playerEntity or content are null
+ *
+ * @see Text#empty()
+ */
+ void createTable(ServerPlayerEntity playerEntity, List content, @Nullable Text header, boolean display);
+
+
+ /**
+ * Display the given page for the given player
+ * @param player display the content of this player
+ * @param page display this page
+ * @throws IllegalArgumentException if page is invalid
+ * @throws NullPointerException if player is null or paginated content for the player doesn't exist (or have expired)
+ * @see fr.altarik.toolbox.pagination.PaginatedContent#display(ServerPlayerEntity, int)
+ */
+ void display(ServerPlayerEntity player, int page) throws PageIndexOutOfBoundException;
}
diff --git a/Pagination/src/main/java/fr/altarik/toolbox/pagination/api/PaginationApiImpl.java b/Pagination/src/main/java/fr/altarik/toolbox/pagination/api/PaginationApiImpl.java
index 2d0477d..700997c 100644
--- a/Pagination/src/main/java/fr/altarik/toolbox/pagination/api/PaginationApiImpl.java
+++ b/Pagination/src/main/java/fr/altarik/toolbox/pagination/api/PaginationApiImpl.java
@@ -1,18 +1,85 @@
package fr.altarik.toolbox.pagination.api;
import fr.altarik.toolbox.pagination.PaginatedContent;
+import fr.altarik.toolbox.pagination.precondition.ContentCondition;
+import fr.altarik.toolbox.pagination.precondition.HeaderCondition;
+import fr.altarik.toolbox.pagination.precondition.NullPlayerCondition;
+import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
+import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity;
+import net.minecraft.text.Text;
import net.minecraft.util.Pair;
+import org.jetbrains.annotations.Nullable;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
+import java.util.function.Predicate;
public class PaginationApiImpl implements PaginationApi {
+ /**
+ * Integer represent relative tts of the paginated content, decreased by 1 every seconds
+ */
+ public final Map> paginatedContent = new HashMap<>();
+ private final Predicate playerCondition = new NullPlayerCondition().negate();
+ private final Predicate headerCondition = new HeaderCondition().negate();
+ private final Predicate contentCondition = new ContentCondition().negate();
- Map> paginatedContent = new HashMap<>();
+ public PaginationApiImpl() {
+ ServerTickEvents.START_SERVER_TICK.register(this::serverTick);
+ }
@Override
- public void createTable(ServerPlayerEntity playerEntity, String content, String header) {
- // TODO: 01/03/2023
+ public void createTable(ServerPlayerEntity playerEntity, String content, String header, boolean display) {
+ if(playerCondition.test(playerEntity) || headerCondition.test(header) || contentCondition.test(content)) {
+ throw new IllegalArgumentException("Preconditions aren't satisfied");
+ }
+ PaginatedContent paginatedContent1 = new PaginatedContent(header, content);
+ storeAndDisplay(playerEntity, paginatedContent1, display);
+ }
+
+ @Override
+ public void createTable(ServerPlayerEntity playerEntity, List content, @Nullable Text header, boolean display) {
+ if(playerCondition.test(playerEntity)) {
+ throw new IllegalArgumentException("Preconditions aren't satisfied");
+ }
+ PaginatedContent paginatedContent1 = new PaginatedContent(header, content);
+ storeAndDisplay(playerEntity, paginatedContent1, display);
+ }
+
+ private void storeAndDisplay(ServerPlayerEntity playerEntity, PaginatedContent paginatedContent1, boolean display) {
+ paginatedContent.put(playerEntity, new Pair<>(18000, paginatedContent1));
+ if(display) {
+ try {
+ paginatedContent1.display(playerEntity, 0);
+ } catch (PageIndexOutOfBoundException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+ }
+
+ @Override
+ public void display(ServerPlayerEntity player, int page) throws PageIndexOutOfBoundException {
+ if(player == null)
+ throw new NullPointerException("Player is null");
+ Pair pair = paginatedContent.get(player);
+ if(pair == null)
+ throw new NullPointerException("No paginated page for player " + player.getCustomName());
+ pair.getRight().display(player, page);
+ }
+
+ private void serverTick(MinecraftServer server) {
+ List toRemove = new ArrayList<>();
+ for(Map.Entry> content : paginatedContent.entrySet()) {
+ if(content.getValue().getLeft() == 0) {
+ toRemove.add(content.getKey());
+ } else {
+ content.getValue().setLeft(content.getValue().getLeft() - 1);
+ }
+ }
+ for(ServerPlayerEntity player : toRemove) {
+ paginatedContent.remove(player);
+ }
}
}
diff --git a/Pagination/src/main/java/fr/altarik/toolbox/pagination/command/CommandsRegister.java b/Pagination/src/main/java/fr/altarik/toolbox/pagination/command/CommandsRegister.java
index ae295a4..e46d715 100644
--- a/Pagination/src/main/java/fr/altarik/toolbox/pagination/command/CommandsRegister.java
+++ b/Pagination/src/main/java/fr/altarik/toolbox/pagination/command/CommandsRegister.java
@@ -2,19 +2,101 @@ package fr.altarik.toolbox.pagination.command;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.IntegerArgumentType;
+import com.mojang.brigadier.context.CommandContext;
+import com.mojang.brigadier.exceptions.CommandSyntaxException;
+import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
+import fr.altarik.toolbox.pagination.Pagination;
+import fr.altarik.toolbox.pagination.api.PageIndexOutOfBoundException;
+import fr.altarik.toolbox.pagination.api.PaginationApi;
import net.minecraft.server.command.ServerCommandSource;
+import net.minecraft.text.Text;
+
+import java.util.ArrayList;
+import java.util.List;
import static net.minecraft.server.command.CommandManager.argument;
import static net.minecraft.server.command.CommandManager.literal;
public class CommandsRegister {
- public void register(CommandDispatcher dispatcher, boolean dedicated) {
+ private final PaginationApi api;
+
+ public CommandsRegister(Pagination instance) {
+ this.api = instance.getApi();
+ }
+
+ public void register(CommandDispatcher dispatcher) {
dispatcher.register(literal("table")
.then(literal("page")
- .then(argument("page", IntegerArgumentType.integer()))
+ .then(argument("page", IntegerArgumentType.integer())
+ .executes(this::selectPageCommand)
+ )
+ ).then(literal("test")
+ .requires(source -> source.isExecutedByPlayer() && source.hasPermissionLevel(3))
+ .executes(this::testPageCommand)
+ ).then(literal("testText")
+ .requires(source -> source.isExecutedByPlayer() && source.hasPermissionLevel(3))
+ .executes(this::testPageTextCommand)
)
);
}
+ /**
+ * Simply a debug command
+ */
+ private int testPageCommand(CommandContext context) {
+ api.createTable(context.getSource().getPlayer(), """
+ first line, string version
+ Second line
+
+ second page
+ dqdq
+ dqdqd
+ qdqdq
+ dqdq
+ dqdq
+ dqdq
+ dqdqd
+ third page
+ dqdqd
+ dqdqd
+ d""", "My header", true);
+ return 0;
+ }
+
+ private int testPageTextCommand(CommandContext context) {
+ List content = new ArrayList<>();
+ content.add(Text.literal("first line, text version"));
+ content.add(Text.literal("Second line"));
+ content.add(null);
+ content.add(Text.literal("second page"));
+ content.add(Text.literal("dqdq"));
+ content.add(Text.literal("dqdqd"));
+ content.add(Text.literal("dqdqd"));
+ content.add(Text.literal("dqdq"));
+ content.add(Text.literal("dqdq"));
+ content.add(Text.literal("dqdq"));
+ content.add(Text.literal("dqdqd"));
+ content.add(Text.literal("third page"));
+ content.add(Text.literal("dqdqd"));
+ content.add(Text.literal("dqdqd"));
+ api.createTable(context.getSource().getPlayer(), content, Text.literal("My Text Header"), true);
+ return 0;
+ }
+
+ private int selectPageCommand(CommandContext context) throws CommandSyntaxException {
+ try {
+ int page = IntegerArgumentType.getInteger(context, "page");
+ api.display(context.getSource().getPlayerOrThrow(), page);
+ } catch(PageIndexOutOfBoundException e) {
+ throw new CommandSyntaxException(new SimpleCommandExceptionType(e.getText()), e.getText());
+ }
+ return 0;
+ }
+
+ private enum TestType {
+ String,
+ Text;
+ }
+
}
diff --git a/Pagination/src/main/java/fr/altarik/toolbox/pagination/precondition/ContentCondition.java b/Pagination/src/main/java/fr/altarik/toolbox/pagination/precondition/ContentCondition.java
new file mode 100644
index 0000000..a5d2542
--- /dev/null
+++ b/Pagination/src/main/java/fr/altarik/toolbox/pagination/precondition/ContentCondition.java
@@ -0,0 +1,15 @@
+package fr.altarik.toolbox.pagination.precondition;
+
+import java.util.function.Predicate;
+
+/**
+ * This predicate returns true if the String is not null or
+ * if its content is not blank (empty or only contains whitespaces)
+ */
+public class ContentCondition implements Predicate {
+ @Override
+ public boolean test(String s) {
+ return s != null && !s.isBlank();
+ }
+
+}
diff --git a/Pagination/src/main/java/fr/altarik/toolbox/pagination/precondition/HeaderCondition.java b/Pagination/src/main/java/fr/altarik/toolbox/pagination/precondition/HeaderCondition.java
new file mode 100644
index 0000000..9b9ee5e
--- /dev/null
+++ b/Pagination/src/main/java/fr/altarik/toolbox/pagination/precondition/HeaderCondition.java
@@ -0,0 +1,13 @@
+package fr.altarik.toolbox.pagination.precondition;
+
+import java.util.function.Predicate;
+
+/**
+ * This predicate returns true if its length doesn't exceed 50 characters.
+ */
+public class HeaderCondition implements Predicate {
+ @Override
+ public boolean test(String header) {
+ return header.length() <= 50;
+ }
+}
diff --git a/Pagination/src/main/java/fr/altarik/toolbox/pagination/precondition/NullPlayerCondition.java b/Pagination/src/main/java/fr/altarik/toolbox/pagination/precondition/NullPlayerCondition.java
new file mode 100644
index 0000000..16e8dfb
--- /dev/null
+++ b/Pagination/src/main/java/fr/altarik/toolbox/pagination/precondition/NullPlayerCondition.java
@@ -0,0 +1,15 @@
+package fr.altarik.toolbox.pagination.precondition;
+
+import net.minecraft.server.network.ServerPlayerEntity;
+
+import java.util.function.Predicate;
+
+/**
+ * This predicate returns true if the player isn't null, false otherwise
+ */
+public class NullPlayerCondition implements Predicate {
+ @Override
+ public boolean test(ServerPlayerEntity player) {
+ return player != null;
+ }
+}
diff --git a/Pagination/src/main/resources/assets/pagination/lang/en_us.json b/Pagination/src/main/resources/assets/pagination/lang/en_us.json
new file mode 100644
index 0000000..94665c3
--- /dev/null
+++ b/Pagination/src/main/resources/assets/pagination/lang/en_us.json
@@ -0,0 +1,4 @@
+{
+ "api.pagination.page_lower_than_0": "argument page is lower than 0",
+ "api.pagination.page_higher_than_expected": "There's %d paginated pages but you wanted page n°%d"
+}
\ No newline at end of file
diff --git a/Pagination/src/main/resources/fabric.mod.json b/Pagination/src/main/resources/fabric.mod.json
index 52051dc..68cc742 100644
--- a/Pagination/src/main/resources/fabric.mod.json
+++ b/Pagination/src/main/resources/fabric.mod.json
@@ -1,8 +1,8 @@
{
"schemaVersion": 1,
- "id": "pagination",
+ "id": "toolbox-pagination",
"version": "${version}",
- "name": "Task",
+ "name": "Pagination",
"description": "A mod to use to paginate long result to player in chat",
"authors": [
"Altarik"
diff --git a/Tasks/build.gradle b/Tasks/build.gradle
deleted file mode 100644
index 77c4a5a..0000000
--- a/Tasks/build.gradle
+++ /dev/null
@@ -1,36 +0,0 @@
-plugins {
- id 'fabric-loom'
-
-}
-
-dependencies {
- minecraft "com.mojang:minecraft:${project.minecraft_version}"
- mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
- modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
-
- modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
-}
-
-processResources {
- inputs.property "version", project.version
-
- filesMatching("fabric.mod.json") {
- expand "version": project.version
- }
-}
-
-tasks.withType(JavaCompile).configureEach {
- // ensure that the encoding is set to UTF-8, no matter what the system default is
- // this fixes some edge cases with special characters not displaying correctly
- // see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html
- // If Javadoc is generated, this must be specified in that task too.
- it.options.encoding = "UTF-8"
-
- // The Minecraft launcher currently installs Java 8 for users, so your mod probably wants to target Java 8 too
- // JDK 9 introduced a new way of specifying this that will make sure no newer classes or methods are used.
- // We'll use that if it's available, but otherwise we'll use the older option.
- def targetVersion = 17
- if (JavaVersion.current().isJava9Compatible()) {
- it.options.release = targetVersion
- }
-}
diff --git a/Tasks/src/main/resources/fabric.mod.json b/Tasks/src/main/resources/fabric.mod.json
index c7940a1..217672b 100644
--- a/Tasks/src/main/resources/fabric.mod.json
+++ b/Tasks/src/main/resources/fabric.mod.json
@@ -1,6 +1,6 @@
{
"schemaVersion": 1,
- "id": "task",
+ "id": "toolbox-task",
"version": "${version}",
"name": "Task",
"description": "A mod to use as a dependency for others to schedule tasks",
diff --git a/build.gradle b/build.gradle
index 7ab39e6..333cd5f 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,11 +1,13 @@
plugins {
- id 'java'
- id 'fabric-loom' version '1.1-SNAPSHOT' apply false
+ id 'fabric-loom' version '1.2-SNAPSHOT' apply false
}
+Properties local = new Properties()
+local.load(new FileInputStream(rootProject.file("local.properties")))
+
allprojects {
- apply plugin: 'java'
apply plugin: 'maven-publish'
+ apply plugin: 'fabric-loom'
group = project.maven_group
version = project.maven_version
@@ -34,25 +36,50 @@ allprojects {
mavenJava(MavenPublication) {
from components.java
}
- }
+ }
repositories {
maven {
name 'altarik'
url 'https://repo.altarik.fr/'.concat(project.version.endsWith('SNAPSHOT') ? 'snapshots/' : 'releases/')
credentials {
- username = project.repo_username
- password = project.repo_password
+ username = System.getProperty("REPO_USERNAME", local.getProperty("repo_username"))
+ password = System.getProperty("REPO_PASSWORD", local.getProperty("repo_password"))
}
}
}
}
-}
-subprojects {
+ processResources {
+ inputs.property "version", project.version
+
+ filesMatching("fabric.mod.json") {
+ expand "version": project.version
+ }
+ }
+
+ tasks.withType(JavaCompile).configureEach {
+ // ensure that the encoding is set to UTF-8, no matter what the system default is
+ // this fixes some edge cases with special characters not displaying correctly
+ // see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html
+ // If Javadoc is generated, this must be specified in that task too.
+ it.options.encoding = "UTF-8"
+
+ // The Minecraft launcher currently installs Java 8 for users, so your mod probably wants to target Java 8 too
+ // JDK 9 introduced a new way of specifying this that will make sure no newer classes or methods are used.
+ // We'll use that if it's available, but otherwise we'll use the older option.
+ def targetVersion = 17
+ if (JavaVersion.current().isJava9Compatible()) {
+ it.options.release = targetVersion
+ }
+ }
dependencies {
testImplementation "org.junit.jupiter:junit-jupiter-api:${project.junit_version}"
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${project.junit_version}"
+ minecraft "com.mojang:minecraft:${project.minecraft_version}"
+ mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
+ modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
+ modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
}
java {
@@ -66,11 +93,16 @@ subprojects {
}
-jar {
+dependencies {
+ include subprojects.collect { project -> project }
+ implementation subprojects.collect { project -> project }
+}
+
+/*jar {
dependsOn subprojects.jar
subprojects.each { project ->
from(project.jar) {
into("META-INF/jars/")
}
}
-}
+}*/
diff --git a/gradle.properties b/gradle.properties
index f76810d..edaf432 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,4 +1,4 @@
-org.gradle.jvmargs=-Xmx4G
+org.gradle.jvmargs=-Xmx2G
junit_version=5.9.0
@@ -8,6 +8,5 @@ loader_version=0.14.14
fabric_version=0.75.1+1.19.3
maven_group=fr.altarik.toolbox
-maven_version=4.0.0-SNAPSHOT
+maven_version=4.1.0-SNAPSHOT
repo_username=Altarik
-repo_password=password
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 070cb70..59bc51a 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/settings.gradle b/settings.gradle
index 16f164b..c266500 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -10,4 +10,7 @@ pluginManagement {
}
rootProject.name = 'Toolbox'
-include(':Tasks', ':Database', /* ':Pagination' */)
+include(':Tasks')
+include(':Database')
+include(':Pagination')
+include(':Core')
diff --git a/src/main/resources/assets/toolbox/icon.png b/src/main/resources/assets/toolbox/icon.png
new file mode 100644
index 0000000..1049a98
Binary files /dev/null and b/src/main/resources/assets/toolbox/icon.png differ
diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json
index 797e8d6..ce07720 100644
--- a/src/main/resources/fabric.mod.json
+++ b/src/main/resources/fabric.mod.json
@@ -14,13 +14,17 @@
"homepage": "https://altarik.fr"
},
"license": "Altarik @ All-Rights-Reserved ",
- "icon": "assets/quests/icon.png",
+ "icon": "assets/toolbox/icon.png",
"environment": "*",
"depends": {
"fabricloader": "^0.14.12",
"fabric-api": "*",
"minecraft": "1.19.3",
- "java": ">=17"
+ "java": ">=17",
+ "toolbox-core": "${version}",
+ "toolbox-database": "${version}",
+ "toolbox-pagination": "${version}",
+ "toolbox-task": "${version}"
}
}