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:
    *
  1. header length is more than 50 characters
  2. - *
  3. content is empty
  4. + *
  5. content is empty/blank
  6. *
  7. playerEntity or content are null
  8. *
*/ - 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:
    + *
  1. header length is more than 50 characters
  2. + *
  3. content is empty/blank
  4. + *
  5. playerEntity or content are null
  6. + *
+ * @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}" } }