From 401713e1871a3c548476fb163a0345332d11d440 Mon Sep 17 00:00:00 2001 From: phinner <62483793+phinner@users.noreply.github.com> Date: Sat, 10 Feb 2024 14:58:40 +0100 Subject: [PATCH] feat: upload initial changes --- .editorconfig | 4 + .github/workflows/build.yml | 66 ++-- .spotless/distributor.importorder | 3 - LICENSE_HEADER.md | 2 +- build.gradle.kts | 15 +- buildSrc/build.gradle.kts | 22 -- ...stributor.mindustry-conventions.gradle.kts | 46 --- .../distributor.parent-conventions.gradle.kts | 4 - ...tributor.publishing-conventions.gradle.kts | 38 -- buildSrc/src/main/kotlin/extensions.kt | 3 - distributor-api/build.gradle.kts | 24 -- .../xpdustry/distributor/api/Distributor.java | 56 --- .../DistributorInitializationException.java | 34 -- .../distributor/api/DistributorProvider.java | 72 ---- .../distributor/api/command/ArcCommand.java | 189 ---------- .../api/command/ArcCommandManager.java | 286 --------------- .../api/command/ArcParserParameters.java | 38 -- .../api/command/argument/PlayerArgument.java | 228 ------------ .../command/argument/PlayerInfoArgument.java | 162 --------- .../api/command/argument/TeamArgument.java | 273 -------------- .../api/command/sender/CommandSender.java | 109 ------ .../command/sender/ConsoleCommandSender.java | 64 ---- .../command/sender/PlayerCommandSender.java | 63 ---- .../api/command/specifier/AllTeams.java | 34 -- .../distributor/api/event/EventBus.java | 151 -------- .../api/localization/LocalizationSource.java | 76 ---- .../LocalizationSourceRegistry.java | 169 --------- .../LocalizationSourceRegistryImpl.java | 94 ----- .../localization/MultiLocalizationSource.java | 39 -- .../MultiLocalizationSourceImpl.java | 49 --- .../RouterLocalizationSource.java | 38 -- .../distributor/api/package-info.java | 41 --- .../api/plugin/PluginAnnotationParser.java | 45 --- .../plugin/SimplePluginAnnotationParser.java | 144 -------- .../api/scheduler/Cancellable.java | 30 -- .../api/scheduler/MindustryTimeUnit.java | 109 ------ .../api/scheduler/PluginScheduler.java | 68 ---- .../distributor/api/scheduler/PluginTask.java | 35 -- .../api/scheduler/PluginTaskBuilder.java | 80 ----- .../api/scheduler/PluginTaskRecipe.java | 57 --- .../api/scheduler/TaskHandler.java | 57 --- .../api/security/PlayerValidator.java | 96 ----- .../security/permission/GroupPermissible.java | 39 -- .../api/security/permission/Permissible.java | 130 ------- .../permission/PermissibleManager.java | 117 ------ .../permission/PermissionService.java | 72 ---- .../permission/PlayerPermissible.java | 30 -- .../argument/AbstractArgumentTest.java | 59 --- .../AbstractPlayerLookupArgumentTest.java | 127 ------- .../argument/AbstractTeamArgumentTest.java | 69 ---- .../command/argument/AllTeamArgumentTest.java | 54 --- .../argument/BaseTeamArgumentTest.java | 27 -- .../argument/PlayerInfoArgumentTest.java | 77 ---- .../LocalizationSourceRegistryImplTest.java | 80 ----- .../MultiLocalizationSourceImplTest.java | 55 --- .../api/scheduler/MindustryTimeUnitTest.java | 80 ----- .../distributor/api/util/ArcListTest.java | 71 ---- .../distributor/api/util/ArcMapTest.java | 71 ---- .../distributor/api/util/ArcSetTest.java | 67 ---- .../distributor/api/util/PlayersTest.java | 109 ------ distributor-bom/build.gradle.kts | 22 -- distributor-build-logic/build.gradle.kts | 37 ++ distributor-build-logic/settings.gradle.kts | 9 + .../distributor4.base-conventions.gradle.kts | 34 +- ...tributor4.mindustry-conventions.gradle.kts | 68 ++++ ...distributor4.parent-conventions.gradle.kts | 20 ++ .../src/main/kotlin/extensions.kt | 17 + distributor-command-cloud/build.gradle.kts | 23 ++ .../command/cloud}/ArcCaptionKeys.java | 8 +- .../command/cloud/ArcCommandManager.java | 124 +++++++ .../cloud}/ArcRegistrationHandler.java | 56 +-- .../command/cloud/CloudCommandFacade.java | 190 ++++++++++ .../command/cloud/PermissionChecker.java | 15 +- .../command/cloud/package-info.java | 4 + .../command/cloud/parser/TeamParser.java | 123 +++++++ .../command/cloud/parser/package-info.java | 4 + distributor-common/build.gradle.kts | 19 + .../distributor/core/Distributor.java | 21 ++ .../core/DistributorCorePlugin.java | 15 +- .../core/collection}/ArcCollections.java | 4 +- .../distributor/core/collection}/ArcList.java | 4 +- .../distributor/core/collection}/ArcMap.java | 14 +- .../distributor/core/collection}/ArcSet.java | 6 +- .../core/collection/package-info.java | 4 + .../core/command/ArcCommandFacade.java | 73 ++++ .../core/command/CommandDescription.java | 12 +- .../core/command/CommandElement.java | 70 ++++ .../core/command/CommandFacade.java | 41 ++- .../distributor/core/command/CommandHelp.java | 71 ++++ .../core/command/CommandSender.java | 60 ++++ .../command/SimpleCommandFacadeFactory.java | 23 +- .../core/command/package-info.java | 4 + .../core/internal/GeneratedDataClass.java | 32 +- .../distributor/core/package-info.java | 4 + .../core/permission/Permissible.java | 21 ++ .../core/permission/package-info.java | 4 + .../distributor/core/player}/MUUID.java | 6 +- .../distributor/core/player/PlayerLookup.java | 27 +- .../core/player/SimplePlayerLookup.java | 56 +-- .../core}/plugin/AbstractMindustryPlugin.java | 6 +- .../core}/plugin/MindustryPlugin.java | 4 +- .../core/plugin/PluginAnnotationParser.java | 15 +- .../distributor/core}/plugin/PluginAware.java | 4 +- .../core}/plugin/PluginDescriptor.java | 6 +- .../core}/plugin/PluginListener.java | 4 +- .../core}/plugin/WrappingMindustryPlugin.java | 4 +- .../distributor/core/plugin/package-info.java | 4 + distributor-core/build.gradle.kts | 48 --- .../core/DistributorConfiguration.java | 88 ----- .../core/DistributorCorePlugin.java | 259 -------------- .../commands/GroupPermissibleCommands.java | 127 ------- .../core/commands/PermissibleCommands.java | 233 ------------ .../commands/PlayerPermissibleCommands.java | 42 --- .../commands/PlayerValidatorCommands.java | 106 ------ .../parser/GroupPermissibleParser.java | 38 -- .../commands/parser/PermissibleParser.java | 73 ---- .../parser/PlayerPermissibleParser.java | 48 --- .../core/database/ConnectionConsumer.java | 28 -- .../core/database/ConnectionFactory.java | 108 ------ .../core/database/ConnectionFunction.java | 28 -- .../database/HikariConnectionFactory.java | 97 ----- .../core/database/MySQLConnectionFactory.java | 108 ------ .../core/database/NonClosableConnection.java | 338 ------------------ .../database/SQLiteConnectionFactory.java | 90 ----- .../core/dependency/Dependency.java | 52 --- .../core/dependency/DependencyManager.java | 219 ------------ .../core/dependency/IsolatedClassLoader.java | 34 -- .../core/event/SimpleEventBus.java | 210 ----------- .../distributor/core/package-info.java | 41 --- .../core/scheduler/RecipePluginTask.java | 236 ------------ .../core/scheduler/SimplePluginScheduler.java | 195 ---------- .../core/scheduler/SimplePluginTask.java | 163 --------- .../core/scheduler/TimeSource.java | 44 --- .../security/PlayerValidatorListener.java | 94 ----- .../core/security/SQLPlayerValidator.java | 231 ------------ .../permission/AbstractPermissible.java | 97 ----- .../AbstractSQLPermissibleManager.java | 222 ------------ .../security/permission/Permissibles.java | 39 -- .../security/permission/PermissionTree.java | 123 ------- .../SQLGroupPermissibleManager.java | 69 ---- .../permission/SQLPermissionService.java | 185 ---------- .../SQLPlayerPermissibleManager.java | 59 --- .../permission/SimpleGroupPermissible.java | 75 ---- .../permission/SimplePlayerPermissible.java | 70 ---- .../org.slf4j.spi.SLF4JServiceProvider | 1 - .../fr/xpdustry/distributor/assets/banner.txt | 4 - .../assets/bundles/bundle_en.properties | 57 --- .../assets/bundles/bundle_fr.properties | 26 -- .../distributor/assets/schemas/permission.sql | 40 --- .../core/event/SimpleEventBusTest.java | 287 --------------- .../scheduler/SimplePluginSchedulerTest.java | 285 --------------- .../core/security/SQLPlayerValidatorTest.java | 271 -------------- .../AbstractSQLPermissibleManagerTest.java | 162 --------- .../SQLGroupPermissibleManagerTest.java | 42 --- .../permission/SQLPermissionServiceTest.java | 145 -------- .../SQLPlayerPermissibleManagerTest.java | 47 --- distributor-kotlin/build.gradle.kts | 83 ----- distributor-kotlin/gradle.properties | 1 - .../kotlin/DistributorKotlinPlugin.kt | 27 -- distributor-logging-simple/build.gradle.kts | 16 + .../logger/simple/DistributorLogger.java | 65 ++-- .../simple/DistributorLoggerFactory.java | 80 +++-- .../simple/DistributorLoggerPlugin.java | 57 +++ .../simple/DistributorLoggerProvider.java | 23 +- .../logger/simple/package-info.java | 4 + .../org.slf4j.spi.SLF4JServiceProvider | 1 + gradle/libs.versions.toml | 82 +++++ plugin.json | 2 + settings.gradle.kts | 8 +- 169 files changed, 1465 insertions(+), 10607 deletions(-) create mode 100644 .editorconfig delete mode 100644 .spotless/distributor.importorder delete mode 100644 buildSrc/build.gradle.kts delete mode 100644 buildSrc/src/main/kotlin/distributor.mindustry-conventions.gradle.kts delete mode 100644 buildSrc/src/main/kotlin/distributor.parent-conventions.gradle.kts delete mode 100644 buildSrc/src/main/kotlin/distributor.publishing-conventions.gradle.kts delete mode 100644 buildSrc/src/main/kotlin/extensions.kt delete mode 100644 distributor-api/build.gradle.kts delete mode 100644 distributor-api/src/main/java/fr/xpdustry/distributor/api/Distributor.java delete mode 100644 distributor-api/src/main/java/fr/xpdustry/distributor/api/DistributorInitializationException.java delete mode 100644 distributor-api/src/main/java/fr/xpdustry/distributor/api/DistributorProvider.java delete mode 100644 distributor-api/src/main/java/fr/xpdustry/distributor/api/command/ArcCommand.java delete mode 100644 distributor-api/src/main/java/fr/xpdustry/distributor/api/command/ArcCommandManager.java delete mode 100644 distributor-api/src/main/java/fr/xpdustry/distributor/api/command/ArcParserParameters.java delete mode 100644 distributor-api/src/main/java/fr/xpdustry/distributor/api/command/argument/PlayerArgument.java delete mode 100644 distributor-api/src/main/java/fr/xpdustry/distributor/api/command/argument/PlayerInfoArgument.java delete mode 100644 distributor-api/src/main/java/fr/xpdustry/distributor/api/command/argument/TeamArgument.java delete mode 100644 distributor-api/src/main/java/fr/xpdustry/distributor/api/command/sender/CommandSender.java delete mode 100644 distributor-api/src/main/java/fr/xpdustry/distributor/api/command/sender/ConsoleCommandSender.java delete mode 100644 distributor-api/src/main/java/fr/xpdustry/distributor/api/command/sender/PlayerCommandSender.java delete mode 100644 distributor-api/src/main/java/fr/xpdustry/distributor/api/command/specifier/AllTeams.java delete mode 100644 distributor-api/src/main/java/fr/xpdustry/distributor/api/event/EventBus.java delete mode 100644 distributor-api/src/main/java/fr/xpdustry/distributor/api/localization/LocalizationSource.java delete mode 100644 distributor-api/src/main/java/fr/xpdustry/distributor/api/localization/LocalizationSourceRegistry.java delete mode 100644 distributor-api/src/main/java/fr/xpdustry/distributor/api/localization/LocalizationSourceRegistryImpl.java delete mode 100644 distributor-api/src/main/java/fr/xpdustry/distributor/api/localization/MultiLocalizationSource.java delete mode 100644 distributor-api/src/main/java/fr/xpdustry/distributor/api/localization/MultiLocalizationSourceImpl.java delete mode 100644 distributor-api/src/main/java/fr/xpdustry/distributor/api/localization/RouterLocalizationSource.java delete mode 100644 distributor-api/src/main/java/fr/xpdustry/distributor/api/package-info.java delete mode 100644 distributor-api/src/main/java/fr/xpdustry/distributor/api/plugin/PluginAnnotationParser.java delete mode 100644 distributor-api/src/main/java/fr/xpdustry/distributor/api/plugin/SimplePluginAnnotationParser.java delete mode 100644 distributor-api/src/main/java/fr/xpdustry/distributor/api/scheduler/Cancellable.java delete mode 100644 distributor-api/src/main/java/fr/xpdustry/distributor/api/scheduler/MindustryTimeUnit.java delete mode 100644 distributor-api/src/main/java/fr/xpdustry/distributor/api/scheduler/PluginScheduler.java delete mode 100644 distributor-api/src/main/java/fr/xpdustry/distributor/api/scheduler/PluginTask.java delete mode 100644 distributor-api/src/main/java/fr/xpdustry/distributor/api/scheduler/PluginTaskBuilder.java delete mode 100644 distributor-api/src/main/java/fr/xpdustry/distributor/api/scheduler/PluginTaskRecipe.java delete mode 100644 distributor-api/src/main/java/fr/xpdustry/distributor/api/scheduler/TaskHandler.java delete mode 100644 distributor-api/src/main/java/fr/xpdustry/distributor/api/security/PlayerValidator.java delete mode 100644 distributor-api/src/main/java/fr/xpdustry/distributor/api/security/permission/GroupPermissible.java delete mode 100644 distributor-api/src/main/java/fr/xpdustry/distributor/api/security/permission/Permissible.java delete mode 100644 distributor-api/src/main/java/fr/xpdustry/distributor/api/security/permission/PermissibleManager.java delete mode 100644 distributor-api/src/main/java/fr/xpdustry/distributor/api/security/permission/PermissionService.java delete mode 100644 distributor-api/src/main/java/fr/xpdustry/distributor/api/security/permission/PlayerPermissible.java delete mode 100644 distributor-api/src/test/java/fr/xpdustry/distributor/api/command/argument/AbstractArgumentTest.java delete mode 100644 distributor-api/src/test/java/fr/xpdustry/distributor/api/command/argument/AbstractPlayerLookupArgumentTest.java delete mode 100644 distributor-api/src/test/java/fr/xpdustry/distributor/api/command/argument/AbstractTeamArgumentTest.java delete mode 100644 distributor-api/src/test/java/fr/xpdustry/distributor/api/command/argument/AllTeamArgumentTest.java delete mode 100644 distributor-api/src/test/java/fr/xpdustry/distributor/api/command/argument/BaseTeamArgumentTest.java delete mode 100644 distributor-api/src/test/java/fr/xpdustry/distributor/api/command/argument/PlayerInfoArgumentTest.java delete mode 100644 distributor-api/src/test/java/fr/xpdustry/distributor/api/localization/LocalizationSourceRegistryImplTest.java delete mode 100644 distributor-api/src/test/java/fr/xpdustry/distributor/api/localization/MultiLocalizationSourceImplTest.java delete mode 100644 distributor-api/src/test/java/fr/xpdustry/distributor/api/scheduler/MindustryTimeUnitTest.java delete mode 100644 distributor-api/src/test/java/fr/xpdustry/distributor/api/util/ArcListTest.java delete mode 100644 distributor-api/src/test/java/fr/xpdustry/distributor/api/util/ArcMapTest.java delete mode 100644 distributor-api/src/test/java/fr/xpdustry/distributor/api/util/ArcSetTest.java delete mode 100644 distributor-api/src/test/java/fr/xpdustry/distributor/api/util/PlayersTest.java delete mode 100644 distributor-bom/build.gradle.kts create mode 100644 distributor-build-logic/build.gradle.kts create mode 100644 distributor-build-logic/settings.gradle.kts rename buildSrc/src/main/kotlin/distributor.base-conventions.gradle.kts => distributor-build-logic/src/main/kotlin/distributor4.base-conventions.gradle.kts (56%) create mode 100644 distributor-build-logic/src/main/kotlin/distributor4.mindustry-conventions.gradle.kts create mode 100644 distributor-build-logic/src/main/kotlin/distributor4.parent-conventions.gradle.kts create mode 100644 distributor-build-logic/src/main/kotlin/extensions.kt create mode 100644 distributor-command-cloud/build.gradle.kts rename {distributor-api/src/main/java/fr/xpdustry/distributor/api/command => distributor-command-cloud/src/main/java/com/xpdustry/distributor/command/cloud}/ArcCaptionKeys.java (93%) create mode 100644 distributor-command-cloud/src/main/java/com/xpdustry/distributor/command/cloud/ArcCommandManager.java rename {distributor-api/src/main/java/fr/xpdustry/distributor/api/command => distributor-command-cloud/src/main/java/com/xpdustry/distributor/command/cloud}/ArcRegistrationHandler.java (61%) create mode 100644 distributor-command-cloud/src/main/java/com/xpdustry/distributor/command/cloud/CloudCommandFacade.java rename distributor-api/src/main/java/fr/xpdustry/distributor/api/util/Priority.java => distributor-command-cloud/src/main/java/com/xpdustry/distributor/command/cloud/PermissionChecker.java (76%) create mode 100644 distributor-command-cloud/src/main/java/com/xpdustry/distributor/command/cloud/package-info.java create mode 100644 distributor-command-cloud/src/main/java/com/xpdustry/distributor/command/cloud/parser/TeamParser.java create mode 100644 distributor-command-cloud/src/main/java/com/xpdustry/distributor/command/cloud/parser/package-info.java create mode 100644 distributor-common/build.gradle.kts create mode 100644 distributor-common/src/main/java/com/xpdustry/distributor/core/Distributor.java rename distributor-api/src/main/java/fr/xpdustry/distributor/api/event/EventSubscription.java => distributor-common/src/main/java/com/xpdustry/distributor/core/DistributorCorePlugin.java (74%) rename {distributor-api/src/main/java/fr/xpdustry/distributor/api/util => distributor-common/src/main/java/com/xpdustry/distributor/core/collection}/ArcCollections.java (98%) rename {distributor-api/src/main/java/fr/xpdustry/distributor/api/util => distributor-common/src/main/java/com/xpdustry/distributor/core/collection}/ArcList.java (97%) rename {distributor-api/src/main/java/fr/xpdustry/distributor/api/util => distributor-common/src/main/java/com/xpdustry/distributor/core/collection}/ArcMap.java (93%) rename {distributor-api/src/main/java/fr/xpdustry/distributor/api/util => distributor-common/src/main/java/com/xpdustry/distributor/core/collection}/ArcSet.java (94%) create mode 100644 distributor-common/src/main/java/com/xpdustry/distributor/core/collection/package-info.java create mode 100644 distributor-common/src/main/java/com/xpdustry/distributor/core/command/ArcCommandFacade.java rename distributor-core/src/main/java/fr/xpdustry/distributor/core/scheduler/ScheduledPluginTask.java => distributor-common/src/main/java/com/xpdustry/distributor/core/command/CommandDescription.java (75%) create mode 100644 distributor-common/src/main/java/com/xpdustry/distributor/core/command/CommandElement.java rename distributor-api/src/main/java/fr/xpdustry/distributor/api/util/Tristate.java => distributor-common/src/main/java/com/xpdustry/distributor/core/command/CommandFacade.java (53%) create mode 100644 distributor-common/src/main/java/com/xpdustry/distributor/core/command/CommandHelp.java create mode 100644 distributor-common/src/main/java/com/xpdustry/distributor/core/command/CommandSender.java rename distributor-api/src/test/java/fr/xpdustry/distributor/api/command/argument/PlayerArgumentTest.java => distributor-common/src/main/java/com/xpdustry/distributor/core/command/SimpleCommandFacadeFactory.java (56%) create mode 100644 distributor-common/src/main/java/com/xpdustry/distributor/core/command/package-info.java rename distributor-api/src/main/java/fr/xpdustry/distributor/api/event/EventHandler.java => distributor-common/src/main/java/com/xpdustry/distributor/core/internal/GeneratedDataClass.java (61%) create mode 100644 distributor-common/src/main/java/com/xpdustry/distributor/core/package-info.java create mode 100644 distributor-common/src/main/java/com/xpdustry/distributor/core/permission/Permissible.java create mode 100644 distributor-common/src/main/java/com/xpdustry/distributor/core/permission/package-info.java rename {distributor-api/src/main/java/fr/xpdustry/distributor/api/util => distributor-common/src/main/java/com/xpdustry/distributor/core/player}/MUUID.java (97%) rename distributor-api/src/test/java/fr/xpdustry/distributor/api/TestPlayer.java => distributor-common/src/main/java/com/xpdustry/distributor/core/player/PlayerLookup.java (61%) rename distributor-api/src/main/java/fr/xpdustry/distributor/api/util/Players.java => distributor-common/src/main/java/com/xpdustry/distributor/core/player/SimplePlayerLookup.java (66%) rename {distributor-api/src/main/java/fr/xpdustry/distributor/api => distributor-common/src/main/java/com/xpdustry/distributor/core}/plugin/AbstractMindustryPlugin.java (97%) rename {distributor-api/src/main/java/fr/xpdustry/distributor/api => distributor-common/src/main/java/com/xpdustry/distributor/core}/plugin/MindustryPlugin.java (97%) rename distributor-api/src/main/java/fr/xpdustry/distributor/api/security/PlayerValidatorEvent.java => distributor-common/src/main/java/com/xpdustry/distributor/core/plugin/PluginAnnotationParser.java (74%) rename {distributor-api/src/main/java/fr/xpdustry/distributor/api => distributor-common/src/main/java/com/xpdustry/distributor/core}/plugin/PluginAware.java (92%) rename {distributor-api/src/main/java/fr/xpdustry/distributor/api => distributor-common/src/main/java/com/xpdustry/distributor/core}/plugin/PluginDescriptor.java (97%) rename {distributor-api/src/main/java/fr/xpdustry/distributor/api => distributor-common/src/main/java/com/xpdustry/distributor/core}/plugin/PluginListener.java (95%) rename {distributor-api/src/main/java/fr/xpdustry/distributor/api => distributor-common/src/main/java/com/xpdustry/distributor/core}/plugin/WrappingMindustryPlugin.java (96%) create mode 100644 distributor-common/src/main/java/com/xpdustry/distributor/core/plugin/package-info.java delete mode 100644 distributor-core/build.gradle.kts delete mode 100644 distributor-core/src/main/java/fr/xpdustry/distributor/core/DistributorConfiguration.java delete mode 100644 distributor-core/src/main/java/fr/xpdustry/distributor/core/DistributorCorePlugin.java delete mode 100644 distributor-core/src/main/java/fr/xpdustry/distributor/core/commands/GroupPermissibleCommands.java delete mode 100644 distributor-core/src/main/java/fr/xpdustry/distributor/core/commands/PermissibleCommands.java delete mode 100644 distributor-core/src/main/java/fr/xpdustry/distributor/core/commands/PlayerPermissibleCommands.java delete mode 100644 distributor-core/src/main/java/fr/xpdustry/distributor/core/commands/PlayerValidatorCommands.java delete mode 100644 distributor-core/src/main/java/fr/xpdustry/distributor/core/commands/parser/GroupPermissibleParser.java delete mode 100644 distributor-core/src/main/java/fr/xpdustry/distributor/core/commands/parser/PermissibleParser.java delete mode 100644 distributor-core/src/main/java/fr/xpdustry/distributor/core/commands/parser/PlayerPermissibleParser.java delete mode 100644 distributor-core/src/main/java/fr/xpdustry/distributor/core/database/ConnectionConsumer.java delete mode 100644 distributor-core/src/main/java/fr/xpdustry/distributor/core/database/ConnectionFactory.java delete mode 100644 distributor-core/src/main/java/fr/xpdustry/distributor/core/database/ConnectionFunction.java delete mode 100644 distributor-core/src/main/java/fr/xpdustry/distributor/core/database/HikariConnectionFactory.java delete mode 100644 distributor-core/src/main/java/fr/xpdustry/distributor/core/database/MySQLConnectionFactory.java delete mode 100644 distributor-core/src/main/java/fr/xpdustry/distributor/core/database/NonClosableConnection.java delete mode 100644 distributor-core/src/main/java/fr/xpdustry/distributor/core/database/SQLiteConnectionFactory.java delete mode 100644 distributor-core/src/main/java/fr/xpdustry/distributor/core/dependency/Dependency.java delete mode 100644 distributor-core/src/main/java/fr/xpdustry/distributor/core/dependency/DependencyManager.java delete mode 100644 distributor-core/src/main/java/fr/xpdustry/distributor/core/dependency/IsolatedClassLoader.java delete mode 100644 distributor-core/src/main/java/fr/xpdustry/distributor/core/event/SimpleEventBus.java delete mode 100644 distributor-core/src/main/java/fr/xpdustry/distributor/core/package-info.java delete mode 100644 distributor-core/src/main/java/fr/xpdustry/distributor/core/scheduler/RecipePluginTask.java delete mode 100644 distributor-core/src/main/java/fr/xpdustry/distributor/core/scheduler/SimplePluginScheduler.java delete mode 100644 distributor-core/src/main/java/fr/xpdustry/distributor/core/scheduler/SimplePluginTask.java delete mode 100644 distributor-core/src/main/java/fr/xpdustry/distributor/core/scheduler/TimeSource.java delete mode 100644 distributor-core/src/main/java/fr/xpdustry/distributor/core/security/PlayerValidatorListener.java delete mode 100644 distributor-core/src/main/java/fr/xpdustry/distributor/core/security/SQLPlayerValidator.java delete mode 100644 distributor-core/src/main/java/fr/xpdustry/distributor/core/security/permission/AbstractPermissible.java delete mode 100644 distributor-core/src/main/java/fr/xpdustry/distributor/core/security/permission/AbstractSQLPermissibleManager.java delete mode 100644 distributor-core/src/main/java/fr/xpdustry/distributor/core/security/permission/Permissibles.java delete mode 100644 distributor-core/src/main/java/fr/xpdustry/distributor/core/security/permission/PermissionTree.java delete mode 100644 distributor-core/src/main/java/fr/xpdustry/distributor/core/security/permission/SQLGroupPermissibleManager.java delete mode 100644 distributor-core/src/main/java/fr/xpdustry/distributor/core/security/permission/SQLPermissionService.java delete mode 100644 distributor-core/src/main/java/fr/xpdustry/distributor/core/security/permission/SQLPlayerPermissibleManager.java delete mode 100644 distributor-core/src/main/java/fr/xpdustry/distributor/core/security/permission/SimpleGroupPermissible.java delete mode 100644 distributor-core/src/main/java/fr/xpdustry/distributor/core/security/permission/SimplePlayerPermissible.java delete mode 100644 distributor-core/src/main/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider delete mode 100644 distributor-core/src/main/resources/fr/xpdustry/distributor/assets/banner.txt delete mode 100644 distributor-core/src/main/resources/fr/xpdustry/distributor/assets/bundles/bundle_en.properties delete mode 100644 distributor-core/src/main/resources/fr/xpdustry/distributor/assets/bundles/bundle_fr.properties delete mode 100644 distributor-core/src/main/resources/fr/xpdustry/distributor/assets/schemas/permission.sql delete mode 100644 distributor-core/src/test/java/fr/xpdustry/distributor/core/event/SimpleEventBusTest.java delete mode 100644 distributor-core/src/test/java/fr/xpdustry/distributor/core/scheduler/SimplePluginSchedulerTest.java delete mode 100644 distributor-core/src/test/java/fr/xpdustry/distributor/core/security/SQLPlayerValidatorTest.java delete mode 100644 distributor-core/src/test/java/fr/xpdustry/distributor/core/security/permission/AbstractSQLPermissibleManagerTest.java delete mode 100644 distributor-core/src/test/java/fr/xpdustry/distributor/core/security/permission/SQLGroupPermissibleManagerTest.java delete mode 100644 distributor-core/src/test/java/fr/xpdustry/distributor/core/security/permission/SQLPermissionServiceTest.java delete mode 100644 distributor-core/src/test/java/fr/xpdustry/distributor/core/security/permission/SQLPlayerPermissibleManagerTest.java delete mode 100644 distributor-kotlin/build.gradle.kts delete mode 100644 distributor-kotlin/gradle.properties delete mode 100644 distributor-kotlin/src/main/kotlin/fr/xpdustry/distributor/kotlin/DistributorKotlinPlugin.kt create mode 100644 distributor-logging-simple/build.gradle.kts rename distributor-core/src/main/java/fr/xpdustry/distributor/core/logging/ArcLogger.java => distributor-logging-simple/src/main/java/com/xpdustry/distributor/logger/simple/DistributorLogger.java (70%) rename distributor-core/src/main/java/fr/xpdustry/distributor/core/logging/ArcLoggerFactory.java => distributor-logging-simple/src/main/java/com/xpdustry/distributor/logger/simple/DistributorLoggerFactory.java (52%) create mode 100644 distributor-logging-simple/src/main/java/com/xpdustry/distributor/logger/simple/DistributorLoggerPlugin.java rename distributor-core/src/main/java/fr/xpdustry/distributor/core/logging/ArcServiceProvider.java => distributor-logging-simple/src/main/java/com/xpdustry/distributor/logger/simple/DistributorLoggerProvider.java (68%) create mode 100644 distributor-logging-simple/src/main/java/com/xpdustry/distributor/logger/simple/package-info.java create mode 100644 distributor-logging-simple/src/main/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider create mode 100644 gradle/libs.versions.toml diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..ec1929cb --- /dev/null +++ b/.editorconfig @@ -0,0 +1,4 @@ +[*.{kt,kts}] +ktlint_code_style = ktlint_official +ktlint_standard_filename = disabled +ij_kotlin_packages_to_use_import_on_demand = unset diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8050bfed..7bd9c98e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -36,22 +36,22 @@ jobs: - name: Build Artifact run: ./gradlew build - - name: Set up Artifact Upload - run: | - echo "CORE_ARTIFACT_PATH=$(./gradlew :distributor-core:getArtifactPath -q)" >> $GITHUB_ENV - echo "KOTLIN_ARTIFACT_PATH=$(./gradlew :distributor-kotlin:getArtifactPath -q)" >> $GITHUB_ENV - - - name: Upload Artifact to Actions (Core) - uses: actions/upload-artifact@v3 - with: - name: ${{ github.event.repository.name }} - path: ${{ env.CORE_ARTIFACT_PATH }} - - - name: Upload Artifact to Actions (Kotlin) - uses: actions/upload-artifact@v3 - with: - name: ${{ github.event.repository.name }} - path: ${{ env.KOTLIN_ARTIFACT_PATH }} + # - name: Set up Artifact Upload + # run: | + # echo "CORE_ARTIFACT_PATH=$(./gradlew :distributor-core:getArtifactPath -q)" >> $GITHUB_ENV + # echo "KOTLIN_ARTIFACT_PATH=$(./gradlew :distributor-kotlin:getArtifactPath -q)" >> $GITHUB_ENV + + # - name: Upload Artifact to Actions (Core) + # uses: actions/upload-artifact@v3 + # with: + # name: ${{ github.event.repository.name }} + # path: ${{ env.CORE_ARTIFACT_PATH }} + + # - name: Upload Artifact to Actions (Kotlin) + # uses: actions/upload-artifact@v3 + # with: + # name: ${{ github.event.repository.name }} + # path: ${{ env.KOTLIN_ARTIFACT_PATH }} - name: Determine Status run: | @@ -70,23 +70,23 @@ jobs: ORG_GRADLE_PROJECT_signingKey: "${{ secrets.XPDUSTRY_MAVEN_SIGNING_KEY }}" ORG_GRADLE_PROJECT_signingPassword: "${{ secrets.XPDUSTRY_MAVEN_SIGNING_PASSWORD }}" - - name: Upload Artifact to Release (Core) - if: ${{ env.STATUS == 'release' && github.event_name == 'release' }} - uses: svenstaro/upload-release-action@v2 - with: - asset_name: distributor-core.jar - repo_token: ${{ secrets.GITHUB_TOKEN }} - file: ${{ env.CORE_ARTIFACT_PATH }} - tag: ${{ github.ref }} - - - name: Upload Artifact to Release (Kotlin) - if: ${{ env.STATUS == 'release' && github.event_name == 'release' }} - uses: svenstaro/upload-release-action@v2 - with: - asset_name: distributor-kotlin.jar - repo_token: ${{ secrets.GITHUB_TOKEN }} - file: ${{ env.KOTLIN_ARTIFACT_PATH }} - tag: ${{ github.ref }} + # - name: Upload Artifact to Release (Core) + # if: ${{ env.STATUS == 'release' && github.event_name == 'release' }} + # uses: svenstaro/upload-release-action@v2 + # with: + # asset_name: distributor-core.jar + # repo_token: ${{ secrets.GITHUB_TOKEN }} + # file: ${{ env.CORE_ARTIFACT_PATH }} + # tag: ${{ github.ref }} + + # - name: Upload Artifact to Release (Kotlin) + # if: ${{ env.STATUS == 'release' && github.event_name == 'release' }} + # uses: svenstaro/upload-release-action@v2 + # with: + # asset_name: distributor-kotlin.jar + # repo_token: ${{ secrets.GITHUB_TOKEN }} + # file: ${{ env.KOTLIN_ARTIFACT_PATH }} + # tag: ${{ github.ref }} - name: Update Changelog if: ${{ env.STATUS == 'release' && github.event_name == 'release' }} diff --git a/.spotless/distributor.importorder b/.spotless/distributor.importorder deleted file mode 100644 index 7c276dbd..00000000 --- a/.spotless/distributor.importorder +++ /dev/null @@ -1,3 +0,0 @@ -# Distributor import order -0= -1=\# diff --git a/LICENSE_HEADER.md b/LICENSE_HEADER.md index 005e0eac..2d8dcdf5 100644 --- a/LICENSE_HEADER.md +++ b/LICENSE_HEADER.md @@ -1,6 +1,6 @@ Distributor, a feature-rich framework for Mindustry plugins. -Copyright (C) 2023 Xpdustry +Copyright (C) 2024 Xpdustry This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/build.gradle.kts b/build.gradle.kts index f84cd7c8..735b83fa 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,7 +1,16 @@ plugins { - id("distributor.parent-conventions") + id("distributor4.parent-conventions") } -version = "3.3.1" + if (indraGit.headTag() == null) "-SNAPSHOT" else "" -group = "fr.xpdustry" +version = "4.0.0" + if (indraGit.headTag() == null) "-SNAPSHOT" else "" +group = "com.xpdustry" description = "The Mindustry plugin of ur dreams..." + +tasks { + spotlessCheck { + dependsOn(gradle.includedBuild("distributor-build-logic").task(":spotlessCheck")) + } + spotlessApply { + dependsOn(gradle.includedBuild("distributor-build-logic").task(":spotlessApply")) + } +} diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts deleted file mode 100644 index b2e34465..00000000 --- a/buildSrc/build.gradle.kts +++ /dev/null @@ -1,22 +0,0 @@ -plugins { - `kotlin-dsl` -} - -repositories { - gradlePluginPortal() -} - -dependencies { - implementation("net.kyori:indra-common:3.1.3") - implementation("net.kyori:indra-licenser-spotless:3.1.3") - implementation("fr.xpdustry:toxopid:3.2.0") - implementation("com.github.johnrengelman:shadow:8.1.1") - implementation("net.ltgt.gradle:gradle-errorprone-plugin:3.1.0") - implementation("com.diffplug.spotless:spotless-plugin-gradle:6.22.0") - implementation("com.github.ben-manes:gradle-versions-plugin:0.49.0") -} - -java { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} diff --git a/buildSrc/src/main/kotlin/distributor.mindustry-conventions.gradle.kts b/buildSrc/src/main/kotlin/distributor.mindustry-conventions.gradle.kts deleted file mode 100644 index f9af066d..00000000 --- a/buildSrc/src/main/kotlin/distributor.mindustry-conventions.gradle.kts +++ /dev/null @@ -1,46 +0,0 @@ -import fr.xpdustry.toxopid.dsl.mindustryDependencies - -plugins { - id("net.kyori.indra") - id("com.github.johnrengelman.shadow") - id("fr.xpdustry.toxopid") -} - -val metadata = fr.xpdustry.toxopid.spec.ModMetadata.fromJson(rootProject.file("plugin.json")) - -toxopid { - compileVersion.set("v" + metadata.minGameVersion) - platforms.add(fr.xpdustry.toxopid.spec.ModPlatform.HEADLESS) -} - -repositories { - mavenCentral() - maven("https://maven.xpdustry.com/mindustry") { - name = "xpdustry-mindustry" - mavenContent { releasesOnly() } - } -} - -dependencies { - mindustryDependencies() -} - -tasks.runMindustryClient { - mods.setFrom() -} - -tasks.register("getArtifactPath") { - doLast { println(tasks.shadowJar.get().archiveFile.get().toString()) } -} - -tasks.shadowJar { - archiveClassifier.set("plugin") - from(rootProject.file("LICENSE.md")) { - into("META-INF") - } - mergeServiceFiles() -} - -tasks.build { - dependsOn(tasks.shadowJar) -} diff --git a/buildSrc/src/main/kotlin/distributor.parent-conventions.gradle.kts b/buildSrc/src/main/kotlin/distributor.parent-conventions.gradle.kts deleted file mode 100644 index 12d516f2..00000000 --- a/buildSrc/src/main/kotlin/distributor.parent-conventions.gradle.kts +++ /dev/null @@ -1,4 +0,0 @@ -plugins { - id("net.kyori.indra.git") - id("com.github.ben-manes.versions") -} diff --git a/buildSrc/src/main/kotlin/distributor.publishing-conventions.gradle.kts b/buildSrc/src/main/kotlin/distributor.publishing-conventions.gradle.kts deleted file mode 100644 index 908ec12d..00000000 --- a/buildSrc/src/main/kotlin/distributor.publishing-conventions.gradle.kts +++ /dev/null @@ -1,38 +0,0 @@ -plugins { - id("net.kyori.indra.publishing") -} - -signing { - val signingKey: String? by project - val signingPassword: String? by project - useInMemoryPgpKeys(signingKey, signingPassword) -} - -indra { - publishReleasesTo("xpdustry", "https://maven.xpdustry.com/releases") - publishSnapshotsTo("xpdustry", "https://maven.xpdustry.com/snapshots") - - gpl3OnlyLicense() - - github("xpdustry", "distributor") { - ci(true) - issues(true) - scm(true) - } - - configurePublications { - pom { - organization { - name.set("Xpdustry") - url.set("https://www.xpdustry.com") - } - - developers { - developer { - id.set("Phinner") - timezone.set("Europe/Brussels") - } - } - } - } -} diff --git a/buildSrc/src/main/kotlin/extensions.kt b/buildSrc/src/main/kotlin/extensions.kt deleted file mode 100644 index b1fa544a..00000000 --- a/buildSrc/src/main/kotlin/extensions.kt +++ /dev/null @@ -1,3 +0,0 @@ -const val cloud = "1.8.4" -fun cloudCommandFramework(module: String) = - "cloud.commandframework:cloud-$module:$cloud" diff --git a/distributor-api/build.gradle.kts b/distributor-api/build.gradle.kts deleted file mode 100644 index 70b7cc26..00000000 --- a/distributor-api/build.gradle.kts +++ /dev/null @@ -1,24 +0,0 @@ -plugins { - id("distributor.base-conventions") - id("distributor.publishing-conventions") - id("distributor.mindustry-conventions") -} - -dependencies { - val apiGuardian = "1.1.2" - compileOnlyApi("org.apiguardian:apiguardian-api:$apiGuardian") - - val slf4j = "2.0.9" - api("org.slf4j:slf4j-api:$slf4j") - testRuntimeOnly("org.slf4j:slf4j-simple:$slf4j") - - val geantyref = "1.3.14" - api("io.leangen.geantyref:geantyref:$geantyref") - - api(cloudCommandFramework("core")) - api(cloudCommandFramework("annotations")) - api(cloudCommandFramework("tasks")) - api(cloudCommandFramework("services")) - - testImplementation("com.google.guava:guava-testlib:32.1.3-jre") -} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/Distributor.java b/distributor-api/src/main/java/fr/xpdustry/distributor/api/Distributor.java deleted file mode 100644 index 85e2d158..00000000 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/Distributor.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api; - -import fr.xpdustry.distributor.api.event.EventBus; -import fr.xpdustry.distributor.api.localization.MultiLocalizationSource; -import fr.xpdustry.distributor.api.scheduler.PluginScheduler; -import fr.xpdustry.distributor.api.security.PlayerValidator; -import fr.xpdustry.distributor.api.security.permission.PermissionService; - -/** - * The main entry point of the Distributor API. - */ -public interface Distributor { - - /** - * Returns the global localization source instance. - */ - MultiLocalizationSource getGlobalLocalizationSource(); - - /** - * Returns the plugin scheduler. - */ - PluginScheduler getPluginScheduler(); - - /** - * Returns the player validator. - */ - PlayerValidator getPlayerValidator(); - - /** - * Returns the permission service. - */ - PermissionService getPermissionService(); - - /** - * Returns the event bus of this server. - */ - EventBus getEventBus(); -} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/DistributorInitializationException.java b/distributor-api/src/main/java/fr/xpdustry/distributor/api/DistributorInitializationException.java deleted file mode 100644 index 9f121ea8..00000000 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/DistributorInitializationException.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api; - -import java.io.Serial; - -/** - * This exception is thrown when the distributor fails to initialize. - */ -public final class DistributorInitializationException extends RuntimeException { - - @Serial - private static final long serialVersionUID = 7989381683086211607L; - - DistributorInitializationException(final String message) { - super(message); - } -} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/DistributorProvider.java b/distributor-api/src/main/java/fr/xpdustry/distributor/api/DistributorProvider.java deleted file mode 100644 index f78bacef..00000000 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/DistributorProvider.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api; - -import org.checkerframework.checker.nullness.qual.Nullable; - -/** - * A holder for the global {@link Distributor} instance. - */ -public final class DistributorProvider { - - private static @Nullable Distributor instance = null; - - private DistributorProvider() {} - - /** - * Returns the global {@link Distributor} instance. - * @throws DistributorInitializationException if the API hasn't been initialized yet - */ - public static Distributor get() { - if (DistributorProvider.instance == null) { - throw new DistributorInitializationException("The API hasn't been initialized yet."); - } - return DistributorProvider.instance; - } - - /** - * Sets the global {@link Distributor} instance. - * @throws DistributorInitializationException if the API has already been initialized - */ - public static void set(final Distributor distributor) { - if (DistributorProvider.instance != null) { - throw new DistributorInitializationException("The API has already been initialized."); - } - DistributorProvider.instance = distributor; - } - - /** - * Clears the global {@link Distributor} instance. - * @throws DistributorInitializationException if the API hasn't been initialized yet - */ - public static void clear() { - if (DistributorProvider.instance != null) { - DistributorProvider.instance = null; - } else { - throw new DistributorInitializationException("The API hasn't been initialized yet."); - } - } - - /** - * Returns whether the API has been initialized. - */ - public static boolean isInitialized() { - return DistributorProvider.instance != null; - } -} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/command/ArcCommand.java b/distributor-api/src/main/java/fr/xpdustry/distributor/api/command/ArcCommand.java deleted file mode 100644 index a530753c..00000000 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/command/ArcCommand.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.command; - -import arc.util.CommandHandler; -import cloud.commandframework.captions.Caption; -import cloud.commandframework.captions.CaptionVariable; -import cloud.commandframework.exceptions.ArgumentParseException; -import cloud.commandframework.exceptions.CommandExecutionException; -import cloud.commandframework.exceptions.InvalidSyntaxException; -import cloud.commandframework.exceptions.NoPermissionException; -import cloud.commandframework.exceptions.NoSuchCommandException; -import cloud.commandframework.exceptions.parsing.ParserException; -import fr.xpdustry.distributor.api.command.sender.CommandSender; -import java.util.Objects; -import mindustry.gen.Player; -import org.checkerframework.checker.nullness.qual.Nullable; - -/** - * This special command class delegates its call to its command manager. - */ -public final class ArcCommand extends CommandHandler.Command { - - private final ArcCommandManager manager; - private final boolean alias; - private final boolean prefixed; - private final String realName; - - ArcCommand( - final String name, - final String description, - final ArcCommandManager manager, - final boolean alias, - final boolean prefixed) { - super( - (prefixed ? manager.getPlugin().getDescriptor().getName() + ":" : "") + name, - "[args...]", - description, - new ArcCommandRunner<>(name, manager)); - this.manager = manager; - this.alias = alias; - this.prefixed = prefixed; - this.realName = name; - } - - /** - * Returns the real name of the command, without the prefix. - */ - public String getRealName() { - return this.realName; - } - - /** - * Returns the command manager that created this command. - */ - public ArcCommandManager getManager() { - return this.manager; - } - - /** - * Returns whether this command is an alias. - */ - public boolean isAlias() { - return this.alias; - } - - /** - * Returns whether this command is prefixed with the plugin name. - */ - public boolean isPrefixed() { - return this.prefixed; - } - - private static final class ArcCommandRunner implements CommandHandler.CommandRunner { - - private final String name; - private final ArcCommandManager manager; - - private ArcCommandRunner(final String name, final ArcCommandManager manager) { - this.name = name; - this.manager = manager; - } - - @SuppressWarnings("FutureReturnValueIgnored") - @Override - public void accept(final String[] args, final @Nullable Player player) { - final var sender = this.manager - .getCommandSenderMapper() - .apply(player != null ? CommandSender.player(player) : CommandSender.console()); - - final var input = new StringBuilder(this.name); - for (final var arg : args) { - input.append(' ').append(arg); - } - - this.manager.executeCommand(sender, input.toString()).whenComplete((result, throwable) -> { - if (throwable == null) { - return; - } - if (throwable instanceof final ArgumentParseException t) { - // NullAway doesn't understand that the cause is not null - throwable = Objects.requireNonNull(t.getCause()); - } - - if (throwable instanceof final InvalidSyntaxException t) { - this.manager.handleException( - sender, - InvalidSyntaxException.class, - t, - (s, e) -> this.sendException( - sender, - ArcCaptionKeys.COMMAND_INVALID_SYNTAX, - CaptionVariable.of("syntax", e.getCorrectSyntax()))); - } else if (throwable instanceof final NoPermissionException t) { - this.manager.handleException( - sender, - NoPermissionException.class, - t, - (s, e) -> this.sendException( - sender, - ArcCaptionKeys.COMMAND_INVALID_PERMISSION, - CaptionVariable.of("permission", e.getMissingPermission()))); - } else if (throwable instanceof final NoSuchCommandException t) { - this.manager.handleException( - sender, - NoSuchCommandException.class, - t, - (s, e) -> this.sendException( - sender, - ArcCaptionKeys.COMMAND_FAILURE_NO_SUCH_COMMAND, - CaptionVariable.of("command", e.getSuppliedCommand()))); - } else if (throwable instanceof final ParserException t) { - this.manager.handleException( - sender, - ParserException.class, - t, - (s, e) -> this.sendException(sender, e.errorCaption(), e.captionVariables())); - } else if (throwable instanceof final CommandExecutionException t) { - this.manager.handleException(sender, CommandExecutionException.class, t, (s, e) -> { - @SuppressWarnings("NullAway") - // TODO Open a pull request to annotate CommandExecutionException#getCause NonNull - final var variable = CaptionVariable.of("message", this.getErrorMessage(e.getCause())); - this.sendException(sender, ArcCaptionKeys.COMMAND_FAILURE_EXECUTION, variable); - }); - this.manager - .getPlugin() - .getLogger() - .error("An error occurred while executing a command.", throwable); - } else { - this.sendException( - sender, - ArcCaptionKeys.COMMAND_FAILURE_EXECUTION, - CaptionVariable.of("message", this.getErrorMessage(throwable))); - this.manager - .getPlugin() - .getLogger() - .error("An unexpected error occurred while executing a command.", throwable); - } - }); - } - - private void sendException(final C sender, final Caption caption, final CaptionVariable... variables) { - final var message = this.manager.captionRegistry().getCaption(caption, sender); - final var formatted = - this.manager.captionVariableReplacementHandler().replaceVariables(message, variables); - this.manager.getBackwardsCommandSenderMapper().apply(sender).sendWarning(formatted); - } - - private String getErrorMessage(final Throwable throwable) { - return throwable.getMessage() != null ? throwable.getMessage() : "none"; - } - } -} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/command/ArcCommandManager.java b/distributor-api/src/main/java/fr/xpdustry/distributor/api/command/ArcCommandManager.java deleted file mode 100644 index 5f97f7da..00000000 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/command/ArcCommandManager.java +++ /dev/null @@ -1,286 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.command; - -import arc.util.CommandHandler; -import cloud.commandframework.CloudCapability; -import cloud.commandframework.CommandManager; -import cloud.commandframework.annotations.AnnotationParser; -import cloud.commandframework.arguments.parser.ParserParameters; -import cloud.commandframework.arguments.parser.StandardParameters; -import cloud.commandframework.execution.AsynchronousCommandExecutionCoordinator; -import cloud.commandframework.execution.CommandExecutionCoordinator; -import cloud.commandframework.internal.CommandRegistrationHandler; -import cloud.commandframework.meta.CommandMeta; -import fr.xpdustry.distributor.api.DistributorProvider; -import fr.xpdustry.distributor.api.command.argument.PlayerArgument; -import fr.xpdustry.distributor.api.command.argument.PlayerInfoArgument; -import fr.xpdustry.distributor.api.command.argument.TeamArgument; -import fr.xpdustry.distributor.api.command.argument.TeamArgument.TeamMode; -import fr.xpdustry.distributor.api.command.sender.CommandSender; -import fr.xpdustry.distributor.api.command.specifier.AllTeams; -import fr.xpdustry.distributor.api.plugin.MindustryPlugin; -import fr.xpdustry.distributor.api.plugin.PluginAware; -import fr.xpdustry.distributor.api.scheduler.PluginTaskRecipe; -import fr.xpdustry.distributor.api.util.MUUID; -import io.leangen.geantyref.TypeToken; -import java.text.MessageFormat; -import java.util.function.Function; -import mindustry.game.Team; -import mindustry.gen.Player; -import mindustry.net.Administration; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; - -/** - * Command manager implementation for Mindustry. Read the cloud documentation for more information. - * - *
 {@code
- *      public final class MyPlugin extends AbstractMindustryPlugin {
- *          private final ArcCommandManager manager = ArcCommandManager.standard(this);
- *          @Override
- *          public void onClientCommandsRegistration(final CommandHandler handler) {
- *              manager.initialize(handler);
- *              manager.command(manager.commandBuilder("echo")
- *                  .meta(CommandMeta.DESCRIPTION, "Print something")
- *                  .argument(StringArgument.of("message"))
- *                  .handler(context -> {
- *                      final String message = context.get("message");
- *                      context.getSender().sendMessage(message);
- *                  }));
- *          }
- *      }
- * } 
- * - * @param the command sender type - */ -public class ArcCommandManager extends CommandManager implements PluginAware { - - /** - * The owning plugin of the command. - */ - public static final CommandMeta.Key PLUGIN = CommandMeta.Key.of(String.class, "distributor-core:plugin"); - - private final MindustryPlugin plugin; - private final Function commandSenderMapper; - private final Function backwardsCommandSenderMapper; - - private @MonotonicNonNull CommandHandler handler; - - /** - * Creates a new {@link ArcCommandManager}. - * - * @param plugin the owning plugin - * @param commandSenderMapper the function that will convert the {@link CommandSender} to the command sender type of - * your choice - * @param backwardsCommandSenderMapper the function that will convert your command sender - * type to {@link CommandSender} - * @param async whether the command manager should execute commands asynchronously - */ - public ArcCommandManager( - final MindustryPlugin plugin, - final Function commandSenderMapper, - final Function backwardsCommandSenderMapper, - final boolean async) { - super( - async - ? AsynchronousCommandExecutionCoordinator.builder() - .withAsynchronousParsing() - .withExecutor(runnable -> DistributorProvider.get() - .getPluginScheduler() - .scheduleAsync(plugin) - .execute(runnable)) - .build() - : CommandExecutionCoordinator.simpleCoordinator(), - CommandRegistrationHandler.nullCommandRegistrationHandler()); - - this.plugin = plugin; - this.commandSenderMapper = commandSenderMapper; - this.backwardsCommandSenderMapper = backwardsCommandSenderMapper; - - this.registerCapability(CloudCapability.StandardCapabilities.ROOT_COMMAND_DELETION); - - this.captionRegistry((caption, sender) -> { - final var source = DistributorProvider.get().getGlobalLocalizationSource(); - final var locale = - this.getBackwardsCommandSenderMapper().apply(sender).getLocale(); - final var format = source.localize(caption.getKey(), locale); - return format != null ? format.toPattern() : "???" + caption.getKey() + "???"; - }); - - this.captionVariableReplacementHandler((format, variables) -> { - final var arguments = new Object[variables.length]; - for (int i = 0; i < variables.length; i++) { - arguments[i] = variables[i].getValue(); - } - try { - return MessageFormat.format(format, arguments); - } catch (final IllegalArgumentException e) { - this.plugin.getLogger().error("Failed to format {}.", format, e); - return "???" + format + "???"; - } - }); - - this.parserRegistry() - .registerAnnotationMapper( - AllTeams.class, - (annotation, typeToken) -> - ParserParameters.single(ArcParserParameters.TEAM_MODE, TeamMode.ALL)); - - this.parserRegistry() - .registerParserSupplier(TypeToken.get(Player.class), params -> new PlayerArgument.PlayerParser<>()); - - this.parserRegistry() - .registerParserSupplier( - TypeToken.get(Administration.PlayerInfo.class), - params -> new PlayerInfoArgument.PlayerInfoParser<>()); - - this.parserRegistry() - .registerParserSupplier( - TypeToken.get(Team.class), - params -> new TeamArgument.TeamParser<>( - params.get(ArcParserParameters.TEAM_MODE, TeamMode.BASE))); - } - - /** - * Creates a simple {@link ArcCommandManager} with {@link CommandSender} as the command sender type. - */ - public static ArcCommandManager standard(final MindustryPlugin plugin) { - return new ArcCommandManager<>(plugin, Function.identity(), Function.identity(), false); - } - - /** - * Creates a simple async {@link ArcCommandManager} with {@link CommandSender} as the command sender type. - */ - public static ArcCommandManager standardAsync(final MindustryPlugin plugin) { - return new ArcCommandManager<>(plugin, Function.identity(), Function.identity(), true); - } - - /** - * Creates a simple {@link ArcCommandManager} with {@link Player} as the command sender type. - *
- * Warning: this will crash the server if it used with the console command handler. - */ - public static ArcCommandManager player(final MindustryPlugin plugin) { - return new ArcCommandManager<>(plugin, CommandSender::getPlayer, CommandSender::player, false); - } - - /** - * Creates a simple async {@link ArcCommandManager} with {@link Player} as the command sender type. - *
- * Warning: this will crash the server if it used with the console command handler. - */ - public static ArcCommandManager playerAsync(final MindustryPlugin plugin) { - return new ArcCommandManager<>(plugin, CommandSender::getPlayer, CommandSender::player, true); - } - - /** - * Initializes the command manager with it's backing command handler. - * - * @param handler the backing command handler - */ - public final void initialize(final CommandHandler handler) { - this.commandRegistrationHandler(new ArcRegistrationHandler<>(this, handler)); - this.transitionOrThrow(RegistrationState.BEFORE_REGISTRATION, RegistrationState.REGISTERING); - this.handler = handler; - this.parameterInjectorRegistry() - .registerInjector(CommandHandler.class, (ctx, annotation) -> ArcCommandManager.this.handler); - } - - /** - * Returns the command sender mapper of this command manager. - */ - public final Function getCommandSenderMapper() { - return this.commandSenderMapper; - } - - /** - * Returns the backwards command sender mapper of this command manager. - */ - public final Function getBackwardsCommandSenderMapper() { - return this.backwardsCommandSenderMapper; - } - - /** - * A shortcut method for creating an AnnotationParser. - * - * @param type the type token of the command sender - * @return the created annotation parser - */ - public AnnotationParser createAnnotationParser(final TypeToken type) { - return new AnnotationParser<>(this, type, params -> { - final var builder = CommandMeta.simple().with(this.createDefaultCommandMeta()); - if (params.has(StandardParameters.DESCRIPTION)) { - builder.with(CommandMeta.DESCRIPTION, params.get(StandardParameters.DESCRIPTION, "")); - } - return builder.build(); - }); - } - - /** - * A shortcut method for creating an AnnotationParser. - * - * @param type the type of the command sender - * @return the created annotation parser - */ - public final AnnotationParser createAnnotationParser(final Class type) { - return this.createAnnotationParser(TypeToken.get(type)); - } - - /** - * A shortcut method for creating recipe commands, with steps executing synchronously or asynchronously. - * - * @param value the initial value of the recipe, usually a {@link cloud.commandframework.context.CommandContext - * command context} - * @return the created recipe - * @deprecated see {@link fr.xpdustry.distributor.api.scheduler.PluginScheduler#recipe(MindustryPlugin, Object)} - */ - @SuppressWarnings("removal") - @Deprecated - public final PluginTaskRecipe recipe(final V value) { - return DistributorProvider.get().getPluginScheduler().recipe(this.plugin, value); - } - - @SuppressWarnings("NullableProblems") - @Override - public boolean hasPermission(final C sender, final String permission) { - if (permission.isEmpty()) { - return true; - } - final var caller = this.backwardsCommandSenderMapper.apply(sender); - if (caller.isConsole()) { - return true; - } - return DistributorProvider.get() - .getPermissionService() - .getPlayerPermission(MUUID.of(caller.getPlayer()), permission) - .asBoolean(); - } - - @Override - public CommandMeta createDefaultCommandMeta() { - return CommandMeta.simple() - .with(PLUGIN, this.plugin.getDescriptor().getName()) - .build(); - } - - @Override - public final MindustryPlugin getPlugin() { - return this.plugin; - } -} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/command/ArcParserParameters.java b/distributor-api/src/main/java/fr/xpdustry/distributor/api/command/ArcParserParameters.java deleted file mode 100644 index 362b00dc..00000000 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/command/ArcParserParameters.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.command; - -import cloud.commandframework.arguments.parser.ParserParameter; -import fr.xpdustry.distributor.api.command.argument.TeamArgument.TeamMode; -import io.leangen.geantyref.TypeToken; - -/** - * A collection of {@link ParserParameter} used by Distributor to resolve Mindustry types in the - * {@link cloud.commandframework.arguments.parser.ParserRegistry}. - */ -public final class ArcParserParameters { - - /** - * Whether a {@link mindustry.game.Team} argument should include all the teams or only the base ones. - */ - public static final ParserParameter TEAM_MODE = - new ParserParameter<>("team_mode", TypeToken.get(TeamMode.class)); - - private ArcParserParameters() {} -} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/command/argument/PlayerArgument.java b/distributor-api/src/main/java/fr/xpdustry/distributor/api/command/argument/PlayerArgument.java deleted file mode 100644 index 40a23f85..00000000 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/command/argument/PlayerArgument.java +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.command.argument; - -import cloud.commandframework.ArgumentDescription; -import cloud.commandframework.arguments.CommandArgument; -import cloud.commandframework.arguments.parser.ArgumentParseResult; -import cloud.commandframework.arguments.parser.ArgumentParser; -import cloud.commandframework.captions.Caption; -import cloud.commandframework.captions.CaptionVariable; -import cloud.commandframework.context.CommandContext; -import cloud.commandframework.exceptions.parsing.NoInputProvidedException; -import cloud.commandframework.exceptions.parsing.ParserException; -import fr.xpdustry.distributor.api.command.ArcCaptionKeys; -import fr.xpdustry.distributor.api.util.Players; -import java.io.Serial; -import java.util.List; -import java.util.Queue; -import java.util.function.BiFunction; -import mindustry.gen.Player; -import org.checkerframework.checker.nullness.qual.Nullable; - -/** - * A command argument for an online {@link Player}. - * - * @param the command sender type - */ -public final class PlayerArgument extends CommandArgument { - - private PlayerArgument( - final boolean required, - final String name, - final String defaultValue, - final @Nullable BiFunction, String, List> suggestionsProvider, - final ArgumentDescription defaultDescription) { - super( - required, - name, - new PlayerParser<>(), - defaultValue, - Player.class, - suggestionsProvider, - defaultDescription); - } - - /** - * Creates a new {@link Builder}. - * - * @param name the name of the argument - * @param the command sender type - * @return the created builder - */ - public static Builder builder(final String name) { - return new Builder<>(name); - } - - /** - * Creates a new required {@link PlayerArgument}. - * - * @param name the name of the argument - * @param the command sender type - * @return the created builder - */ - public static PlayerArgument of(final String name) { - return PlayerArgument.builder(name).asRequired().build(); - } - - /** - * Creates a new optional {@link PlayerArgument}. - * - * @param name the name of the argument - * @param the command sender type - * @return the created builder - */ - public static PlayerArgument optional(final String name) { - return PlayerArgument.builder(name).asOptional().build(); - } - - /** - * The internal builder class of {@link PlayerArgument}. - * - * @param the command sender type - */ - public static final class Builder extends CommandArgument.TypedBuilder> { - - private Builder(final String name) { - super(Player.class, name); - } - - /** - * Construct a new {@link PlayerArgument}. - * - * @return the constructed player argument - */ - @Override - public PlayerArgument build() { - return new PlayerArgument<>( - this.isRequired(), - this.getName(), - this.getDefaultValue(), - this.getSuggestionsProvider(), - this.getDefaultDescription()); - } - } - - /** - * An argument parser that outputs an online {@link Player} in the current server. - * - * @param the command sender type - */ - public static final class PlayerParser implements ArgumentParser { - - @Override - public ArgumentParseResult parse(final CommandContext ctx, final Queue inputQueue) { - final var input = inputQueue.peek(); - if (input == null) { - return ArgumentParseResult.failure(new NoInputProvidedException(PlayerParser.class, ctx)); - } - - final var players = Players.findPlayers(input, true); - - if (players.isEmpty()) { - return ArgumentParseResult.failure(new PlayerNotFoundException(input, ctx)); - } else if (players.size() > 1) { - return ArgumentParseResult.failure(new TooManyPlayersFoundException(input, ctx)); - } else { - inputQueue.remove(); - return ArgumentParseResult.success(players.get(0)); - } - } - - @Override - public List suggestions(final CommandContext commandContext, final String input) { - return Players.findPlayers(input, true).stream() - .map(Player::plainName) - .toList(); - } - - @Override - public boolean isContextFree() { - return true; - } - } - - /** - * An exception thrown when a parsing error occurs while searching for a player. - */ - public static class PlayerParseException extends ParserException { - - @Serial - private static final long serialVersionUID = 3264229396134848993L; - - private final String input; - - /** - * Creates a new {@link PlayerParseException}. - * - * @param input the input string - * @param ctx the command context - * @param caption the error caption of this exception - */ - public PlayerParseException(final String input, final CommandContext ctx, final Caption caption) { - super(PlayerParser.class, ctx, caption, CaptionVariable.of("input", input)); - this.input = input; - } - - /** - * Returns the input string. - */ - public final String getInput() { - return this.input; - } - } - - /** - * An exception thrown when too many players are found for the given input. - */ - public static final class TooManyPlayersFoundException extends PlayerParseException { - - @Serial - private static final long serialVersionUID = 2964533701700707264L; - - /** - * Creates a new {@link TooManyPlayersFoundException}. - * - * @param input the input string - * @param ctx the command context - */ - public TooManyPlayersFoundException(final String input, final CommandContext ctx) { - super(input, ctx, ArcCaptionKeys.ARGUMENT_PARSE_FAILURE_PLAYER_TOO_MANY); - } - } - - /** - * An exception thrown when no player was found for the given input. - */ - public static final class PlayerNotFoundException extends PlayerParseException { - - @Serial - private static final long serialVersionUID = 4683487234146844501L; - - /** - * Creates a new {@link PlayerNotFoundException}. - * - * @param input the input string - * @param ctx the command context - */ - public PlayerNotFoundException(final String input, final CommandContext ctx) { - super(input, ctx, ArcCaptionKeys.ARGUMENT_PARSE_FAILURE_PLAYER_NOT_FOUND); - } - } -} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/command/argument/PlayerInfoArgument.java b/distributor-api/src/main/java/fr/xpdustry/distributor/api/command/argument/PlayerInfoArgument.java deleted file mode 100644 index 0235445f..00000000 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/command/argument/PlayerInfoArgument.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.command.argument; - -import cloud.commandframework.ArgumentDescription; -import cloud.commandframework.arguments.CommandArgument; -import cloud.commandframework.arguments.parser.ArgumentParseResult; -import cloud.commandframework.arguments.parser.ArgumentParser; -import cloud.commandframework.context.CommandContext; -import cloud.commandframework.exceptions.parsing.NoInputProvidedException; -import fr.xpdustry.distributor.api.command.argument.PlayerArgument.PlayerNotFoundException; -import fr.xpdustry.distributor.api.command.argument.PlayerArgument.TooManyPlayersFoundException; -import fr.xpdustry.distributor.api.util.MUUID; -import fr.xpdustry.distributor.api.util.Players; -import java.util.List; -import java.util.Queue; -import java.util.function.BiFunction; -import mindustry.Vars; -import mindustry.gen.Player; -import mindustry.net.Administration.PlayerInfo; -import org.checkerframework.checker.nullness.qual.Nullable; - -/** - * A command argument for an offline player, always guaranteed to return if an uuid is provided. - * Can also search in online players. - * - * @param the command sender type - */ -public final class PlayerInfoArgument extends CommandArgument { - - public PlayerInfoArgument( - final boolean required, - final String name, - final String defaultValue, - final @Nullable BiFunction, String, List> suggestionsProvider, - final ArgumentDescription defaultDescription) { - super( - required, - name, - new PlayerInfoParser<>(), - defaultValue, - PlayerInfo.class, - suggestionsProvider, - defaultDescription); - } - - /** - * Creates a new {@link Builder}. - * - * @param name the name of the argument - * @param the command sender type - * @return the created builder - */ - public static Builder builder(final String name) { - return new Builder<>(name); - } - - /** - * Creates a new required {@link PlayerInfoArgument}. - * - * @param name the name of the argument - * @param the command sender type - * @return the created argument - */ - public static PlayerInfoArgument of(final String name) { - return PlayerInfoArgument.builder(name).asRequired().build(); - } - - /** - * Creates a new optional {@link PlayerInfoArgument}. - * - * @param name the name of the argument - * @param the command sender type - * @return the created argument - */ - public static PlayerInfoArgument optional(final String name) { - return PlayerInfoArgument.builder(name).asOptional().build(); - } - - /** - * The internal builder class of {@link PlayerInfoArgument}. - * - * @param the command sender type - */ - public static final class Builder extends CommandArgument.TypedBuilder> { - - private Builder(final String name) { - super(PlayerInfo.class, name); - } - - /** - * Construct a new {@link PlayerInfoArgument}. - * - * @return the constructed argument - */ - @Override - public PlayerInfoArgument build() { - return new PlayerInfoArgument<>( - this.isRequired(), - this.getName(), - this.getDefaultValue(), - this.getSuggestionsProvider(), - this.getDefaultDescription()); - } - } - - /** - * An argument parser that outputs a {@link PlayerInfo} from an online {@link Player} or an offline player in - * this server. - * - * @param the command sender type - */ - public static final class PlayerInfoParser implements ArgumentParser { - - @Override - public ArgumentParseResult parse(final CommandContext ctx, final Queue inputQueue) { - final var input = inputQueue.peek(); - if (input == null) { - return ArgumentParseResult.failure(new NoInputProvidedException(PlayerInfoParser.class, ctx)); - } - - if (MUUID.isUuid(input)) { - inputQueue.remove(); - return ArgumentParseResult.success(Vars.netServer.admins.getInfo(input)); - } - - final var players = Players.findPlayers(input); - - if (players.isEmpty()) { - return ArgumentParseResult.failure(new PlayerNotFoundException(input, ctx)); - } else if (players.size() > 1) { - return ArgumentParseResult.failure(new TooManyPlayersFoundException(input, ctx)); - } else { - inputQueue.remove(); - return ArgumentParseResult.success(players.get(0).getInfo()); - } - } - - @Override - public List suggestions(final CommandContext commandContext, final String input) { - return Players.findPlayers(input, true).stream() - .map(Player::plainName) - .toList(); - } - } -} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/command/argument/TeamArgument.java b/distributor-api/src/main/java/fr/xpdustry/distributor/api/command/argument/TeamArgument.java deleted file mode 100644 index adc22225..00000000 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/command/argument/TeamArgument.java +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.command.argument; - -import cloud.commandframework.ArgumentDescription; -import cloud.commandframework.arguments.CommandArgument; -import cloud.commandframework.arguments.parser.ArgumentParseResult; -import cloud.commandframework.arguments.parser.ArgumentParser; -import cloud.commandframework.captions.CaptionVariable; -import cloud.commandframework.context.CommandContext; -import cloud.commandframework.exceptions.parsing.NoInputProvidedException; -import cloud.commandframework.exceptions.parsing.ParserException; -import fr.xpdustry.distributor.api.command.ArcCaptionKeys; -import java.io.Serial; -import java.util.Arrays; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Queue; -import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.stream.Collectors; -import mindustry.game.Team; -import org.checkerframework.checker.nullness.qual.Nullable; - -/** - * A command argument for a {@link Team}. - * - * @param the command sender type - */ -public final class TeamArgument extends CommandArgument { - - private TeamArgument( - final boolean required, - final String name, - final String defaultValue, - final @Nullable BiFunction, String, List> suggestionsProvider, - final ArgumentDescription defaultDescription, - final TeamMode teamMode) { - super( - required, - name, - new TeamParser<>(teamMode), - defaultValue, - Team.class, - suggestionsProvider, - defaultDescription); - } - - /** - * Creates a new {@link TeamArgument.Builder}. - * - * @param name the name of the argument - * @param the command sender type - * @return the created builder - */ - public static TeamArgument.Builder builder(final String name) { - return new TeamArgument.Builder<>(name); - } - - /** - * Creates a new required {@link TeamArgument}. - * - * @param name the name of the argument - * @param the command sender type - * @return the created builder - */ - public static TeamArgument of(final String name) { - return TeamArgument.builder(name).asRequired().build(); - } - - /** - * Creates a new optional {@link TeamArgument}. - * - * @param name the name of the argument - * @param the command sender type - * @return the created builder - */ - public static TeamArgument optional(final String name) { - return TeamArgument.builder(name).asOptional().build(); - } - - /** - * Creates a new required {@link TeamArgument} with the {@link TeamMode#BASE} mode, which means that only the 6 base - * teams can be used. - * - * @param name the name of the argument - * @param the command sender type - * @return the created builder - */ - public static TeamArgument base(final String name) { - return new TeamArgument.Builder(name).withTeamMode(TeamMode.BASE).build(); - } - - /** - * Creates a new required {@link TeamArgument} with the {@link TeamMode#ALL} mode, which means that all 256 teams - * can be used. - * - * @param name the name of the argument - * @param the command sender type - * @return the created builder - */ - public static TeamArgument all(final String name) { - return new TeamArgument.Builder(name).withTeamMode(TeamMode.ALL).build(); - } - - /** - * The parsing mode for a {@link TeamArgument}. - */ - public enum TeamMode { - /** - * Only the 6 base teams can be used. - * - * @see Team#baseTeams - */ - BASE, - /** - * All 256 teams can be used. - * - * @see Team#all - */ - ALL - } - - /** - * The internal builder class of {@link TeamArgument}. - * - * @param the command sender type - */ - public static final class Builder extends CommandArgument.TypedBuilder> { - - private TeamMode teamMode = TeamMode.BASE; - - private Builder(final String name) { - super(Team.class, name); - } - - /** - * Sets the parsing mode for the {@link TeamArgument}. - * - * @param teamMode the parsing mode - * @return this builder - */ - public Builder withTeamMode(final TeamMode teamMode) { - this.teamMode = teamMode; - return this; - } - - /** - * Construct a new {@link TeamArgument}. - * - * @return the constructed team argument - */ - @Override - public TeamArgument build() { - return new TeamArgument<>( - this.isRequired(), - this.getName(), - this.getDefaultValue(), - this.getSuggestionsProvider(), - this.getDefaultDescription(), - this.teamMode); - } - } - - /** - * An argument parser that outputs a {@link Team}. - * - * @param the command sender type - */ - public static final class TeamParser implements ArgumentParser { - - private static final Map BASE_TEAMS = Arrays.stream(Team.baseTeams) - .collect(Collectors.toUnmodifiableMap(t -> t.name.toLowerCase(Locale.ROOT), Function.identity())); - - private static final Map ALL_TEAMS = Arrays.stream(Team.all) - .collect(Collectors.toUnmodifiableMap(t -> t.name.toLowerCase(Locale.ROOT), Function.identity())); - - private final TeamMode teamMode; - - public TeamParser(final TeamMode teamMode) { - this.teamMode = teamMode; - } - - @Override - public ArgumentParseResult parse(final CommandContext ctx, final Queue inputQueue) { - final var input = inputQueue.peek(); - if (input == null) { - return ArgumentParseResult.failure(new NoInputProvidedException(TeamArgument.TeamParser.class, ctx)); - } - - final var name = input.toLowerCase(Locale.ROOT); - if (this.getTeamIndex().containsKey(name)) { - inputQueue.remove(); - return ArgumentParseResult.success(this.getTeamIndex().get(name)); - } else { - return ArgumentParseResult.failure(new TeamParseException(input, ctx, this.teamMode)); - } - } - - @Override - public List suggestions(final CommandContext ctx, final String input) { - final var name = input.toLowerCase(Locale.ROOT); - return BASE_TEAMS.keySet().stream() - .filter(t -> t.startsWith(name)) - .sorted() - .toList(); - } - - @Override - public boolean isContextFree() { - return true; - } - - private Map getTeamIndex() { - return this.teamMode == TeamMode.ALL ? ALL_TEAMS : BASE_TEAMS; - } - } - - /** - * Exception thrown when a team cannot be found for the given input and {@link TeamMode}. - */ - public static final class TeamParseException extends ParserException { - - @Serial - private static final long serialVersionUID = -2213430000642727576L; - - private final String input; - private final TeamMode teamMode; - - /** - * Creates a new {@link TeamParseException}. - * - * @param input the input string - * @param ctx the command context - * @param teamMode the team mode - */ - public TeamParseException(final String input, final CommandContext ctx, final TeamMode teamMode) { - super( - PlayerArgument.PlayerParser.class, - ctx, - ArcCaptionKeys.ARGUMENT_PARSE_FAILURE_TEAM, - CaptionVariable.of("input", input), - CaptionVariable.of("teamMode", teamMode.name())); - this.input = input; - this.teamMode = teamMode; - } - - public String getInput() { - return this.input; - } - - public TeamMode getTeamMode() { - return this.teamMode; - } - } -} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/command/sender/CommandSender.java b/distributor-api/src/main/java/fr/xpdustry/distributor/api/command/sender/CommandSender.java deleted file mode 100644 index 3338019f..00000000 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/command/sender/CommandSender.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.command.sender; - -import fr.xpdustry.distributor.api.DistributorProvider; -import java.util.Locale; -import mindustry.gen.Player; - -/** - * Represents an entity that can send commands. - */ -public interface CommandSender { - - /** - * Wraps a player into a command sender. - * - * @param player the player to wrap - * @return the player command sender - */ - static CommandSender player(final Player player) { - return new PlayerCommandSender(player); - } - - /** - * Returns the console command sender of this server. - */ - static CommandSender console() { - return ConsoleCommandSender.INSTANCE; - } - - /** - * Sends a simple message to the sender. - * - * @param content the message to send - */ - void sendMessage(final String content); - - /** - * Sends a localized message to the sender. - *
- * Note: if the string is not found, the key will be sent instead. Such as {@code ???key???}. - * - * @param key the key of the message to send - * @param args the arguments to format the message with - */ - default void sendLocalizedMessage(final String key, final Object... args) { - final var format = - DistributorProvider.get().getGlobalLocalizationSource().localize(key, this.getLocale()); - this.sendMessage(format == null ? "???" + key + " ???" : format.format(args)); - } - - /** - * Sends a warning message to the sender. - * - * @param content the warning to send - */ - void sendWarning(final String content); - - /** - * Sends a localized warning message to the sender. - *
- * Note: if the key is not found, the key will be sent instead. Such as {@code ???key???}. - * - * @param key the key of the warning to send - * @param args the arguments to format the warning with - */ - default void sendLocalizedWarning(final String key, final Object... args) { - final var format = - DistributorProvider.get().getGlobalLocalizationSource().localize(key, this.getLocale()); - this.sendWarning(format == null ? "???" + key + " ???" : format.format(args)); - } - - /** - * Returns the locale of this sender. - */ - Locale getLocale(); - - /** - * @return the player proxied by this sender if it's a player. - * @throws UnsupportedOperationException if this sender is not a player. - */ - Player getPlayer(); - - /** - * Returns whether this sender is a player. - */ - boolean isPlayer(); - - /** - * Returns whether this sender is the console. - */ - boolean isConsole(); -} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/command/sender/ConsoleCommandSender.java b/distributor-api/src/main/java/fr/xpdustry/distributor/api/command/sender/ConsoleCommandSender.java deleted file mode 100644 index 92bb9f66..00000000 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/command/sender/ConsoleCommandSender.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.command.sender; - -import arc.util.Log; -import java.util.Locale; -import mindustry.gen.Player; - -final class ConsoleCommandSender implements CommandSender { - - static final ConsoleCommandSender INSTANCE = new ConsoleCommandSender(); - - private ConsoleCommandSender() {} - - @Override - public void sendMessage(final String content) { - for (final var line : content.split("\n", -1)) { - Log.info(line); - } - } - - @Override - public void sendWarning(final String content) { - for (final var line : content.split("\n", -1)) { - Log.warn(line); - } - } - - @Override - public Locale getLocale() { - return Locale.getDefault(); - } - - @Override - public Player getPlayer() { - throw new UnsupportedOperationException("This sender is not a player."); - } - - @Override - public boolean isPlayer() { - return false; - } - - @Override - public boolean isConsole() { - return true; - } -} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/command/sender/PlayerCommandSender.java b/distributor-api/src/main/java/fr/xpdustry/distributor/api/command/sender/PlayerCommandSender.java deleted file mode 100644 index bc3ebea7..00000000 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/command/sender/PlayerCommandSender.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.command.sender; - -import java.util.Locale; -import mindustry.gen.Player; - -final class PlayerCommandSender implements CommandSender { - - private final Player player; - private final Locale locale; - - PlayerCommandSender(final Player player) { - this.player = player; - this.locale = Locale.forLanguageTag(player.locale().replace('_', '-')); - } - - @Override - public void sendMessage(final String content) { - this.player.sendMessage(content); - } - - @Override - public void sendWarning(final String content) { - this.player.sendMessage("[red]" + content); - } - - @Override - public Locale getLocale() { - return this.locale; - } - - @Override - public Player getPlayer() { - return this.player; - } - - @Override - public boolean isPlayer() { - return true; - } - - @Override - public boolean isConsole() { - return false; - } -} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/command/specifier/AllTeams.java b/distributor-api/src/main/java/fr/xpdustry/distributor/api/command/specifier/AllTeams.java deleted file mode 100644 index fd2ddb81..00000000 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/command/specifier/AllTeams.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.command.specifier; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Annotation used to specify that a {@link mindustry.game.Team} command argument can represent all the teams instead of the 6 base - * teams. - * - * @see fr.xpdustry.distributor.api.command.argument.TeamArgument.TeamMode#ALL - */ -@Target(ElementType.PARAMETER) -@Retention(RetentionPolicy.RUNTIME) -public @interface AllTeams {} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/event/EventBus.java b/distributor-api/src/main/java/fr/xpdustry/distributor/api/event/EventBus.java deleted file mode 100644 index 2a9bbfae..00000000 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/event/EventBus.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.event; - -import fr.xpdustry.distributor.api.plugin.MindustryPlugin; -import fr.xpdustry.distributor.api.util.Priority; -import java.util.function.Consumer; - -/** - * The event bus of this server. A better alternative to {@link arc.Events}. - *
- * Event subscribers registered with this class will not crash the server if an exception is thrown but be logged instead. - * Subscribers also come with {@link EventSubscription} objects to dynamically unsubscribe them. And also - * {@link Priority} to make sure your subscribers are called in a specific order. - *
 {@code
- *      final EventBus bus = DistributorProvider.get().getEventBus();
- *      final MindustryPlugin plugin = ...;
- *      final EventSubscription subscription = bus.subscribe(EventType.PlayerJoin.class, plugin, event -> {
- *          event.player.sendMessage("Hello " + event.player.name() + "!");
- *      });
- *      // When no longer needed, you can unsubscribe the listener
- *      subscription.unsubscribe();
- * } 
- *
- * This class also provides a way to subscribe to events using methods annotated with {@link EventHandler}. - *
- *
 {@code
- *      public final class PlayerListener {
- *          @EventHandler
- *          public void onPlayerJoin(final EventType.PlayerJoin event) {
- *              event.player.sendMessage("Hello, " + event.player.name() + "!");
- *          }
- *      }
- *
- *      public final class MyPlugin extends AbstractMindustryPlugin {
- *          @Override
- *          public void onInit() {
- *              DistributorProvider.get().getEventBus().parse(this, new PlayerListener());
- *          }
- *      }
- * } 
- */ -public interface EventBus { - - /** - * Subscribe to an event. - * - * @param event the event class to subscribe to - * @param priority the priority of the listener - * @param plugin the plugin that owns the listener - * @param listener the listener to subscribe - * @param the type of the event - * @return the subscription of the subscribed listener - */ - EventSubscription subscribe( - final Class event, final Priority priority, final MindustryPlugin plugin, final Consumer listener); - - /** - * Subscribe to an event. - * - * @param event the event class to subscribe to - * @param plugin the plugin that owns the listener - * @param listener the listener to subscribe - * @param the type of the event - * @return the subscription of the subscribed listener - */ - default EventSubscription subscribe( - final Class event, final MindustryPlugin plugin, final Consumer listener) { - return this.subscribe(event, Priority.NORMAL, plugin, listener); - } - - /** - * Subscribe to an event. - * - * @param event the event enum to subscribe to - * @param priority the priority of the listener - * @param plugin the plugin that owns the listener - * @param listener the listener to subscribe - * @param the type of the enum event - * @return the subscription of the subscribed listener - */ - > EventSubscription subscribe( - final E event, final Priority priority, final MindustryPlugin plugin, final Runnable listener); - - /** - * Subscribe to an event. - * - * @param event the event enum to subscribe to - * @param plugin the plugin that owns the listener - * @param listener the listener to subscribe - * @param the type of the enum event - * @return the subscription of the subscribed listener - */ - default > EventSubscription subscribe( - final E event, final MindustryPlugin plugin, final Runnable listener) { - return this.subscribe(event, Priority.NORMAL, plugin, listener); - } - - /** - * Posts the event to the arc event bus. - * - * @param event the event to post - * @param the type of the event - */ - void post(final E event); - - /** - * Posts the event to the arc event bus to the listeners of the given super class. - * - * @param clazz the class of the event - * @param event the event to post - * @param the type of the event - */ - void post(final Class clazz, final E event); - - /** - * Posts the enum event to the arc event bus. - * - * @param event the enum event to post - * @param the type of the enum event - */ - > void post(final E event); - - /** - * Parses the given listener to extract methods annotated with {@link EventHandler} and subscribes them to this - * event bus. - * - * @param plugin the plugin that owns the listener - * @param listener the listener to parse - * @return the subscription of the subscribed handlers - * @deprecated replace with {@link fr.xpdustry.distributor.api.plugin.PluginAnnotationParser} - */ - @Deprecated(forRemoval = true) - EventSubscription parse(final MindustryPlugin plugin, final Object listener); -} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/localization/LocalizationSource.java b/distributor-api/src/main/java/fr/xpdustry/distributor/api/localization/LocalizationSource.java deleted file mode 100644 index 0c0a3f71..00000000 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/localization/LocalizationSource.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.localization; - -import java.text.MessageFormat; -import java.util.Locale; -import org.checkerframework.checker.nullness.qual.Nullable; - -/** - * A helper class for adding localization support to your plugin. - */ -public interface LocalizationSource { - - /** - * Returns a {@code LocalizationSource} for the router language {@code :^)}. - */ - static LocalizationSource router() { - return RouterLocalizationSource.INSTANCE; - } - - /** - * Returns the localized string for the given key or {@code null} if absent. - * - *
 {@code
-     *      // Send a localized message to every player
-     *      final LocalizationSource source = ...;
-     *      Groups.player.each(player -> {
-     *          final var locale = Locale.forLanguageTag(player.locale().replace('_', '-'));
-     *          final var message = source.localize("example.key", locale);
-     *          player.sendMessage(message == null ? "???example.key???" : message);
-     *      }
-     * } 
- * - * @param key the key of the string to localize - * @return the localized string contained in a {@link MessageFormat}, or {@code null} if no string was found. - */ - @Nullable MessageFormat localize(final String key, final Locale locale); - - /** - * Shorthand method to directly format a localized string, with a failover to a default value {@code ???key???}. - * - *
 {@code
-     *      // Send a localized message to every player
-     *      final LocalizationSource source = ...;
-     *      Groups.player.each(player -> {
-     *          final var locale = Locale.forLanguageTag(player.locale().replace('_', '-'));
-     *          player.sendMessage(source.format("example.key", locale));
-     *      }
-     * } 
- * - * @param key the key of the string to localize - * @param locale the locale to use - * @param args the arguments to pass to the {@link MessageFormat#format(Object)} - * @return the formatted string, or {@code ???key???} if no string was found. - */ - default String format(final String key, final Locale locale, final Object... args) { - final var format = this.localize(key, locale); - return format == null ? "???" + key + "???" : format.format(args); - } -} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/localization/LocalizationSourceRegistry.java b/distributor-api/src/main/java/fr/xpdustry/distributor/api/localization/LocalizationSourceRegistry.java deleted file mode 100644 index 8667955d..00000000 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/localization/LocalizationSourceRegistry.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.localization; - -import java.io.BufferedReader; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.text.MessageFormat; -import java.util.Locale; -import java.util.Map; -import java.util.PropertyResourceBundle; -import java.util.ResourceBundle; -import java.util.Set; -import java.util.function.Function; - -/** - * A mutable localization source that can register localized strings. - */ -public interface LocalizationSourceRegistry extends LocalizationSource { - - /** - * Creates a new {@code LocalizationSourceRegistry} instance. - * - * @param defaultLocale the default locale of the localization source - * @return a new {@code LocalizationSourceRegistry} instance - */ - static LocalizationSourceRegistry create(final Locale defaultLocale) { - return new LocalizationSourceRegistryImpl(defaultLocale); - } - - /** - * Registers a map of localized strings. - * - *
 {@code
-     *      final var strings = new HashMap();
-     *      strings.put("example.hello", new MessageFormat("Hello {0}!", Locale.ENGLISH));
-     *      strings.put("example.goodbye", new MessageFormat("Goodbye {0}!", Locale.ENGLISH));
-     *      registry.registerAll(Locale.ENGLISH, strings);
-     * } 
- * - * @param locale the locale to register the strings to - * @param formats the map of localized strings - * @throws IllegalArgumentException if a key is already registered - */ - default void registerAll(final Locale locale, final Map formats) { - this.registerAll(locale, formats.keySet(), formats::get); - } - - /** - * Registers a resource bundle of localized strings. - * - * @param locale the locale to register the strings to - * @param bundle the resource bundle to use - * @throws IllegalArgumentException if a key is already registered - */ - default void registerAll(final Locale locale, final ResourceBundle bundle) { - this.registerAll(locale, bundle.keySet(), key -> new MessageFormat(bundle.getString(key), locale)); - } - - /** - * Registers a resource bundle of localized strings via the classpath. - * - *
 {@code
-     *      final Plugin plugin = ...;
-     *      registry.registerAll(Locale.ENGLISH, "bundle", plugin.getClass().getClassLoader());
-     *      registry.registerAll(Locale.FRENCH, "bundle", plugin.getClass().getClassLoader());
-     * } 
- * - * @param locale the locale to register the strings to - * @param baseName the base name of the resource bundle - * @param loader the class loader to use - * @throws IllegalArgumentException if a key is already registered - */ - default void registerAll(final Locale locale, final String baseName, final ClassLoader loader) { - this.registerAll(locale, ResourceBundle.getBundle(baseName, locale, loader)); - } - - /** - * Registers a resource bundle of localized strings via a file system. - * - *
 {@code
-     *      final var english = Paths.get("bundle_en.properties");
-     *      registry.registerAll(Locale.ENGLISH, path);
-     *      final var english = Paths.get("bundle_fr.properties");
-     *      registry.registerAll(Locale.FRENCH, path);
-     * } 
- * - * @param locale the locale to register the strings to - * @param path the path to the bundle file - * @throws IllegalArgumentException if a key is already registered - */ - default void registerAll(final Locale locale, final Path path) throws IOException { - try (final BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) { - this.registerAll(locale, new PropertyResourceBundle(reader)); - } - } - - /** - * Registers a set of localized strings by using a mapping function to obtain each string. - * - * @param locale the locale to register the strings to - * @param keys the set of keys to register - * @param function the mapping function - * @throws IllegalArgumentException if the key is already registered - */ - default void registerAll( - final Locale locale, final Set keys, final Function function) { - for (final var key : keys) { - this.register(key, locale, function.apply(key)); - } - } - - /** - * Registers a localized string. - * - * @param key the key of the string - * @param locale the locale to register the string to - * @param format the localized string - * @throws IllegalArgumentException if the key is already registered - */ - void register(final String key, final Locale locale, final MessageFormat format); - - /** - * Checks if a key is already registered, for any locale. - * - * @param key the key to check - * @return {@code true} if the key is already registered, {@code false} otherwise - */ - boolean registered(final String key); - - /** - * Checks if a key is already registered for a specific locale. - * - * @param key the key to check - * @param locale the locale to check - * @return {@code true} if the key is already registered for the specific locale, {@code false} otherwise - */ - boolean registered(final String key, final Locale locale); - - /** - * Unregisters a localized string. - * - * @param key the key of the string - */ - void unregister(final String key); - - /** - * Returns the default locale of this source. - */ - Locale getDefaultLocale(); -} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/localization/LocalizationSourceRegistryImpl.java b/distributor-api/src/main/java/fr/xpdustry/distributor/api/localization/LocalizationSourceRegistryImpl.java deleted file mode 100644 index 39b98b55..00000000 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/localization/LocalizationSourceRegistryImpl.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.localization; - -import java.text.MessageFormat; -import java.util.Locale; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import org.checkerframework.checker.nullness.qual.Nullable; - -final class LocalizationSourceRegistryImpl implements LocalizationSourceRegistry { - - private final Map entries = new ConcurrentHashMap<>(); - private final Locale defaultLocale; - - LocalizationSourceRegistryImpl(final Locale defaultLocale) { - this.defaultLocale = defaultLocale; - } - - @Override - public @Nullable MessageFormat localize(final String key, final Locale locale) { - return this.entries.containsKey(key) ? this.entries.get(key).localize(locale) : null; - } - - @Override - public void register(final String key, final Locale locale, final MessageFormat format) { - if (!this.entries.computeIfAbsent(key, k -> new Localization()).register(locale, format)) { - throw new IllegalArgumentException( - String.format("A localization is already present: %s for %s.", key, locale)); - } - } - - @Override - public void unregister(final String key) { - this.entries.remove(key); - } - - @Override - public boolean registered(final String key) { - return this.entries.containsKey(key); - } - - @Override - public boolean registered(final String key, final Locale locale) { - return this.entries.containsKey(key) && this.entries.get(key).formats.containsKey(locale); - } - - @Override - public Locale getDefaultLocale() { - return this.defaultLocale; - } - - private final class Localization { - - private final Map formats = new ConcurrentHashMap<>(); - - private boolean register(final Locale locale, final MessageFormat format) { - return this.formats.putIfAbsent(locale, format) == null; - } - - private @Nullable MessageFormat localize(final Locale locale) { - var format = this.formats.get(locale); - if (format == null) { - // try without the country - format = this.formats.get(new Locale(locale.getLanguage())); - } - if (format == null) { - // try with default locale of this registry - format = this.formats.get(LocalizationSourceRegistryImpl.this.defaultLocale); - } - if (format == null) { - // try local default locale of this JVM - format = this.formats.get(Locale.getDefault()); - } - return format; - } - } -} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/localization/MultiLocalizationSource.java b/distributor-api/src/main/java/fr/xpdustry/distributor/api/localization/MultiLocalizationSource.java deleted file mode 100644 index 1d48220b..00000000 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/localization/MultiLocalizationSource.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.localization; - -/** - * A mutable localization source that delegates the localization lookup to other sources in FIFO order. - */ -public interface MultiLocalizationSource extends LocalizationSource { - - /** - * Creates a new {@code MultiLocalizationSource} instance. - */ - static MultiLocalizationSource create() { - return new MultiLocalizationSourceImpl(); - } - - /** - * Adds a localization source to the list of sources. - * - * @param source the source to add - */ - void addLocalizationSource(final LocalizationSource source); -} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/localization/MultiLocalizationSourceImpl.java b/distributor-api/src/main/java/fr/xpdustry/distributor/api/localization/MultiLocalizationSourceImpl.java deleted file mode 100644 index e93107c4..00000000 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/localization/MultiLocalizationSourceImpl.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.localization; - -import java.text.MessageFormat; -import java.util.ArrayDeque; -import java.util.Deque; -import java.util.Locale; -import org.checkerframework.checker.nullness.qual.Nullable; - -final class MultiLocalizationSourceImpl implements MultiLocalizationSource { - - private final Deque sources = new ArrayDeque<>(); - - @Override - public void addLocalizationSource(final LocalizationSource source) { - this.sources.add(source); - } - - @Override - public @Nullable MessageFormat localize(final String key, final Locale locale) { - final var iterator = this.sources.descendingIterator(); - - while (iterator.hasNext()) { - final var translation = iterator.next().localize(key, locale); - if (translation != null) { - return translation; - } - } - - return null; - } -} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/localization/RouterLocalizationSource.java b/distributor-api/src/main/java/fr/xpdustry/distributor/api/localization/RouterLocalizationSource.java deleted file mode 100644 index 08720677..00000000 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/localization/RouterLocalizationSource.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.localization; - -import java.text.MessageFormat; -import java.util.Locale; -import org.checkerframework.checker.nullness.qual.Nullable; - -final class RouterLocalizationSource implements LocalizationSource { - - static final RouterLocalizationSource INSTANCE = new RouterLocalizationSource(); - - static final Locale ROUTER_LOCALE = new Locale("router"); - private static final MessageFormat ROUTER_FORMAT = new MessageFormat("router", ROUTER_LOCALE); - - private RouterLocalizationSource() {} - - @Override - public @Nullable MessageFormat localize(final String key, final Locale locale) { - return locale.equals(ROUTER_LOCALE) ? ROUTER_FORMAT : null; - } -} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/package-info.java b/distributor-api/src/main/java/fr/xpdustry/distributor/api/package-info.java deleted file mode 100644 index b07c128e..00000000 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/package-info.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2022 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -@DefaultQualifier( - value = NonNull.class, - locations = { - TypeUseLocation.CONSTRUCTOR_RESULT, - TypeUseLocation.EXCEPTION_PARAMETER, - TypeUseLocation.EXPLICIT_LOWER_BOUND, - TypeUseLocation.EXPLICIT_UPPER_BOUND, - TypeUseLocation.FIELD, - TypeUseLocation.IMPLICIT_LOWER_BOUND, - TypeUseLocation.IMPLICIT_UPPER_BOUND, - TypeUseLocation.LOWER_BOUND, - TypeUseLocation.PARAMETER, - TypeUseLocation.RECEIVER, - TypeUseLocation.RESOURCE_VARIABLE, - TypeUseLocation.RETURN, - TypeUseLocation.UPPER_BOUND, - TypeUseLocation.OTHERWISE, - }) -package fr.xpdustry.distributor.api; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.framework.qual.DefaultQualifier; -import org.checkerframework.framework.qual.TypeUseLocation; diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/plugin/PluginAnnotationParser.java b/distributor-api/src/main/java/fr/xpdustry/distributor/api/plugin/PluginAnnotationParser.java deleted file mode 100644 index 1c8f15f7..00000000 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/plugin/PluginAnnotationParser.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.plugin; - -/** - * A plugin component responsible for scanning the plugin instance and its listener for annotations. - */ -public interface PluginAnnotationParser { - - /** - * Creates a simple plugin annotation parser that will parse and register - * {@link fr.xpdustry.distributor.api.scheduler.TaskHandler task handlers} and - * {@link fr.xpdustry.distributor.api.event.EventHandler event handlers}. - * - * @param plugin The owning plugin - * @return the created plugin annotation parser - */ - static PluginAnnotationParser simple(final MindustryPlugin plugin) { - return new SimplePluginAnnotationParser(plugin); - } - - /** - * Parses the given object for annotations related to the plugin. - * - * @param object The object to be scanned for annotations. - * It can be the plugin instance or its listener. - */ - void parse(final Object object); -} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/plugin/SimplePluginAnnotationParser.java b/distributor-api/src/main/java/fr/xpdustry/distributor/api/plugin/SimplePluginAnnotationParser.java deleted file mode 100644 index 181c124b..00000000 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/plugin/SimplePluginAnnotationParser.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.plugin; - -import fr.xpdustry.distributor.api.DistributorProvider; -import fr.xpdustry.distributor.api.event.EventHandler; -import fr.xpdustry.distributor.api.scheduler.Cancellable; -import fr.xpdustry.distributor.api.scheduler.TaskHandler; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.function.Consumer; - -final class SimplePluginAnnotationParser implements PluginAnnotationParser { - - private final MindustryPlugin plugin; - - SimplePluginAnnotationParser(final MindustryPlugin plugin) { - this.plugin = plugin; - } - - @Override - public void parse(final Object object) { - for (final var method : object.getClass().getDeclaredMethods()) { - parseEvents(object, method); - parseTasks(object, method); - } - } - - private void parseEvents(final Object object, final Method method) { - final var annotation = method.getAnnotation(EventHandler.class); - if (annotation == null) { - return; - } - if (method.getParameterCount() != 1) { - throw new IllegalArgumentException("The event handler on " + method + " hasn't the right parameter count."); - } else if (!method.canAccess(object)) { - method.setAccessible(true); - } - - final var handler = new MethodEventHandler<>(object, method, plugin); - DistributorProvider.get() - .getEventBus() - .subscribe(handler.getEventType(), annotation.priority(), plugin, handler); - } - - private void parseTasks(final Object object, final Method method) { - final var annotation = method.getAnnotation(TaskHandler.class); - if (annotation == null) { - return; - } - if (method.getParameterCount() > 1) { - throw new IllegalArgumentException("The event handler on " + method + " hasn't the right parameter count."); - } else if (!method.canAccess(object)) { - method.setAccessible(true); - } else if (method.getParameterCount() == 1 && !Cancellable.class.equals(method.getParameterTypes()[0])) { - throw new IllegalArgumentException("The event handler on " + method + " hasn't the right parameter type."); - } - - final var scheduler = DistributorProvider.get().getPluginScheduler(); - final var builder = annotation.async() ? scheduler.scheduleAsync(plugin) : scheduler.scheduleSync(plugin); - if (annotation.interval() > -1) { - builder.repeat(annotation.interval(), annotation.unit()); - } - if (annotation.delay() > -1) { - builder.delay(annotation.delay(), annotation.unit()); - } - builder.execute(new MethodTaskHandler(object, method)); - } - - private static final class MethodEventHandler implements Consumer { - - private final Object target; - private final Method method; - private final MindustryPlugin plugin; - - private MethodEventHandler(final Object target, final Method method, final MindustryPlugin plugin) { - this.target = target; - this.method = method; - this.plugin = plugin; - } - - @Override - public void accept(final E event) { - try { - this.method.invoke(this.target, event); - } catch (final InvocationTargetException e) { - this.plugin - .getLogger() - .atError() - .setMessage("An error occurred while handling a {} event.") - .addArgument(event.getClass().getSimpleName()) - .setCause(e.getTargetException()) - .log(); - } catch (final ReflectiveOperationException e) { - throw new RuntimeException("Failed to call " + this.method + " on " + this.target, e); - } - } - - @SuppressWarnings("unchecked") - public Class getEventType() { - return (Class) this.method.getParameterTypes()[0]; - } - } - - private static final class MethodTaskHandler implements Consumer { - - private final Object object; - private final Method method; - - private MethodTaskHandler(final Object object, final Method method) { - this.object = object; - this.method = method; - } - - @Override - public void accept(final Cancellable cancellable) { - try { - if (this.method.getParameterCount() == 1) { - this.method.invoke(this.object, cancellable); - } else { - this.method.invoke(this.object); - } - } catch (final IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException("Unable to invoke " + this.method, e); - } - } - } -} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/scheduler/Cancellable.java b/distributor-api/src/main/java/fr/xpdustry/distributor/api/scheduler/Cancellable.java deleted file mode 100644 index 19658c68..00000000 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/scheduler/Cancellable.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.scheduler; - -/** - * A {@code Cancellable} is used to cancel a task. - */ -public interface Cancellable { - - /** - * Cancels the task bound to this {@code Cancellable}. - */ - void cancel(); -} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/scheduler/MindustryTimeUnit.java b/distributor-api/src/main/java/fr/xpdustry/distributor/api/scheduler/MindustryTimeUnit.java deleted file mode 100644 index 7e9cd0a9..00000000 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/scheduler/MindustryTimeUnit.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.scheduler; - -import java.util.Optional; -import java.util.concurrent.TimeUnit; -import org.checkerframework.checker.nullness.qual.Nullable; - -/** - * Time units used by the {@link PluginTaskBuilder} and {@link PluginTask} classes to represent time. - */ -public enum MindustryTimeUnit { - - /** - * Time unit representing one thousandth of a second. - */ - MILLISECONDS(TimeUnit.MILLISECONDS), - - /** - * Time unit representing one game loop, which is 60 times per second. - */ - TICKS(null), - - /** - * Time unit representing one thousandth of a millisecond. - */ - SECONDS(TimeUnit.SECONDS), - - /** - * Time unit representing sixty seconds. - */ - MINUTES(TimeUnit.MINUTES), - - /** - * Time unit representing sixty minutes. - */ - HOURS(TimeUnit.HOURS), - - /** - * Time unit representing twenty-four hours. - */ - DAYS(TimeUnit.DAYS); - - private final @Nullable TimeUnit unit; - - MindustryTimeUnit(final @Nullable TimeUnit unit) { - this.unit = unit; - } - - /** - * Converts the given duration in the given time unit to this time unit. - *

- * Since this method is equivalent to {@link TimeUnit#convert(long, TimeUnit)}: - *

    - *
  • If it overflows, the result will be {@link Long#MAX_VALUE} if the duration is positive, - * or {@link Long#MIN_VALUE} if it is negative.
  • - *
  • Conversions are floored so converting 999 milliseconds to seconds results in 0.
  • - *
- * - * @param sourceDuration the duration to convert - * @param sourceUnit the time unit of the duration - * @return the converted duration - * @see TimeUnit#convert(long, TimeUnit) - */ - public long convert(final long sourceDuration, final MindustryTimeUnit sourceUnit) { - if (this == sourceUnit) { - return sourceDuration; - } - final var sourceJavaUnit = sourceUnit.getJavaTimeUnit(); - final var targetJavaUnit = this.getJavaTimeUnit(); - - if (sourceJavaUnit.isPresent() && targetJavaUnit.isPresent()) { - return targetJavaUnit.get().convert(sourceDuration, sourceJavaUnit.get()); - } else if (sourceJavaUnit.isEmpty()) { - return targetJavaUnit - .orElseThrow() - .convert((long) Math.nextUp(sourceDuration * (1000F / 60F)), TimeUnit.MILLISECONDS); - } else { - final var millis = TimeUnit.MILLISECONDS.convert(sourceDuration, sourceJavaUnit.orElseThrow()); - if (millis == Long.MAX_VALUE || millis == Long.MIN_VALUE) { - return millis; - } - return (long) (millis * (60F / 1000L)); - } - } - - /** - * Returns the Java time unit associated with this Mindustry time unit, if any. - */ - public Optional getJavaTimeUnit() { - return Optional.ofNullable(this.unit); - } -} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/scheduler/PluginScheduler.java b/distributor-api/src/main/java/fr/xpdustry/distributor/api/scheduler/PluginScheduler.java deleted file mode 100644 index 5c4d0538..00000000 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/scheduler/PluginScheduler.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.scheduler; - -import fr.xpdustry.distributor.api.plugin.MindustryPlugin; -import java.util.List; - -/** - * A {@code PluginScheduler} is used to schedule tasks for a plugin. A better alternative to {@link arc.util.Timer}. - */ -public interface PluginScheduler { - - /** - * Returns a new {@link PluginTaskBuilder} instance scheduling a synchronous task. - * - * @param plugin the plugin to schedule the task for. - * @return a new {@link PluginTaskBuilder} instance. - */ - PluginTaskBuilder scheduleSync(final MindustryPlugin plugin); - - /** - * Returns a new {@link PluginTaskBuilder} instance scheduling an asynchronous task. - * - * @param plugin the plugin to schedule the task for. - * @return a new {@link PluginTaskBuilder} instance. - */ - PluginTaskBuilder scheduleAsync(final MindustryPlugin plugin); - - /** - * Returns a new {@link PluginTaskRecipe} instance. - * - * @param plugin the plugin to schedule the task for. - * @param value the initial value. - * @return a new {@link PluginTaskRecipe} instance. - * @deprecated The recipe API is awful given the better alternatives such as completable futures, coroutines, - * or even the structured concurrency API of java 21. - */ - @Deprecated(forRemoval = true) - PluginTaskRecipe recipe(final MindustryPlugin plugin, final V value); - - /** - * Parses the given object to extract methods annotated with {@link TaskHandler} and schedules them to the arc - * event bus. - * - * @param plugin the plugin that owns the listener - * @param object the object to parse - * @return a list of scheduled tasks - * @deprecated replace with {@link fr.xpdustry.distributor.api.plugin.PluginAnnotationParser} - */ - @Deprecated(forRemoval = true) - List> parse(final MindustryPlugin plugin, final Object object); -} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/scheduler/PluginTask.java b/distributor-api/src/main/java/fr/xpdustry/distributor/api/scheduler/PluginTask.java deleted file mode 100644 index dfd0eca2..00000000 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/scheduler/PluginTask.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.scheduler; - -import fr.xpdustry.distributor.api.plugin.PluginAware; -import java.util.concurrent.Future; - -/** - * A {@code PluginTask} is a future used by a {@link PluginScheduler}. - * - * @param the type of the value returned by this task. - */ -public interface PluginTask extends Future, PluginAware { - - /** - * Returns whether this future is executed asynchronously. - */ - boolean isAsync(); -} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/scheduler/PluginTaskBuilder.java b/distributor-api/src/main/java/fr/xpdustry/distributor/api/scheduler/PluginTaskBuilder.java deleted file mode 100644 index 6ecad447..00000000 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/scheduler/PluginTaskBuilder.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.scheduler; - -import java.util.function.Consumer; -import java.util.function.Supplier; - -/** - * A helper object for building and scheduling a {@link PluginTask}. - * - *
 {@code
- *      final PluginScheduler scheduler = DistributorProvider.get().getPluginScheduler();
- *      final MindustryPlugin plugin = ...;
- *      // Warn the players the server is close in 5 minutes.
- *      Groups.player.each(p -> p.sendMessage("The server will restart in 5 minutes."));
- *      // Now schedule the closing task.
- *      scheduler.scheduleSync(plugin).delay(5L, MindustryTimeUnit.MINUTES).execute(() -> Core.app.exit());
- * } 
- */ -public interface PluginTaskBuilder { - - /** - * Run the task after a delay. - * - * @param delay the delay. - * @param unit the time unit of the delay. - * @return this builder. - */ - PluginTaskBuilder delay(final long delay, final MindustryTimeUnit unit); - - /** - * Run the task periodically with a fixed interval. - * Stops the periodic execution if an exception is thrown. - * - * @param interval the interval between the end of the last execution and the start of the next. - * @param unit the time unit of the interval. - * @return this builder. - */ - PluginTaskBuilder repeat(final long interval, final MindustryTimeUnit unit); - - /** - * Build and schedule the task with the given task. - * - * @param runnable the task to run. - * @return a new plugin task. - */ - PluginTask execute(final Runnable runnable); - - /** - * Build and schedule the task with the given task. - * - * @param consumer the task to run, with a cancellable object to stop the task if it's periodic. - * @return a new plugin task. - */ - PluginTask execute(final Consumer consumer); - - /** - * Build and schedule the task with the given task. - * - * @param supplier the task to run, with an output value. Won't output any result value if the task is periodic. - * @return a new plugin task. - */ - PluginTask execute(final Supplier supplier); -} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/scheduler/PluginTaskRecipe.java b/distributor-api/src/main/java/fr/xpdustry/distributor/api/scheduler/PluginTaskRecipe.java deleted file mode 100644 index 1adbe5dd..00000000 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/scheduler/PluginTaskRecipe.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.scheduler; - -import java.util.function.Consumer; -import java.util.function.Function; - -/** - * A {@code PluginTaskRecipe} is a helper class for creating staged {@link PluginTask}s. - * It will basically execute each step (asynchronously or not) in separate tasks, then returning the result. - * - *
 {@code
- *      final PluginScheduler scheduler = ...;
- *      final MindustryPlugin plugin = ...;
- *      final CommandHandler handler = ...;
- *      handler.register("rank", "Get your pvp rank.", (args, player) -> scheduler
- *          .recipe(plugin, player)
- *          .thenAccept(p -> p.sendMessage("Getting your rank..."))
- *          .thenApplyAsync(p -> getRankFromDatabase(p))
- *          .thenAccept(rank -> player.sendMessage("Your rank is " + rank))
- *          .execute());
- * } 
- * - * @param the type of the result. - */ -public interface PluginTaskRecipe { - - PluginTaskRecipe thenAccept(final Consumer consumer); - - PluginTaskRecipe thenApply(final Function function); - - PluginTaskRecipe thenRun(final Runnable runnable); - - PluginTaskRecipe thenAcceptAsync(final Consumer consumer); - - PluginTaskRecipe thenApplyAsync(final Function function); - - PluginTaskRecipe thenRunAsync(final Runnable runnable); - - PluginTask execute(); -} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/scheduler/TaskHandler.java b/distributor-api/src/main/java/fr/xpdustry/distributor/api/scheduler/TaskHandler.java deleted file mode 100644 index 97d95662..00000000 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/scheduler/TaskHandler.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.scheduler; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Marks a method as a task handler, meaning it will be registered and called as a scheduled task in the - * {@link PluginScheduler}. - *
- * The annotated method can have one {@link Cancellable} parameter to allow the task to cancel itself. - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface TaskHandler { - - /** - * The interval between each execution of the task. - * The task will be executed once if the interval is set to a value below -1. - */ - long interval() default -1; - - /** - * The initial delay before the first execution of the task. - * The task will be executed immediately if the delay is set to a value below -1. - */ - long delay() default -1; - - /** - * The time unit of the interval and initial delay. - */ - MindustryTimeUnit unit() default MindustryTimeUnit.SECONDS; - - /** - * Whether the task should be executed asynchronously. - */ - boolean async() default false; -} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/security/PlayerValidator.java b/distributor-api/src/main/java/fr/xpdustry/distributor/api/security/PlayerValidator.java deleted file mode 100644 index 09cbedd9..00000000 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/security/PlayerValidator.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.security; - -import fr.xpdustry.distributor.api.util.MUUID; - -/** - * A service to check player identities based on usids since uuids are very easy to steal. - */ -public interface PlayerValidator { - - /** - * Checks whether a player muuid is valid. - * - * @param muuid the player's muuid. - * @return {@code true} if the muuid is valid, {@code false} if invalid or unknown. - */ - boolean isValid(final MUUID muuid); - - /** - * Checks whether a player uuid is bound to any muuid inside the validator. - * - * @param uuid the player's uuid. - * @return {@code true} if the uuid is bound to a muuid, {@code false} otherwise. - */ - boolean contains(final String uuid); - - /** - * Checks whether a player muuid is present inside this validator. - * - * @param muuid the player's muuid. - * @return {@code true} if the muuid is present, {@code false} otherwise. - */ - boolean contains(final MUUID muuid); - - /** - * Marks the given muuid as valid. - * - * @param muuid the player's muuid. - */ - void validate(final MUUID muuid); - - /** - * Marks the given muuid as invalid. - * - * @param muuid the player's muuid. - */ - void invalidate(final MUUID muuid); - - /** - * Marks all muuid with the given uuid as invalid. - * - * @param uuid the player's uuid. - */ - void invalidate(final String uuid); - - /** - * Invalidates all muuids. - */ - void invalidateAll(); - - /** - * Removes all muuid validation statuses with the given uuid from the validator. - * - * @param uuid the player's uuid. - */ - void remove(final String uuid); - - /** - * Removes muuid validation statuses from the validator. - * - * @param muuid the player's muuid. - */ - void remove(final MUUID muuid); - - /** - * Removes all muuid validation statuses from the validator. - */ - void removeAll(); -} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/security/permission/GroupPermissible.java b/distributor-api/src/main/java/fr/xpdustry/distributor/api/security/permission/GroupPermissible.java deleted file mode 100644 index 4bbf2f6b..00000000 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/security/permission/GroupPermissible.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.security.permission; - -/** - * A permissible representing a group, for easily managing set of permissions with players or other groups. - */ -public interface GroupPermissible extends Permissible { - - /** - * Returns the weight of this group. - *

- * Note: the higher the weight, the higher the priority during permission lookup. - */ - int getWeight(); - - /** - * Sets the weight of this group. - *

- * Note: the higher the weight, the higher the priority during permission lookup. - */ - void setWeight(int weight); -} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/security/permission/Permissible.java b/distributor-api/src/main/java/fr/xpdustry/distributor/api/security/permission/Permissible.java deleted file mode 100644 index 271e10f2..00000000 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/security/permission/Permissible.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.security.permission; - -import fr.xpdustry.distributor.api.util.Tristate; -import java.util.Collection; -import java.util.Map; -import java.util.regex.Pattern; - -/** - * Represents an entity that can be assigned permissions. - */ -public interface Permissible { - - /** - * Regex pattern used to validate permission strings. - *

- * Notes: - *

- * Permission strings are composed of a series of nodes separated by dots with alphanumeric characters and minus - * signs, such as {@code "plugin.command"}. - *
- * A parent node is always overridden by a child node, such as {@code "plugin.command"} overriding {@code "plugin"}. - *
- * Wildcards are also allowed, but they currently have the same effect as a normal node, such as {@code "plugin.command.*"} equals {@code "plugin.command"}. - *
- * The only relevant use of wildcards is the root permission {@code "*"} permission. It - * allows you to set a default value for all permissions. - *
- */ - String PERMISSION_REGEX = "^(\\*|[a-z\\d\\-]+)(\\.(\\*|[a-z\\d\\-]+))*$"; - - Pattern PERMISSION_PATTERN = Pattern.compile(PERMISSION_REGEX); - - /** - * Returns the name of this permissible. - */ - String getName(); - - /** - * Returns the state for a given permission. - *
    - *
  • {@link Tristate#TRUE} if the permission is explicitly granted.
  • - *
  • {@link Tristate#FALSE} if the permission is explicitly denied.
  • - *
  • {@link Tristate#UNDEFINED} if the permission is not set or it does not match the - * {@link #PERMISSION_REGEX regex}.
  • - *
- * - * @param permission the permission string - * @return the state of the permission - */ - Tristate getPermission(final String permission); - - /** - * Sets the state for a given permission. - *
    - *
  • {@link Tristate#TRUE} to explicitly grant the permission.
  • - *
  • {@link Tristate#FALSE} to explicitly deny the permission.
  • - *
  • {@link Tristate#UNDEFINED} to remove the permission.
  • - *
- * - * @param permission the permission string - * @param state the state of the permission - */ - void setPermission(final String permission, final Tristate state); - - /** - * Sets the state for a given permission. - *
    - *
  • {@code true} to explicitly grant the permission.
  • - *
  • {@code false} to explicitly deny the permission.
  • - *
- * - * @param permission the permission string - * @param state the state of the permission - */ - default void setPermission(final String permission, final boolean state) { - this.setPermission(permission, Tristate.of(state)); - } - - /** - * Returns the permissions of this permissible as a map. - */ - Map getPermissions(); - - /** - * Sets the permissions of this permissible. - * - * @param permissions the permissions to set - */ - void setPermissions(final Map permissions); - - /** - * Returns the parents of this permissible. - */ - Collection getParentGroups(); - - /** - * Sets the parents of this permissible. - * - * @param parents the groups to set - */ - void setParentGroups(final Collection parents); - - /** - * Adds a parent to this permissible. - */ - void addParentGroup(final String group); - - /** - * Removes a parent from this permissible. - */ - void removeParentGroup(final String group); -} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/security/permission/PermissibleManager.java b/distributor-api/src/main/java/fr/xpdustry/distributor/api/security/permission/PermissibleManager.java deleted file mode 100644 index 092d6564..00000000 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/security/permission/PermissibleManager.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.security.permission; - -import java.util.Optional; - -/** - * A manager for a specific type of permissible. - * - * @param

the type of permissible - */ -public interface PermissibleManager

{ - - /** - * Saves the given permissible. - * - * @param permissible the permissible to save - */ - void save(final P permissible); - - /** - * Saves the given permissibles in bulk. - * - * @param permissibles the permissibles to save - */ - default void saveAll(final Iterable

permissibles) { - permissibles.forEach(this::save); - } - - /** - * Returns the permissible with the given id. If not found, a new permissible is created. - * - * @param id the id of the permissible - * @return the permissible or a new one - */ - P findOrCreateById(final String id); - - /** - * Returns the permissible with the given id. If not found, an empty optional is returned. - * - * @param id the id of the permissible - * @return the permissible or an empty optional - */ - Optional

findById(final String id); - - /** - * Returns all the permissibles. - */ - Iterable

findAll(); - - /** - * Checks if the permissible exists in the database. - * - * @param permissible the permissible to check - * @return {@code true} if the permissible exists, {@code false} otherwise - */ - boolean exists(final P permissible); - - /** - * Checks if the permissible exists in the database by id. - * - * @param id the id of the permissible - * @return {@code true} if the permissible exists, {@code false} otherwise - */ - default boolean existsById(final String id) { - return this.findById(id).isPresent(); - } - - /** - * Returns the number of permissibles. - */ - long count(); - - /** - * Deletes the given permissible by id if it exists. - * - * @param id the id of the permissible to delete - */ - void deleteById(final String id); - - /** - * Deletes the given permissible. - * - * @param permissible the permissible to delete - */ - void delete(final P permissible); - - /** - * Deletes all the permissibles. - */ - void deleteAll(); - - /** - * Deletes all the given permissibles in bulk. - * - * @param permissibles the permissibles to delete - */ - default void deleteAll(final Iterable

permissibles) { - permissibles.forEach(this::delete); - } -} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/security/permission/PermissionService.java b/distributor-api/src/main/java/fr/xpdustry/distributor/api/security/permission/PermissionService.java deleted file mode 100644 index 54c022bf..00000000 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/security/permission/PermissionService.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.security.permission; - -import fr.xpdustry.distributor.api.util.MUUID; -import fr.xpdustry.distributor.api.util.Tristate; - -/** - * A service that manages permissions. - */ -public interface PermissionService { - - /** - * Looks up the permission of a player. Taking into account: - *

    - *
  • the player's parent groups
  • - *
  • the player's validation status
  • - *
- * - * @param muuid the player's muuid. - * @return the state of the permission for the player. - */ - Tristate getPlayerPermission(final MUUID muuid, final String permission); - - /** - * Looks up the permission of a player. Taking into account: - *
    - *
  • the player's parent groups
  • - *
- * - * @param uuid the player's uuid. - * @return the state of the permission for the player. - */ - Tristate getPlayerPermission(final String uuid, final String permission); - - /** - * Looks up the permission of a group. Taking into account: - *
    - *
  • the group's parent groups
  • - *
- * - * @param group the group's name. - * @return the state of the permission for the group. - */ - Tristate getGroupPermission(final String group, final String permission); - - /** - * Returns the permissible manager for players. - */ - PermissibleManager getPlayerPermissionManager(); - - /** - * Returns the permissible manager for groups. - */ - PermissibleManager getGroupPermissionManager(); -} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/security/permission/PlayerPermissible.java b/distributor-api/src/main/java/fr/xpdustry/distributor/api/security/permission/PlayerPermissible.java deleted file mode 100644 index 880e1385..00000000 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/security/permission/PlayerPermissible.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.security.permission; - -/** - * A permissible representing a specific player. - */ -public interface PlayerPermissible extends Permissible { - - /** - * Returns the UUID of this player. - */ - String getUuid(); -} diff --git a/distributor-api/src/test/java/fr/xpdustry/distributor/api/command/argument/AbstractArgumentTest.java b/distributor-api/src/test/java/fr/xpdustry/distributor/api/command/argument/AbstractArgumentTest.java deleted file mode 100644 index 185e8b6f..00000000 --- a/distributor-api/src/test/java/fr/xpdustry/distributor/api/command/argument/AbstractArgumentTest.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.command.argument; - -import cloud.commandframework.arguments.CommandArgument; -import cloud.commandframework.context.CommandContext; -import cloud.commandframework.exceptions.parsing.NoInputProvidedException; -import java.util.ArrayDeque; -import java.util.Arrays; -import java.util.Queue; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -import static org.assertj.core.api.Assertions.assertThat; - -public abstract class AbstractArgumentTest> { - - private CommandContext context; - - @SuppressWarnings("unchecked") - @BeforeEach - void createContext() { - this.context = (CommandContext) Mockito.mock(CommandContext.class); - } - - @Test - void test_fail_no_input() { - final var argument = this.createArgument(); - final var result = argument.getParser().parse(this.getCommandContext(), this.createArgumentQueue()); - assertThat(result.getFailure()).isPresent().get().isInstanceOf(NoInputProvidedException.class); - } - - protected CommandContext getCommandContext() { - return this.context; - } - - protected Queue createArgumentQueue(final String... arguments) { - return new ArrayDeque<>(Arrays.asList(arguments)); - } - - protected abstract A createArgument(); -} diff --git a/distributor-api/src/test/java/fr/xpdustry/distributor/api/command/argument/AbstractPlayerLookupArgumentTest.java b/distributor-api/src/test/java/fr/xpdustry/distributor/api/command/argument/AbstractPlayerLookupArgumentTest.java deleted file mode 100644 index c5e769ff..00000000 --- a/distributor-api/src/test/java/fr/xpdustry/distributor/api/command/argument/AbstractPlayerLookupArgumentTest.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.command.argument; - -import cloud.commandframework.arguments.CommandArgument; -import fr.xpdustry.distributor.api.TestPlayer; -import mindustry.gen.Groups; -import mindustry.gen.Player; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -public abstract class AbstractPlayerLookupArgumentTest, R> - extends AbstractArgumentTest { - - private Player player1; - private Player player2; - private Player player3; - - @BeforeEach - void createPlayers() { - Groups.init(); - - this.player1 = new TestPlayer("zeta", "AAAAAAAAAAAAAAAAAAAAAA=="); - this.player2 = new TestPlayer("[cyan]{XP}[] bob", "BAAAAAAAAAAAAAAAAAAAAA=="); - this.player3 = new TestPlayer("[red]{XP}[] max", "CAAAAAAAAAAAAAAAAAAAAA=="); - - Groups.player.add(this.player1); - Groups.player.add(this.player2); - Groups.player.add(this.player3); - } - - @AfterEach - void clearGroups() { - Groups.clear(); - } - - @Test - void test_find_by_name_simple() { - final var argument = this.createArgument(); - final var result = argument.getParser().parse(this.getCommandContext(), this.createArgumentQueue("zeta")); - assertThat(result.getParsedValue()).isPresent().get().isEqualTo(this.mapPlayer(this.player1)); - } - - @Test - void test_find_by_name_colored() { - final var argument = this.createArgument(); - final var result = argument.getParser().parse(this.getCommandContext(), this.createArgumentQueue("[blue]bob")); - assertThat(result.getParsedValue()).isPresent().get().isEqualTo(this.mapPlayer(this.player2)); - } - - @Test - void test_find_by_uuid() { - final var argument = this.createArgument(); - final var result = - argument.getParser().parse(this.getCommandContext(), this.createArgumentQueue(this.player3.uuid())); - assertThat(result.getParsedValue()).isPresent().get().isEqualTo(this.mapPlayer(this.player3)); - } - - @Test - void test_fail_too_many_players_found() { - final var argument = this.createArgument(); - final var result = argument.getParser().parse(this.getCommandContext(), this.createArgumentQueue("{XP}")); - assertThat(result.getFailure()) - .isPresent() - .get() - .isInstanceOf(PlayerArgument.TooManyPlayersFoundException.class); - } - - @Test - void test_fail_no_player_found() { - final var argument = this.createArgument(); - final var result = argument.getParser().parse(this.getCommandContext(), this.createArgumentQueue("unknown")); - assertThat(result.getFailure()).isPresent().get().isInstanceOf(PlayerArgument.PlayerNotFoundException.class); - } - - @Test - void test_single_suggestion() { - final var argument = this.createArgument(); - final var result = argument.getParser().suggestions(this.getCommandContext(), "z"); - assertThat(result).containsExactly("zeta"); - } - - @Test - void test_multiple_suggestions() { - final var argument = this.createArgument(); - final var result = argument.getSuggestionsProvider().apply(this.getCommandContext(), "{XP}"); - assertThat(result).containsExactly("{XP} bob", "{XP} max"); - } - - @Override - protected A createArgument() { - return null; - } - - protected Player getPlayer1() { - return this.player1; - } - - protected Player getPlayer2() { - return this.player2; - } - - protected Player getPlayer3() { - return this.player3; - } - - protected abstract R mapPlayer(final Player player); -} diff --git a/distributor-api/src/test/java/fr/xpdustry/distributor/api/command/argument/AbstractTeamArgumentTest.java b/distributor-api/src/test/java/fr/xpdustry/distributor/api/command/argument/AbstractTeamArgumentTest.java deleted file mode 100644 index 18162f6b..00000000 --- a/distributor-api/src/test/java/fr/xpdustry/distributor/api/command/argument/AbstractTeamArgumentTest.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.command.argument; - -import java.util.Arrays; -import java.util.stream.Stream; -import mindustry.game.Team; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtensionContext; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.ArgumentsProvider; -import org.junit.jupiter.params.provider.ArgumentsSource; - -import static org.assertj.core.api.Assertions.assertThat; - -public abstract class AbstractTeamArgumentTest extends AbstractArgumentTest> { - - @ParameterizedTest - @ArgumentsSource(BaseTeamArgumentProvider.class) - void test_find_base_team(final Team team) { - final var argument = this.createArgument(); - final var result = argument.getParser().parse(this.getCommandContext(), this.createArgumentQueue(team.name)); - assertThat(result.getParsedValue()).isPresent().get().isEqualTo(team); - } - - @Test - void test_fail_find_team() { - final var argument = this.createArgument(); - final var result = - argument.getParser().parse(this.getCommandContext(), this.createArgumentQueue("tartiflette")); - assertThat(result.getFailure()).isPresent().get().isInstanceOf(TeamArgument.TeamParseException.class); - } - - @Test - void test_suggestions() { - final var argument = this.createArgument(); - - final var suggestions1 = argument.getSuggestionsProvider().apply(this.getCommandContext(), "sh"); - assertThat(suggestions1).containsExactly("sharded"); - - final var suggestions2 = argument.getSuggestionsProvider().apply(this.getCommandContext(), "team#"); - assertThat(suggestions2).isEmpty(); - } - - private static final class BaseTeamArgumentProvider implements ArgumentsProvider { - - @Override - public Stream provideArguments(final ExtensionContext context) { - return Arrays.stream(Team.baseTeams).map(Arguments::of); - } - } -} diff --git a/distributor-api/src/test/java/fr/xpdustry/distributor/api/command/argument/AllTeamArgumentTest.java b/distributor-api/src/test/java/fr/xpdustry/distributor/api/command/argument/AllTeamArgumentTest.java deleted file mode 100644 index 71cf16b2..00000000 --- a/distributor-api/src/test/java/fr/xpdustry/distributor/api/command/argument/AllTeamArgumentTest.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.command.argument; - -import java.util.Arrays; -import java.util.stream.Stream; -import mindustry.game.Team; -import org.junit.jupiter.api.extension.ExtensionContext; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.ArgumentsProvider; -import org.junit.jupiter.params.provider.ArgumentsSource; - -import static org.assertj.core.api.Assertions.assertThat; - -public final class AllTeamArgumentTest extends AbstractTeamArgumentTest { - - @ParameterizedTest - @ArgumentsSource(AllTeamArgumentProvider.class) - void test_find_all_team(final Team team) { - final var argument = this.createArgument(); - final var result = argument.getParser().parse(this.getCommandContext(), this.createArgumentQueue(team.name)); - assertThat(result.getParsedValue()).isPresent().get().isEqualTo(team); - } - - @Override - protected TeamArgument createArgument() { - return TeamArgument.all("argument"); - } - - private static final class AllTeamArgumentProvider implements ArgumentsProvider { - - @Override - public Stream provideArguments(final ExtensionContext context) { - return Arrays.stream(Team.all).map(Arguments::of); - } - } -} diff --git a/distributor-api/src/test/java/fr/xpdustry/distributor/api/command/argument/BaseTeamArgumentTest.java b/distributor-api/src/test/java/fr/xpdustry/distributor/api/command/argument/BaseTeamArgumentTest.java deleted file mode 100644 index 06932b4b..00000000 --- a/distributor-api/src/test/java/fr/xpdustry/distributor/api/command/argument/BaseTeamArgumentTest.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.command.argument; - -public final class BaseTeamArgumentTest extends AbstractTeamArgumentTest { - - @Override - protected TeamArgument createArgument() { - return TeamArgument.base("argument"); - } -} diff --git a/distributor-api/src/test/java/fr/xpdustry/distributor/api/command/argument/PlayerInfoArgumentTest.java b/distributor-api/src/test/java/fr/xpdustry/distributor/api/command/argument/PlayerInfoArgumentTest.java deleted file mode 100644 index 3b583dc2..00000000 --- a/distributor-api/src/test/java/fr/xpdustry/distributor/api/command/argument/PlayerInfoArgumentTest.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.command.argument; - -import arc.Core; -import arc.mock.MockSettings; -import mindustry.Vars; -import mindustry.core.NetServer; -import mindustry.gen.Player; -import mindustry.net.Administration.PlayerInfo; -import mindustry.net.Net; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -import static org.assertj.core.api.Assertions.assertThat; - -public final class PlayerInfoArgumentTest - extends AbstractPlayerLookupArgumentTest, PlayerInfo> { - - private static final String CREATED_UUID = "DAAAAAAAAAAAAAAAAAAAAA=="; - - @BeforeEach - void createAdministration() { - Core.settings = new MockSettings(); - Vars.net = Mockito.mock(Net.class); - Vars.netServer = new NetServer(); - } - - @Test - void test_find_by_created_info() { - final var argument = this.createArgument(); - final var result = argument.getParser().parse(this.getCommandContext(), this.createArgumentQueue(CREATED_UUID)); - assertThat(result.getParsedValue()).isPresent().get().extracting("id").isEqualTo(CREATED_UUID); - } - - @Test - void test_find_by_existing_info() { - Vars.netServer.admins.updatePlayerJoined( - this.getPlayer1().uuid(), "0.0.0.0", this.getPlayer1().name()); - final var argument = this.createArgument(); - final var result = argument.getParser() - .parse( - this.getCommandContext(), - this.createArgumentQueue(this.getPlayer1().uuid())); - assertThat(result.getParsedValue()) - .isPresent() - .get() - .isEqualTo(this.getPlayer1().getInfo()); - } - - @Override - protected PlayerInfoArgument createArgument() { - return PlayerInfoArgument.of("argument"); - } - - @Override - protected PlayerInfo mapPlayer(final Player player) { - return player.getInfo(); - } -} diff --git a/distributor-api/src/test/java/fr/xpdustry/distributor/api/localization/LocalizationSourceRegistryImplTest.java b/distributor-api/src/test/java/fr/xpdustry/distributor/api/localization/LocalizationSourceRegistryImplTest.java deleted file mode 100644 index a8fbb53b..00000000 --- a/distributor-api/src/test/java/fr/xpdustry/distributor/api/localization/LocalizationSourceRegistryImplTest.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.localization; - -import java.text.MessageFormat; -import java.util.Locale; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -public final class LocalizationSourceRegistryImplTest { - - private LocalizationSourceRegistryImpl registry; - - @BeforeEach - void setup() { - this.registry = new LocalizationSourceRegistryImpl(Locale.ENGLISH); - } - - @Test - void test_simple_localize() { - this.registry.register("greeting", Locale.FRENCH, new MessageFormat("Bonjour {0}!", Locale.FRENCH)); - this.registry.register("greeting", Locale.ENGLISH, new MessageFormat("Hello {0}!", Locale.ENGLISH)); - - assertThat(this.registry.localize("greeting", Locale.FRENCH)) - .isNotNull() - .extracting(MessageFormat::toPattern) - .isEqualTo("Bonjour {0}!"); - - assertThat(this.registry.localize("greeting", Locale.ENGLISH)) - .isNotNull() - .extracting(MessageFormat::toPattern) - .isEqualTo("Hello {0}!"); - - assertThat(this.registry.localize("greeting", Locale.CHINESE)) - .isNotNull() - .extracting(MessageFormat::toPattern) - .isEqualTo("Hello {0}!"); - } - - @Test - void test_unregister() { - this.registry.register("greeting", Locale.FRENCH, new MessageFormat("Bonjour {0}!", Locale.FRENCH)); - this.registry.register("greeting", Locale.ENGLISH, new MessageFormat("Hello {0}!", Locale.ENGLISH)); - - assertThat(this.registry.localize("greeting", Locale.ENGLISH)).isNotNull(); - assertThat(this.registry.localize("greeting", Locale.FRENCH)).isNotNull(); - - this.registry.unregister("greeting"); - - assertThat(this.registry.localize("greeting", Locale.ENGLISH)).isNull(); - assertThat(this.registry.localize("greeting", Locale.FRENCH)).isNull(); - } - - @Test - void test_illegal_register() { - this.registry.register("greeting", Locale.FRENCH, new MessageFormat("Bonjour {0}!", Locale.FRENCH)); - assertThatThrownBy(() -> this.registry.register( - "greeting", Locale.FRENCH, new MessageFormat("Bonjour {0}!", Locale.FRENCH))) - .isInstanceOf(IllegalArgumentException.class); - } -} diff --git a/distributor-api/src/test/java/fr/xpdustry/distributor/api/localization/MultiLocalizationSourceImplTest.java b/distributor-api/src/test/java/fr/xpdustry/distributor/api/localization/MultiLocalizationSourceImplTest.java deleted file mode 100644 index a4b65be0..00000000 --- a/distributor-api/src/test/java/fr/xpdustry/distributor/api/localization/MultiLocalizationSourceImplTest.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.localization; - -import java.text.MessageFormat; -import java.util.Locale; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -public final class MultiLocalizationSourceImplTest { - - private MultiLocalizationSource source; - - @BeforeEach - void setup() { - this.source = new MultiLocalizationSourceImpl(); - } - - @Test - void test_lookup_order() { - final var source1 = new LocalizationSourceRegistryImpl(Locale.FRENCH); - final var source2 = RouterLocalizationSource.INSTANCE; - source1.register("greeting", Locale.FRENCH, new MessageFormat("Bonjour {0}!", Locale.FRENCH)); - - this.source.addLocalizationSource(source1); - assertThat(this.source.localize("greeting", RouterLocalizationSource.ROUTER_LOCALE)) - .isNotNull() - .extracting(MessageFormat::toPattern) - .isEqualTo("Bonjour {0}!"); - - this.source.addLocalizationSource(source2); - assertThat(this.source.localize("greeting", RouterLocalizationSource.ROUTER_LOCALE)) - .isNotNull() - .extracting(MessageFormat::toPattern) - .isEqualTo("router"); - } -} diff --git a/distributor-api/src/test/java/fr/xpdustry/distributor/api/scheduler/MindustryTimeUnitTest.java b/distributor-api/src/test/java/fr/xpdustry/distributor/api/scheduler/MindustryTimeUnitTest.java deleted file mode 100644 index 1fd537e1..00000000 --- a/distributor-api/src/test/java/fr/xpdustry/distributor/api/scheduler/MindustryTimeUnitTest.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.scheduler; - -import java.util.Arrays; -import java.util.stream.Stream; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.EnumSource; -import org.junit.jupiter.params.provider.MethodSource; - -import static org.assertj.core.api.Assertions.assertThat; - -public final class MindustryTimeUnitTest { - - @ParameterizedTest - @EnumSource(MindustryTimeUnit.class) - void test_java_unit(final MindustryTimeUnit unit) { - if (unit != MindustryTimeUnit.TICKS) { - assertThat(unit.getJavaTimeUnit()).isPresent(); - } else { - assertThat(unit.getJavaTimeUnit()).isEmpty(); - } - } - - @ParameterizedTest - @MethodSource("provideJavaMindustryTimeUnits") - void test_java_conversion(final MindustryTimeUnit unitA, final MindustryTimeUnit unitB) { - assertThat(unitA.getJavaTimeUnit() - .orElseThrow() - .convert(1000L, unitB.getJavaTimeUnit().orElseThrow())) - .isEqualTo(unitA.convert(1000L, unitB)); - } - - @Test - void test_tick_conversion() { - assertThat(MindustryTimeUnit.TICKS.convert(60L, MindustryTimeUnit.TICKS)) - .isEqualTo(60L); - - assertThat(MindustryTimeUnit.TICKS.convert(2L, MindustryTimeUnit.SECONDS)) - .isEqualTo(120L); - - assertThat(MindustryTimeUnit.SECONDS.convert(120L, MindustryTimeUnit.TICKS)) - .isEqualTo(2L); - } - - @Test - void test_tick_conversion_overflow() { - assertThat(MindustryTimeUnit.TICKS.convert(+10_000_000_000_000L, MindustryTimeUnit.DAYS)) - .isEqualTo(Long.MAX_VALUE); - - assertThat(MindustryTimeUnit.TICKS.convert(-10_000_000_000_000L, MindustryTimeUnit.DAYS)) - .isEqualTo(Long.MIN_VALUE); - } - - private static Stream provideJavaMindustryTimeUnits() { - return Arrays.stream(MindustryTimeUnit.values()) - .filter(unit -> unit != MindustryTimeUnit.TICKS) - .flatMap(unit1 -> Arrays.stream(MindustryTimeUnit.values()) - .filter(unit2 -> unit2 != MindustryTimeUnit.TICKS) - .map(unit2 -> Arguments.of(unit1, unit2))); - } -} diff --git a/distributor-api/src/test/java/fr/xpdustry/distributor/api/util/ArcListTest.java b/distributor-api/src/test/java/fr/xpdustry/distributor/api/util/ArcListTest.java deleted file mode 100644 index 5b37c284..00000000 --- a/distributor-api/src/test/java/fr/xpdustry/distributor/api/util/ArcListTest.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.util; - -import arc.struct.Seq; -import com.google.common.collect.testing.ListTestSuiteBuilder; -import com.google.common.collect.testing.TestStringListGenerator; -import com.google.common.collect.testing.features.CollectionFeature; -import com.google.common.collect.testing.features.CollectionSize; -import com.google.common.collect.testing.features.ListFeature; -import java.util.Arrays; -import java.util.List; -import junit.framework.TestResult; -import junit.framework.TestSuite; -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.Test; - -public final class ArcListTest { - - @Test - void testSuite() { - final var suite = new TestSuite(); - suite.addTest(ListTestSuiteBuilder.using(new TestArcListGenerator()) - .named("ArcList") - .withFeatures(List.of(ListFeature.values())) - .withFeatures(List.of( - CollectionSize.ANY, - CollectionFeature.ALLOWS_NULL_VALUES, - CollectionFeature.SUBSET_VIEW, - CollectionFeature.DESCENDING_VIEW)) - .createTestSuite()); - final var result = new TestResult(); - suite.run(result); - if (!result.wasSuccessful()) { - @SuppressWarnings("MismatchedQueryAndUpdateOfStringBuilder") // tf ? - final var builder = new StringBuilder(); - builder.append("ArcList Test suite failed:\n"); - result.failures().asIterator().forEachRemaining(failure -> builder.append(failure.toString()) - .append('\n')); - result.errors().asIterator().forEachRemaining(error -> builder.append(error.toString()) - .append('\n')); - Assertions.fail(builder.toString()); - } - } - - private static final class TestArcListGenerator extends TestStringListGenerator { - - @Override - protected List create(String[] elements) { - final var list = new ArcList(new Seq<>(String.class)); - list.addAll(Arrays.asList(elements)); - return list; - } - } -} diff --git a/distributor-api/src/test/java/fr/xpdustry/distributor/api/util/ArcMapTest.java b/distributor-api/src/test/java/fr/xpdustry/distributor/api/util/ArcMapTest.java deleted file mode 100644 index 0fe07158..00000000 --- a/distributor-api/src/test/java/fr/xpdustry/distributor/api/util/ArcMapTest.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.util; - -import arc.struct.ObjectMap; -import com.google.common.collect.testing.MapTestSuiteBuilder; -import com.google.common.collect.testing.TestStringMapGenerator; -import com.google.common.collect.testing.features.CollectionFeature; -import com.google.common.collect.testing.features.CollectionSize; -import com.google.common.collect.testing.features.MapFeature; -import java.util.List; -import java.util.Map; -import junit.framework.TestResult; -import junit.framework.TestSuite; -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.Test; - -public final class ArcMapTest { - - @Test - void testSuite() { - final var suite = new TestSuite(); - suite.addTest(MapTestSuiteBuilder.using(new TestArcMapGenerator()) - .named("ArcMap") - .withFeatures( - List.of(MapFeature.GENERAL_PURPOSE, MapFeature.ALLOWS_NULL_VALUES, MapFeature.SUPPORTS_REMOVE)) - .withFeatures(List.of( - CollectionSize.ANY, - CollectionFeature.NON_STANDARD_TOSTRING, - CollectionFeature.SUPPORTS_ITERATOR_REMOVE)) - .createTestSuite()); - final var result = new TestResult(); - suite.run(result); - if (!result.wasSuccessful()) { - @SuppressWarnings("MismatchedQueryAndUpdateOfStringBuilder") // tf ? - final var builder = new StringBuilder(); - builder.append("ArcMap Test suite failed:\n"); - result.failures().asIterator().forEachRemaining(failure -> builder.append(failure.toString()) - .append('\n')); - result.errors().asIterator().forEachRemaining(error -> builder.append(error.toString()) - .append('\n')); - Assertions.fail(builder.toString()); - } - } - - private static final class TestArcMapGenerator extends TestStringMapGenerator { - - @Override - protected Map create(final Map.Entry[] entries) { - final var map = new ArcMap(new ObjectMap<>()); - for (final var entry : entries) map.put(entry.getKey(), entry.getValue()); - return map; - } - } -} diff --git a/distributor-api/src/test/java/fr/xpdustry/distributor/api/util/ArcSetTest.java b/distributor-api/src/test/java/fr/xpdustry/distributor/api/util/ArcSetTest.java deleted file mode 100644 index 20e44ea9..00000000 --- a/distributor-api/src/test/java/fr/xpdustry/distributor/api/util/ArcSetTest.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.util; - -import arc.struct.ObjectSet; -import com.google.common.collect.testing.SetTestSuiteBuilder; -import com.google.common.collect.testing.TestStringSetGenerator; -import com.google.common.collect.testing.features.CollectionSize; -import com.google.common.collect.testing.features.SetFeature; -import java.util.Arrays; -import java.util.List; -import java.util.Set; -import junit.framework.TestResult; -import junit.framework.TestSuite; -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.Test; - -public final class ArcSetTest { - - @Test - void testSuite() { - final var suite = new TestSuite(); - suite.addTest(SetTestSuiteBuilder.using(new TestArcSetGenerator()) - .named("ArcSet") - .withFeatures(List.of(SetFeature.values())) - .withFeatures(List.of(CollectionSize.ANY)) - .createTestSuite()); - final var result = new TestResult(); - suite.run(result); - if (!result.wasSuccessful()) { - @SuppressWarnings("MismatchedQueryAndUpdateOfStringBuilder") // tf ? - final var builder = new StringBuilder(); - builder.append("ArcSet TEst suite failed:\n"); - result.failures().asIterator().forEachRemaining(failure -> builder.append(failure.toString()) - .append('\n')); - result.errors().asIterator().forEachRemaining(error -> builder.append(error.toString()) - .append('\n')); - Assertions.fail(builder.toString()); - } - } - - private static final class TestArcSetGenerator extends TestStringSetGenerator { - - @Override - protected Set create(String[] elements) { - final var set = new ArcSet(new ObjectSet<>()); - set.addAll(Arrays.asList(elements)); - return set; - } - } -} diff --git a/distributor-api/src/test/java/fr/xpdustry/distributor/api/util/PlayersTest.java b/distributor-api/src/test/java/fr/xpdustry/distributor/api/util/PlayersTest.java deleted file mode 100644 index db8d49ef..00000000 --- a/distributor-api/src/test/java/fr/xpdustry/distributor/api/util/PlayersTest.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.api.util; - -import fr.xpdustry.distributor.api.TestPlayer; -import java.util.Locale; -import mindustry.gen.Groups; -import mindustry.gen.Player; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -public final class PlayersTest { - - private Player player1; - private Player player2; - private Player player3; - private Player player4; - private Player player5; - - @BeforeEach - void createPlayers() { - Groups.init(); - - this.player1 = new TestPlayer("deez", "AAAAAAAAAAAAAAAAAAAAAA==", 1); - this.player2 = new TestPlayer("deez nuts", "BAAAAAAAAAAAAAAAAAAAAA==", 2); - this.player3 = new TestPlayer("phinner", "CAAAAAAAAAAAAAAAAAAAAA==", 3); - this.player4 = new TestPlayer("[green]zeta", "DAAAAAAAAAAAAAAAAAAAAA==", 4); - this.player5 = new TestPlayer("[cyan]zeta", "EAAAAAAAAAAAAAAAAAAAAA==", 5); - - Groups.player.add(this.player1); - Groups.player.add(this.player2); - Groups.player.add(this.player3); - Groups.player.add(this.player4); - Groups.player.add(this.player5); - } - - @AfterEach - void clearGroups() { - Groups.clear(); - } - - @Test - void test_find_by_name_simple() { - assertThat(Players.findPlayers("phinner")).singleElement().isEqualTo(this.player3); - } - - @Test - void test_find_by_name_colored() { - assertThat(Players.findPlayers("[green]phinner")).singleElement().isEqualTo(this.player3); - } - - @Test - void test_find_by_partial_name() { - assertThat(Players.findPlayers("de")).containsExactlyInAnyOrder(this.player1, this.player2); - } - - @Test - void test_find_by_exact_name() { - assertThat(Players.findPlayers("deez")).singleElement().isEqualTo(this.player1); - } - - @Test - void test_find_by_name_accents() { - assertThat(Players.findPlayers("phînnér")).singleElement().isEqualTo(this.player3); - } - - @Test - void test_find_by_multiple_exact_names() { - assertThat(Players.findPlayers("zeta")).containsExactlyInAnyOrder(this.player4, this.player5); - } - - @Test - void test_find_by_entity_id() { - assertThat(Players.findPlayers("#4")).singleElement().isEqualTo(this.player4); - } - - @Test - void test_find_by_uuid() { - assertThat(Players.findPlayers(this.player3.uuid())).isEmpty(); - assertThat(Players.findPlayers(this.player3.uuid(), true)) - .singleElement() - .isEqualTo(this.player3); - } - - @Test - void test_get_locale() { - this.player1.locale(Locale.FRANCE.toLanguageTag()); - assertThat(Players.getLocale(this.player1)).isEqualTo(Locale.FRANCE); - } -} diff --git a/distributor-bom/build.gradle.kts b/distributor-bom/build.gradle.kts deleted file mode 100644 index 7e5580cf..00000000 --- a/distributor-bom/build.gradle.kts +++ /dev/null @@ -1,22 +0,0 @@ -plugins { - `java-platform` - id("distributor.publishing-conventions") -} - -indra { - configurePublications { - from(components["javaPlatform"]) - } -} - -dependencies { - constraints { - for (subproject in rootProject.subprojects) { - if (subproject == project) { - continue - } - - api(project(subproject.path)) - } - } -} diff --git a/distributor-build-logic/build.gradle.kts b/distributor-build-logic/build.gradle.kts new file mode 100644 index 00000000..3ea60436 --- /dev/null +++ b/distributor-build-logic/build.gradle.kts @@ -0,0 +1,37 @@ +plugins { + `kotlin-dsl` + id("com.diffplug.spotless") version libs.versions.spotless +} + +repositories { + gradlePluginPortal() +} + +dependencies { + implementation(libs.toxopid) + implementation(libs.spotless) + implementation(libs.shadow) + implementation(libs.bundles.indra) + implementation(libs.errorprone.gradle) + implementation(libs.mammoth) + + // https://github.com/gradle/gradle/issues/15383#issuecomment-779893192 + implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location)) +} + +java { + sourceCompatibility = JavaVersion.toVersion(libs.versions.java.get()) + targetCompatibility = JavaVersion.toVersion(libs.versions.java.get()) +} + +spotless { + kotlin { + targetExclude("src/*/kotlin/**.gradle.kts", "build/generated-sources/**") + ktlint().setEditorConfigPath(file("../.editorconfig")) + } + + kotlinGradle { + target("*.gradle.kts", "src/*/kotlin/**.gradle.kts") + ktlint().setEditorConfigPath(file("../.editorconfig")) + } +} diff --git a/distributor-build-logic/settings.gradle.kts b/distributor-build-logic/settings.gradle.kts new file mode 100644 index 00000000..17a83ad3 --- /dev/null +++ b/distributor-build-logic/settings.gradle.kts @@ -0,0 +1,9 @@ +rootProject.name = "distributor-build-logic" + +dependencyResolutionManagement { + versionCatalogs { + register("libs") { + from(files("../gradle/libs.versions.toml")) // include from parent project + } + } +} diff --git a/buildSrc/src/main/kotlin/distributor.base-conventions.gradle.kts b/distributor-build-logic/src/main/kotlin/distributor4.base-conventions.gradle.kts similarity index 56% rename from buildSrc/src/main/kotlin/distributor.base-conventions.gradle.kts rename to distributor-build-logic/src/main/kotlin/distributor4.base-conventions.gradle.kts index deab41df..28ec964e 100644 --- a/buildSrc/src/main/kotlin/distributor.base-conventions.gradle.kts +++ b/distributor-build-logic/src/main/kotlin/distributor4.base-conventions.gradle.kts @@ -10,26 +10,23 @@ plugins { repositories { mavenCentral() + maven("https://maven.xpdustry.com/mindustry") { + name = "xpdustry-mindustry" + mavenContent { releasesOnly() } + } } dependencies { - compileOnlyApi("org.checkerframework:checker-qual:3.39.0") - - val junit = "5.10.0" - testImplementation("org.junit.jupiter:junit-jupiter-params:$junit") - testImplementation("org.junit.jupiter:junit-jupiter-api:$junit") - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$junit") - testImplementation("org.mockito:mockito-core:5.6.0") - testImplementation("org.assertj:assertj-core:3.24.2") - - annotationProcessor("com.uber.nullaway:nullaway:0.10.14") - errorprone("com.google.errorprone:error_prone_core:2.22.0") + compileOnlyApi(libs.jspecify) + errorprone(libs.errorprone.core) + errorprone(libs.nullaway) + testImplementation(libs.bundles.tests) } indra { javaVersions { - target(17) - minimumToolchain(17) + target(libs.versions.java.get().toInt()) + minimumToolchain(libs.versions.java.get().toInt()) } } @@ -39,9 +36,9 @@ indraSpotlessLicenser { spotless { java { - palantirJavaFormat() + palantirJavaFormat(libs.versions.palantir.get()) formatAnnotations() - importOrderFile(rootProject.file(".spotless/distributor.importorder")) + importOrder("", "\\#") custom("noWildcardImports") { if (it.contains("*;\n")) { throw Error("No wildcard imports allowed") @@ -50,9 +47,6 @@ spotless { } bumpThisNumberIfACustomStepChanges(1) } - kotlinGradle { - ktlint() - } } tasks.withType { @@ -63,11 +57,11 @@ tasks.withType { "BadImport", "FutureReturnValueIgnored", "InlineMeSuggester", - "EmptyCatch" + "EmptyCatch", ) if (!name.contains("test", true)) { check("NullAway", CheckSeverity.ERROR) - option("NullAway:AnnotatedPackages", "fr.xpdustry.distributor") + option("NullAway:AnnotatedPackages", "com.xpdustry.distributor") } } } diff --git a/distributor-build-logic/src/main/kotlin/distributor4.mindustry-conventions.gradle.kts b/distributor-build-logic/src/main/kotlin/distributor4.mindustry-conventions.gradle.kts new file mode 100644 index 00000000..b7f98a51 --- /dev/null +++ b/distributor-build-logic/src/main/kotlin/distributor4.mindustry-conventions.gradle.kts @@ -0,0 +1,68 @@ +import fr.xpdustry.toxopid.dsl.mindustryDependencies +import fr.xpdustry.toxopid.spec.ModMetadata + +plugins { + id("net.kyori.indra") + id("com.github.johnrengelman.shadow") + id("fr.xpdustry.toxopid") +} + +val extension = project.extensions.findOrCreateExtension("module") + +toxopid { + compileVersion.set(libs.versions.mindustry) + platforms.add(fr.xpdustry.toxopid.spec.ModPlatform.HEADLESS) +} + +dependencies { + mindustryDependencies() +} + +tasks.runMindustryClient { + mods.setFrom() +} + +tasks.runMindustryServer { + mods.from(tasks.shadowJar) + mods.from(extension.dependencies.map { projects -> projects.map { it.tasks.shadowJar } }) +} + +tasks.register("getArtifactPath") { + doLast { println(tasks.shadowJar.get().archiveFile.get().toString()) } +} + +tasks.shadowJar { + doFirst { + val metadata = + ModMetadata( + name = extension.identifier.get(), + displayName = extension.display.get(), + description = extension.description.get(), + version = project.version.toString(), + repo = "xpdustry/distributor", + author = "xpdustry", + minGameVersion = libs.versions.mindustry.get().substring(1), + main = extension.main.get(), + java = true, + hidden = true, + dependencies = + extension.dependencies.get() + .map { it.extensions.getByType().identifier.get() } + .toMutableList(), + ) + + val temp = temporaryDir.resolve("plugin.json") + temp.writeText(metadata.toJson(true)) + from(temp) + } + + archiveClassifier.set("plugin") + from(rootProject.file("LICENSE.md")) { + into("META-INF") + } + mergeServiceFiles() +} + +tasks.build { + dependsOn(tasks.shadowJar) +} diff --git a/distributor-build-logic/src/main/kotlin/distributor4.parent-conventions.gradle.kts b/distributor-build-logic/src/main/kotlin/distributor4.parent-conventions.gradle.kts new file mode 100644 index 00000000..251ffbad --- /dev/null +++ b/distributor-build-logic/src/main/kotlin/distributor4.parent-conventions.gradle.kts @@ -0,0 +1,20 @@ +import com.diffplug.gradle.spotless.SpotlessExtensionPredeclare + +plugins { + id("com.diffplug.spotless") + id("net.kyori.indra.git") +} + +repositories { + mavenCentral() +} + +spotless { + predeclareDeps() +} + +extensions.configure { + java { palantirJavaFormat(libs.versions.palantir.get()) } + kotlin { ktlint(libs.versions.ktlint.get()) } + kotlinGradle { ktlint(libs.versions.ktlint.get()) } +} diff --git a/distributor-build-logic/src/main/kotlin/extensions.kt b/distributor-build-logic/src/main/kotlin/extensions.kt new file mode 100644 index 00000000..e0b45f71 --- /dev/null +++ b/distributor-build-logic/src/main/kotlin/extensions.kt @@ -0,0 +1,17 @@ +import net.kyori.mammoth.Extensions +import org.gradle.accessors.dm.LibrariesForLibs +import org.gradle.api.Project +import org.gradle.api.plugins.ExtensionContainer +import org.gradle.kotlin.dsl.the + +internal val Project.libs: LibrariesForLibs get() = the() + +inline fun ExtensionContainer.findOrCreateExtension(name: String): T = Extensions.findOrCreate(this, name, T::class.java) + +open class DistributorModuleExtension(project: Project) { + val identifier = project.objects.property(String::class.java) + val display = project.objects.property(String::class.java) + val main = project.objects.property(String::class.java) + val description = project.objects.property(String::class.java) + val dependencies = project.objects.setProperty(Project::class.java) +} diff --git a/distributor-command-cloud/build.gradle.kts b/distributor-command-cloud/build.gradle.kts new file mode 100644 index 00000000..f27448e2 --- /dev/null +++ b/distributor-command-cloud/build.gradle.kts @@ -0,0 +1,23 @@ +plugins { + id("distributor4.base-conventions") + id("distributor4.mindustry-conventions") +} + +repositories { + maven("https://oss.sonatype.org/content/repositories/snapshots/") { + name = "sonatype-oss-snapshots" + mavenContent { snapshotsOnly() } + } +} + +module { + identifier = "distributor-command-cloud" + display = "DistributorLoggerSimple" + main = "com.xpdustry.distributor.logger.simple.DistributorLoggerPlugin" + description = "Simple slf4j logger implementation for plugins." +} + +dependencies { + api(libs.cloud.core) + compileOnly(project(":distributor-common")) +} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/command/ArcCaptionKeys.java b/distributor-command-cloud/src/main/java/com/xpdustry/distributor/command/cloud/ArcCaptionKeys.java similarity index 93% rename from distributor-api/src/main/java/fr/xpdustry/distributor/api/command/ArcCaptionKeys.java rename to distributor-command-cloud/src/main/java/com/xpdustry/distributor/command/cloud/ArcCaptionKeys.java index 2b4fd24a..c3cdc438 100644 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/command/ArcCaptionKeys.java +++ b/distributor-command-cloud/src/main/java/com/xpdustry/distributor/command/cloud/ArcCaptionKeys.java @@ -1,7 +1,7 @@ /* * Distributor, a feature-rich framework for Mindustry plugins. * - * Copyright (C) 2023 Xpdustry + * Copyright (C) 2024 Xpdustry * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,13 +16,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package fr.xpdustry.distributor.api.command; +package com.xpdustry.distributor.command.cloud; -import cloud.commandframework.captions.Caption; -import cloud.commandframework.captions.StandardCaptionKeys; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import org.incendo.cloud.caption.Caption; +import org.incendo.cloud.caption.StandardCaptionKeys; /** * {@link Caption} instances for {@link ArcCommandManager} error messages. diff --git a/distributor-command-cloud/src/main/java/com/xpdustry/distributor/command/cloud/ArcCommandManager.java b/distributor-command-cloud/src/main/java/com/xpdustry/distributor/command/cloud/ArcCommandManager.java new file mode 100644 index 00000000..7cfb665c --- /dev/null +++ b/distributor-command-cloud/src/main/java/com/xpdustry/distributor/command/cloud/ArcCommandManager.java @@ -0,0 +1,124 @@ +/* + * Distributor, a feature-rich framework for Mindustry plugins. + * + * Copyright (C) 2024 Xpdustry + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.xpdustry.distributor.command.cloud; + +import com.xpdustry.distributor.core.command.CommandSender; +import com.xpdustry.distributor.core.plugin.MindustryPlugin; +import com.xpdustry.distributor.core.plugin.PluginAware; +import io.leangen.geantyref.TypeToken; +import java.text.MessageFormat; +import mindustry.game.Team; +import mindustry.gen.Player; +import mindustry.net.Administration; +import org.incendo.cloud.CloudCapability; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.SenderMapper; +import org.incendo.cloud.SenderMapperHolder; +import org.incendo.cloud.execution.ExecutionCoordinator; +import org.incendo.cloud.internal.CommandRegistrationHandler; +import org.incendo.cloud.parser.ParserParameters; +import org.jspecify.annotations.NonNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ArcCommandManager extends CommandManager + implements PluginAware, SenderMapperHolder { + + private final MindustryPlugin plugin; + private final SenderMapper mapper; + private final Logger logger; + + public ArcCommandManager( + final MindustryPlugin plugin, + final ExecutionCoordinator coordinator, + final SenderMapper mapper) { + super(coordinator, CommandRegistrationHandler.nullCommandRegistrationHandler()); + this.plugin = plugin; + this.mapper = mapper; + this.logger = LoggerFactory.getLogger(this.getClass()); + + this.registerCapability(CloudCapability.StandardCapabilities.ROOT_COMMAND_DELETION); + + this.registerDefaultExceptionHandlers(); + + this.captionRegistry().registerProvider((caption, sender) -> { + final var source = DistributorProvider.get().getGlobalLocalizationSource(); + final var locale = + this.getBackwardsCommandSenderMapper().apply(sender).getLocale(); + final var format = source.localize(caption.getKey(), locale); + return format != null ? format.toPattern() : "???" + caption.getKey() + "???"; + }); + + this.captionFormatter((key, recipient, caption, variables) -> { + final var arguments = variables.toArray(); + try { + return MessageFormat.format(caption, arguments); + } catch (final IllegalArgumentException e) { + this.plugin.getLogger().error("Failed to format {}.", caption, e); + return "???" + caption + "???"; + } + }); + + this.parserRegistry() + .registerAnnotationMapper( + AllTeams.class, + (annotation, typeToken) -> + ParserParameters.single(ArcParserParameters.TEAM_MODE, TeamMode.ALL)); + + this.parserRegistry() + .registerParserSupplier(TypeToken.get(Player.class), params -> new PlayerArgument.PlayerParser<>()); + + this.parserRegistry() + .registerParserSupplier( + TypeToken.get(Administration.PlayerInfo.class), + params -> new PlayerInfoArgument.PlayerInfoParser<>()); + + this.parserRegistry() + .registerParserSupplier( + TypeToken.get(Team.class), + params -> new TeamArgument.TeamParser<>( + params.get(ArcParserParameters.TEAM_MODE, TeamMode.BASE))); + } + + @Override + public boolean hasPermission(final @NonNull C sender, final String permission) { + return permission.isEmpty() || senderMapper().reverse(sender).isServer(); // TODO Add permission + } + + @Override + public final MindustryPlugin getPlugin() { + return this.plugin; + } + + @Override + public final @NonNull SenderMapper senderMapper() { + return this.mapper; + } + + protected void registerDefaultExceptionHandlers() { + this.registerDefaultExceptionHandlers( + triplet -> { + final var context = triplet.first(); + senderMapper() + .reverse(context.sender()) + .sendWarning(context.formatCaption(triplet.second(), triplet.third())); + }, + pair -> logger.error(pair.first(), pair.second())); + } +} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/command/ArcRegistrationHandler.java b/distributor-command-cloud/src/main/java/com/xpdustry/distributor/command/cloud/ArcRegistrationHandler.java similarity index 61% rename from distributor-api/src/main/java/fr/xpdustry/distributor/api/command/ArcRegistrationHandler.java rename to distributor-command-cloud/src/main/java/com/xpdustry/distributor/command/cloud/ArcRegistrationHandler.java index b729adca..796269f2 100644 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/command/ArcRegistrationHandler.java +++ b/distributor-command-cloud/src/main/java/com/xpdustry/distributor/command/cloud/ArcRegistrationHandler.java @@ -1,7 +1,7 @@ /* * Distributor, a feature-rich framework for Mindustry plugins. * - * Copyright (C) 2023 Xpdustry + * Copyright (C) 2024 Xpdustry * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,25 +16,24 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package fr.xpdustry.distributor.api.command; +package com.xpdustry.distributor.command.cloud; import arc.struct.ObjectMap; import arc.util.CommandHandler; -import cloud.commandframework.Command; -import cloud.commandframework.CommandManager.ManagerSettings; -import cloud.commandframework.arguments.StaticArgument; -import cloud.commandframework.internal.CommandRegistrationHandler; -import cloud.commandframework.meta.CommandMeta; -import fr.xpdustry.distributor.api.util.ArcCollections; +import com.xpdustry.distributor.core.collection.ArcCollections; import java.lang.reflect.Field; import java.util.HashSet; import java.util.Set; +import org.incendo.cloud.Command; +import org.incendo.cloud.component.CommandComponent; +import org.incendo.cloud.internal.CommandRegistrationHandler; +import org.incendo.cloud.setting.ManagerSetting; /** * This class acts as a bridge between the {@link ArcCommandManager} and the {@link CommandHandler}, * by registering cloud commands as arc commands. */ -final class ArcRegistrationHandler implements CommandRegistrationHandler { +final class ArcRegistrationHandler implements CommandRegistrationHandler { private static final Field COMMAND_MAP_ACCESSOR; @@ -50,7 +49,7 @@ final class ArcRegistrationHandler implements CommandRegistrationHandler { private final ArcCommandManager manager; private final CommandHandler handler; private final ObjectMap commands; - private final Set> registered = new HashSet<>(); + private final Set> registered = new HashSet<>(); @SuppressWarnings("unchecked") ArcRegistrationHandler(final ArcCommandManager manager, final CommandHandler handler) { @@ -64,30 +63,31 @@ final class ArcRegistrationHandler implements CommandRegistrationHandler { } @Override - public boolean registerCommand(final Command command) { - final var root = (StaticArgument) command.getArguments().get(0); - - if (!this.registered.add(root)) { + public boolean registerCommand(final Command command) { + if (!this.registered.add(command.rootComponent())) { return false; } - var description = - command.getComponents().get(0).getArgumentDescription().getDescription(); + var description = command.rootComponent().description(); if (description.isEmpty()) { - description = command.getCommandMeta().getOrDefault(CommandMeta.DESCRIPTION, ""); + description = command.commandDescription().description(); } - if (this.manager.getSetting(ManagerSettings.OVERRIDE_EXISTING_COMMANDS)) { - root.getAliases().forEach(this.handler::removeCommand); + if (this.manager.settings().get(ManagerSetting.OVERRIDE_EXISTING_COMMANDS)) { + command.rootComponent().aliases().forEach(this.handler::removeCommand); } // Register with the primary name - this.addCommand(new ArcCommand<>( - root.getName(), description, this.manager, false, this.commands.containsKey(root.getName()))); + this.addCommand(new CloudCommandFacade<>( + command.rootComponent().name(), + description, + this.manager, + false, + this.commands.containsKey(command.rootComponent().name()))); - for (final var alias : root.getAlternativeAliases()) { + for (final var alias : command.rootComponent().alternativeAliases()) { if (!this.commands.containsKey(alias)) { - this.addCommand(new ArcCommand<>(alias, description, this.manager, true, false)); + this.addCommand(new CloudCommandFacade<>(alias, description, this.manager, true, false)); } } @@ -95,18 +95,18 @@ public boolean registerCommand(final Command command) { } @Override - public void unregisterRootCommand(final StaticArgument root) { + public void unregisterRootCommand(final CommandComponent root) { this.registered.remove(root); for (final var command : ArcCollections.immutableList(this.handler.getCommandList())) { - if (command instanceof final ArcCommand cloud - && cloud.getManager() == this.manager - && root.getAliases().contains(cloud.getRealName())) { + if (command instanceof final CloudCommandFacade cloud + && cloud.manager == this.manager + && root.aliases().contains(cloud.getRealName())) { this.handler.removeCommand(command.text); } } } - private void addCommand(final ArcCommand command) { + private void addCommand(final CloudCommandFacade command) { this.commands.put(command.text, command); this.handler.getCommandList().add(command); } diff --git a/distributor-command-cloud/src/main/java/com/xpdustry/distributor/command/cloud/CloudCommandFacade.java b/distributor-command-cloud/src/main/java/com/xpdustry/distributor/command/cloud/CloudCommandFacade.java new file mode 100644 index 00000000..bcdf4884 --- /dev/null +++ b/distributor-command-cloud/src/main/java/com/xpdustry/distributor/command/cloud/CloudCommandFacade.java @@ -0,0 +1,190 @@ +/* + * Distributor, a feature-rich framework for Mindustry plugins. + * + * Copyright (C) 2024 Xpdustry + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.xpdustry.distributor.command.cloud; + +import arc.util.CommandHandler; +import com.xpdustry.distributor.core.command.CommandDescription; +import com.xpdustry.distributor.core.command.CommandElement; +import com.xpdustry.distributor.core.command.CommandFacade; +import com.xpdustry.distributor.core.command.CommandHelp; +import com.xpdustry.distributor.core.command.CommandSender; +import com.xpdustry.distributor.core.plugin.MindustryPlugin; +import java.util.ArrayList; +import java.util.List; +import mindustry.gen.Player; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.incendo.cloud.Command; +import org.incendo.cloud.description.Description; +import org.incendo.cloud.help.HelpQuery; +import org.incendo.cloud.help.result.MultipleCommandResult; +import org.incendo.cloud.help.result.VerboseCommandResult; +import org.incendo.cloud.parser.flag.CommandFlag; + +/** + * This special command class delegates its call to its command manager. + */ +final class CloudCommandFacade extends CommandHandler.Command implements CommandFacade { + + final ArcCommandManager manager; + private final boolean alias; + private final boolean prefixed; + private final String realName; + private final Description cloudDescription; + + CloudCommandFacade( + final String name, + final Description description, + final ArcCommandManager manager, + final boolean alias, + final boolean prefixed) { + super( + (prefixed ? manager.getPlugin().getDescriptor().getName() + ":" : "") + name, + "[args...]", + description.textDescription(), + new ArcCommandRunner<>(name, manager)); + this.manager = manager; + this.alias = alias; + this.prefixed = prefixed; + this.realName = name; + this.cloudDescription = description; + } + + /** + * Returns the real name of the command, without the prefix. + */ + @Override + public String getRealName() { + return this.realName; + } + + @Override + public String getName() { + return this.text; + } + + @Override + public CommandDescription getDescription() { + return this.cloudDescription::textDescription; + } + + /** + * Returns whether this command is an alias. + */ + @Override + public boolean isAlias() { + return this.alias; + } + + /** + * Returns whether this command is prefixed with the plugin name. + */ + @Override + public boolean isPrefixed() { + return this.prefixed; + } + + @Override + public boolean isVisible(final CommandSender sender) { + final var mapped = manager.senderMapper().map(sender); + return manager.commands().stream() + .anyMatch(command -> command.rootComponent().name().equalsIgnoreCase(realName) + && manager.testPermission(mapped, command.commandPermission()) + .allowed()); + } + + @Override + public CommandHelp getHelp(final CommandSender sender, final String query) { + final var mapped = manager.senderMapper().map(sender); + final var result = manager.createHelpHandler().query(HelpQuery.of(mapped, getRealName() + " " + query)); + if (result instanceof MultipleCommandResult multi) { + return CommandHelp.Suggestion.of(multi.longestPath(), multi.childSuggestions()); + } else if (result instanceof VerboseCommandResult verbose) { + final var command = verbose.entry().command(); + return CommandHelp.Entry.of( + verbose.entry().syntax(), + command.commandDescription().description()::textDescription, + getArguments(command), + getFlags(command)); + } else { + return CommandHelp.Empty.getInstance(); + } + } + + private List getArguments(final Command command) { + return command.nonFlagArguments().stream() + .map(component -> CommandElement.Argument.of( + component.name(), + component.description()::textDescription, + component.alternativeAliases(), + switch (component.type()) { + case LITERAL -> CommandElement.Argument.Kind.LITERAL; + case REQUIRED_VARIABLE -> CommandElement.Argument.Kind.REQUIRED; + case OPTIONAL_VARIABLE -> CommandElement.Argument.Kind.OPTIONAL; + case FLAG -> throw new IllegalStateException("impossible"); + })) + .toList(); + } + + private List getFlags(final Command command) { + final var flags = new ArrayList(); + final var parser = command.flagParser(); + if (parser != null) { + for (final var flag : parser.flags()) { + flags.add(CommandElement.Flag.of( + flag.name(), + flag.description()::textDescription, + flag.aliases(), + flag.mode() == CommandFlag.FlagMode.REPEATABLE)); + } + } + return flags; + } + + @Override + public MindustryPlugin getPlugin() { + return this.manager.getPlugin(); + } + + @SuppressWarnings("ClassCanBeRecord") + private static final class ArcCommandRunner implements CommandHandler.CommandRunner { + + private final String name; + private final ArcCommandManager manager; + + private ArcCommandRunner(final String name, final ArcCommandManager manager) { + this.name = name; + this.manager = manager; + } + + @SuppressWarnings("FutureReturnValueIgnored") + @Override + public void accept(final String[] args, final @Nullable Player player) { + final var sender = this.manager + .senderMapper() + .map(player != null ? CommandSender.player(player) : CommandSender.server()); + + final var input = new StringBuilder(this.name); + for (final var arg : args) { + input.append(' ').append(arg); + } + + this.manager.commandExecutor().executeCommand(sender, input.toString()); + } + } +} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/util/Priority.java b/distributor-command-cloud/src/main/java/com/xpdustry/distributor/command/cloud/PermissionChecker.java similarity index 76% rename from distributor-api/src/main/java/fr/xpdustry/distributor/api/util/Priority.java rename to distributor-command-cloud/src/main/java/com/xpdustry/distributor/command/cloud/PermissionChecker.java index 5f34ca40..9d6df2f5 100644 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/util/Priority.java +++ b/distributor-command-cloud/src/main/java/com/xpdustry/distributor/command/cloud/PermissionChecker.java @@ -1,7 +1,7 @@ /* * Distributor, a feature-rich framework for Mindustry plugins. * - * Copyright (C) 2023 Xpdustry + * Copyright (C) 2024 Xpdustry * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,15 +16,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package fr.xpdustry.distributor.api.util; +package com.xpdustry.distributor.command.cloud; -/** - * A utility class to represent the priority of an object. - */ -public enum Priority { - HIGHEST, - HIGH, - NORMAL, - LOW, - LOWEST -} +public interface PermissionChecker {} diff --git a/distributor-command-cloud/src/main/java/com/xpdustry/distributor/command/cloud/package-info.java b/distributor-command-cloud/src/main/java/com/xpdustry/distributor/command/cloud/package-info.java new file mode 100644 index 00000000..ce11a898 --- /dev/null +++ b/distributor-command-cloud/src/main/java/com/xpdustry/distributor/command/cloud/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package com.xpdustry.distributor.command.cloud; + +import org.jspecify.annotations.NullMarked; diff --git a/distributor-command-cloud/src/main/java/com/xpdustry/distributor/command/cloud/parser/TeamParser.java b/distributor-command-cloud/src/main/java/com/xpdustry/distributor/command/cloud/parser/TeamParser.java new file mode 100644 index 00000000..72ac2aec --- /dev/null +++ b/distributor-command-cloud/src/main/java/com/xpdustry/distributor/command/cloud/parser/TeamParser.java @@ -0,0 +1,123 @@ +/* + * Distributor, a feature-rich framework for Mindustry plugins. + * + * Copyright (C) 2024 Xpdustry + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.xpdustry.distributor.command.cloud.parser; + +import com.xpdustry.distributor.command.cloud.ArcCaptionKeys; +import java.util.Arrays; +import java.util.Locale; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import mindustry.game.Team; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.incendo.cloud.caption.CaptionVariable; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.context.CommandInput; +import org.incendo.cloud.exception.parsing.ParserException; +import org.incendo.cloud.parser.ArgumentParseResult; +import org.incendo.cloud.parser.ArgumentParser; +import org.incendo.cloud.suggestion.SuggestionProvider; + +public final class TeamParser implements ArgumentParser { + + private static final Map BASE_TEAMS = Arrays.stream(Team.baseTeams) + .collect(Collectors.toUnmodifiableMap(t -> t.name.toLowerCase(Locale.ROOT), Function.identity())); + + private static final Map ALL_TEAMS = Arrays.stream(Team.all) + .collect(Collectors.toUnmodifiableMap(t -> t.name.toLowerCase(Locale.ROOT), Function.identity())); + + private final TeamMode teamMode; + + public TeamParser(final TeamMode teamMode) { + this.teamMode = teamMode; + } + + @Override + public ArgumentParseResult parse(final CommandContext ctx, final CommandInput input) { + final var name = input.readString().toLowerCase(Locale.ROOT); + if (this.getTeamIndex().containsKey(name)) { + return ArgumentParseResult.success(this.getTeamIndex().get(name)); + } else { + return ArgumentParseResult.failure(new TeamParseException(name, ctx, this.teamMode)); + } + } + + @Override + public @NonNull SuggestionProvider suggestionProvider() { + return SuggestionProvider.suggestingStrings( + getTeamIndex().keySet().stream().sorted().toArray(String[]::new)); + } + + private Map getTeamIndex() { + return this.teamMode == TeamMode.ALL ? ALL_TEAMS : BASE_TEAMS; + } + + /** + * The parsing mode for a {@link TeamParser}. + */ + public enum TeamMode { + /** + * Only the 6 base teams can be used. + * + * @see Team#baseTeams + */ + BASE, + /** + * All 256 teams can be used. + * + * @see Team#all + */ + ALL + } + + /** + * Exception thrown when a team cannot be found for the given input and {@link TeamMode}. + */ + public static final class TeamParseException extends ParserException { + + private final String input; + private final TeamMode teamMode; + + /** + * Creates a new {@link TeamParseException}. + * + * @param input the input string + * @param ctx the command context + * @param teamMode the team mode + */ + public TeamParseException(final String input, final CommandContext ctx, final TeamMode teamMode) { + super( + TeamParser.class, + ctx, + ArcCaptionKeys.ARGUMENT_PARSE_FAILURE_TEAM, + CaptionVariable.of("input", input), + CaptionVariable.of("teamMode", teamMode.name())); + this.input = input; + this.teamMode = teamMode; + } + + public String getInput() { + return this.input; + } + + public TeamMode getTeamMode() { + return this.teamMode; + } + } +} diff --git a/distributor-command-cloud/src/main/java/com/xpdustry/distributor/command/cloud/parser/package-info.java b/distributor-command-cloud/src/main/java/com/xpdustry/distributor/command/cloud/parser/package-info.java new file mode 100644 index 00000000..2db49ad2 --- /dev/null +++ b/distributor-command-cloud/src/main/java/com/xpdustry/distributor/command/cloud/parser/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package com.xpdustry.distributor.command.cloud.parser; + +import org.jspecify.annotations.NullMarked; diff --git a/distributor-common/build.gradle.kts b/distributor-common/build.gradle.kts new file mode 100644 index 00000000..72104367 --- /dev/null +++ b/distributor-common/build.gradle.kts @@ -0,0 +1,19 @@ +plugins { + id("distributor4.base-conventions") + id("distributor4.mindustry-conventions") +} + +module { + identifier = "distributor-core" + display = "DistributorCore" + main = "com.xpdustry.distributor.core.DistributorCorePlugin" + description = "Core classes of distributor." + dependencies = setOf(project(":distributor-logging-simple")) +} + +dependencies { + compileOnlyApi(libs.immutables) + annotationProcessor(libs.immutables) + compileOnlyApi(libs.slf4j.api) + implementation(libs.geantyref) +} diff --git a/distributor-common/src/main/java/com/xpdustry/distributor/core/Distributor.java b/distributor-common/src/main/java/com/xpdustry/distributor/core/Distributor.java new file mode 100644 index 00000000..6d8bd2ab --- /dev/null +++ b/distributor-common/src/main/java/com/xpdustry/distributor/core/Distributor.java @@ -0,0 +1,21 @@ +/* + * Distributor, a feature-rich framework for Mindustry plugins. + * + * Copyright (C) 2024 Xpdustry + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.xpdustry.distributor.core; + +public interface Distributor {} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/event/EventSubscription.java b/distributor-common/src/main/java/com/xpdustry/distributor/core/DistributorCorePlugin.java similarity index 74% rename from distributor-api/src/main/java/fr/xpdustry/distributor/api/event/EventSubscription.java rename to distributor-common/src/main/java/com/xpdustry/distributor/core/DistributorCorePlugin.java index 3c364f97..8c62c169 100644 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/event/EventSubscription.java +++ b/distributor-common/src/main/java/com/xpdustry/distributor/core/DistributorCorePlugin.java @@ -1,7 +1,7 @@ /* * Distributor, a feature-rich framework for Mindustry plugins. * - * Copyright (C) 2023 Xpdustry + * Copyright (C) 2024 Xpdustry * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,15 +16,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package fr.xpdustry.distributor.api.event; +package com.xpdustry.distributor.core; -/** - * A subscription to an event. - */ -public interface EventSubscription { +import com.xpdustry.distributor.core.plugin.AbstractMindustryPlugin; - /** - * Unsubscribes the bound subscriber from the event. - */ - void unsubscribe(); -} +public final class DistributorCorePlugin extends AbstractMindustryPlugin {} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/util/ArcCollections.java b/distributor-common/src/main/java/com/xpdustry/distributor/core/collection/ArcCollections.java similarity index 98% rename from distributor-api/src/main/java/fr/xpdustry/distributor/api/util/ArcCollections.java rename to distributor-common/src/main/java/com/xpdustry/distributor/core/collection/ArcCollections.java index de366692..c80bd1c5 100644 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/util/ArcCollections.java +++ b/distributor-common/src/main/java/com/xpdustry/distributor/core/collection/ArcCollections.java @@ -1,7 +1,7 @@ /* * Distributor, a feature-rich framework for Mindustry plugins. * - * Copyright (C) 2023 Xpdustry + * Copyright (C) 2024 Xpdustry * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,7 +16,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package fr.xpdustry.distributor.api.util; +package com.xpdustry.distributor.core.collection; import arc.struct.ObjectMap; import arc.struct.ObjectSet; diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/util/ArcList.java b/distributor-common/src/main/java/com/xpdustry/distributor/core/collection/ArcList.java similarity index 97% rename from distributor-api/src/main/java/fr/xpdustry/distributor/api/util/ArcList.java rename to distributor-common/src/main/java/com/xpdustry/distributor/core/collection/ArcList.java index ec069610..61e93c64 100644 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/util/ArcList.java +++ b/distributor-common/src/main/java/com/xpdustry/distributor/core/collection/ArcList.java @@ -1,7 +1,7 @@ /* * Distributor, a feature-rich framework for Mindustry plugins. * - * Copyright (C) 2023 Xpdustry + * Copyright (C) 2024 Xpdustry * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,7 +16,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package fr.xpdustry.distributor.api.util; +package com.xpdustry.distributor.core.collection; import arc.struct.Seq; import java.io.Serial; diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/util/ArcMap.java b/distributor-common/src/main/java/com/xpdustry/distributor/core/collection/ArcMap.java similarity index 93% rename from distributor-api/src/main/java/fr/xpdustry/distributor/api/util/ArcMap.java rename to distributor-common/src/main/java/com/xpdustry/distributor/core/collection/ArcMap.java index c9494fc5..48ad55ba 100644 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/util/ArcMap.java +++ b/distributor-common/src/main/java/com/xpdustry/distributor/core/collection/ArcMap.java @@ -1,7 +1,7 @@ /* * Distributor, a feature-rich framework for Mindustry plugins. * - * Copyright (C) 2023 Xpdustry + * Copyright (C) 2024 Xpdustry * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,7 +16,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package fr.xpdustry.distributor.api.util; +package com.xpdustry.distributor.core.collection; import arc.struct.ObjectMap; import java.io.Serial; @@ -27,7 +27,7 @@ import java.util.Map; import java.util.Objects; import java.util.Set; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A wrapper {@link Map} for an {@link ObjectMap}. @@ -120,14 +120,14 @@ private void checkNullKey(final @Nullable Object o) { } } - private final class EntrySet extends AbstractSet> { + private final class EntrySet extends AbstractSet> { @Override public boolean remove(final @Nullable Object o) { if (o == null) { return false; } - final var entry = (Map.Entry) o; + final var entry = (Entry) o; return ArcMap.this.remove(entry.getKey(), entry.getValue()); } @@ -142,7 +142,7 @@ public void clear() { } @Override - public Iterator> iterator() { + public Iterator> iterator() { return new EntryIterator(); } } @@ -167,7 +167,7 @@ public Entry next() { } } - private final class ArcMapEntry implements Map.Entry, Serializable { + private final class ArcMapEntry implements Entry, Serializable { @Serial private static final long serialVersionUID = -2069200917533589764L; diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/util/ArcSet.java b/distributor-common/src/main/java/com/xpdustry/distributor/core/collection/ArcSet.java similarity index 94% rename from distributor-api/src/main/java/fr/xpdustry/distributor/api/util/ArcSet.java rename to distributor-common/src/main/java/com/xpdustry/distributor/core/collection/ArcSet.java index 8539da97..5e7666a1 100644 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/util/ArcSet.java +++ b/distributor-common/src/main/java/com/xpdustry/distributor/core/collection/ArcSet.java @@ -1,7 +1,7 @@ /* * Distributor, a feature-rich framework for Mindustry plugins. * - * Copyright (C) 2023 Xpdustry + * Copyright (C) 2024 Xpdustry * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,7 +16,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package fr.xpdustry.distributor.api.util; +package com.xpdustry.distributor.core.collection; import arc.struct.ObjectSet; import java.io.Serial; @@ -24,7 +24,7 @@ import java.util.AbstractSet; import java.util.Iterator; import java.util.Set; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A wrapper {@link Set} for an {@link ObjectSet}. diff --git a/distributor-common/src/main/java/com/xpdustry/distributor/core/collection/package-info.java b/distributor-common/src/main/java/com/xpdustry/distributor/core/collection/package-info.java new file mode 100644 index 00000000..39d38c51 --- /dev/null +++ b/distributor-common/src/main/java/com/xpdustry/distributor/core/collection/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package com.xpdustry.distributor.core.collection; + +import org.jspecify.annotations.NullMarked; diff --git a/distributor-common/src/main/java/com/xpdustry/distributor/core/command/ArcCommandFacade.java b/distributor-common/src/main/java/com/xpdustry/distributor/core/command/ArcCommandFacade.java new file mode 100644 index 00000000..93bac05c --- /dev/null +++ b/distributor-common/src/main/java/com/xpdustry/distributor/core/command/ArcCommandFacade.java @@ -0,0 +1,73 @@ +/* + * Distributor, a feature-rich framework for Mindustry plugins. + * + * Copyright (C) 2024 Xpdustry + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.xpdustry.distributor.core.command; + +import arc.util.CommandHandler; +import com.xpdustry.distributor.core.plugin.MindustryPlugin; +import java.util.List; +import org.jspecify.annotations.Nullable; + +final class ArcCommandFacade implements CommandFacade { + + private final CommandHandler.Command command; + + ArcCommandFacade(final CommandHandler.Command command) { + this.command = command; + } + + @Override + public String getRealName() { + return command.text; + } + + @Override + public String getName() { + return command.text; + } + + @Override + public CommandDescription getDescription() { + return () -> command.description; + } + + @Override + public boolean isAlias() { + return false; + } + + @Override + public boolean isPrefixed() { + return false; + } + + @Override + public boolean isVisible(final CommandSender sender) { + return true; + } + + @Override + public CommandHelp getHelp(final CommandSender sender, final String query) { + return CommandHelp.Entry.of(command.paramText, getDescription(), List.of(), List.of()); + } + + @Override + public @Nullable MindustryPlugin getPlugin() { + return null; + } +} diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/scheduler/ScheduledPluginTask.java b/distributor-common/src/main/java/com/xpdustry/distributor/core/command/CommandDescription.java similarity index 75% rename from distributor-core/src/main/java/fr/xpdustry/distributor/core/scheduler/ScheduledPluginTask.java rename to distributor-common/src/main/java/com/xpdustry/distributor/core/command/CommandDescription.java index 0770cf2e..a8ffd72c 100644 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/scheduler/ScheduledPluginTask.java +++ b/distributor-common/src/main/java/com/xpdustry/distributor/core/command/CommandDescription.java @@ -1,7 +1,7 @@ /* * Distributor, a feature-rich framework for Mindustry plugins. * - * Copyright (C) 2023 Xpdustry + * Copyright (C) 2024 Xpdustry * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,11 +16,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package fr.xpdustry.distributor.core.scheduler; +package com.xpdustry.distributor.core.command; -import fr.xpdustry.distributor.api.scheduler.PluginTask; +public interface CommandDescription { -public interface ScheduledPluginTask extends PluginTask, Runnable { + String getText(); - long getNextExecutionTime(); + default String getText(final CommandSender sender) { + return this.getText(); + } } diff --git a/distributor-common/src/main/java/com/xpdustry/distributor/core/command/CommandElement.java b/distributor-common/src/main/java/com/xpdustry/distributor/core/command/CommandElement.java new file mode 100644 index 00000000..185ff407 --- /dev/null +++ b/distributor-common/src/main/java/com/xpdustry/distributor/core/command/CommandElement.java @@ -0,0 +1,70 @@ +/* + * Distributor, a feature-rich framework for Mindustry plugins. + * + * Copyright (C) 2024 Xpdustry + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.xpdustry.distributor.core.command; + +import com.xpdustry.distributor.core.internal.GeneratedDataClass; +import java.util.Collection; +import org.immutables.value.Value; + +public sealed interface CommandElement { + + String getName(); + + CommandDescription getDescription(); + + Collection getAliases(); + + @SuppressWarnings("immutables:subtype") + @GeneratedDataClass + @Value.Immutable + sealed interface Argument extends CommandElement permits ImmutableArgument { + + static Argument of( + final String name, + final CommandDescription description, + final Collection aliases, + final Kind kind) { + return ImmutableArgument.of(name, description, aliases, kind); + } + + Kind getKind(); + + enum Kind { + LITERAL, + REQUIRED, + OPTIONAL, + } + } + + @SuppressWarnings("immutables:subtype") + @GeneratedDataClass + @Value.Immutable + sealed interface Flag extends CommandElement permits ImmutableFlag { + + static Flag of( + final String name, + final CommandDescription description, + final Collection aliases, + final boolean repeatable) { + return ImmutableFlag.of(name, description, aliases, repeatable); + } + + boolean isRepeatable(); + } +} diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/util/Tristate.java b/distributor-common/src/main/java/com/xpdustry/distributor/core/command/CommandFacade.java similarity index 53% rename from distributor-api/src/main/java/fr/xpdustry/distributor/api/util/Tristate.java rename to distributor-common/src/main/java/com/xpdustry/distributor/core/command/CommandFacade.java index 12efe3ae..ccccf12a 100644 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/util/Tristate.java +++ b/distributor-common/src/main/java/com/xpdustry/distributor/core/command/CommandFacade.java @@ -1,7 +1,7 @@ /* * Distributor, a feature-rich framework for Mindustry plugins. * - * Copyright (C) 2023 Xpdustry + * Copyright (C) 2024 Xpdustry * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,29 +16,32 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package fr.xpdustry.distributor.api.util; +package com.xpdustry.distributor.core.command; -import org.checkerframework.checker.nullness.qual.Nullable; +import arc.util.CommandHandler; +import com.xpdustry.distributor.core.plugin.MindustryPlugin; +import org.jspecify.annotations.Nullable; -/** - * A ternary boolean type, used by the permission system. - */ -public enum Tristate { - FALSE(false), - TRUE(true), - UNDEFINED(false); +public interface CommandFacade { - private final boolean value; + String getRealName(); - Tristate(final boolean value) { - this.value = value; - } + String getName(); - public static Tristate of(final @Nullable Boolean state) { - return state == null ? UNDEFINED : state ? TRUE : FALSE; - } + CommandDescription getDescription(); + + boolean isAlias(); + + boolean isPrefixed(); + + boolean isVisible(final CommandSender sender); + + CommandHelp getHelp(final CommandSender sender, final String query); + + @Nullable MindustryPlugin getPlugin(); + + interface Factory { - public boolean asBoolean() { - return this.value; + CommandFacade create(final CommandHandler.Command command); } } diff --git a/distributor-common/src/main/java/com/xpdustry/distributor/core/command/CommandHelp.java b/distributor-common/src/main/java/com/xpdustry/distributor/core/command/CommandHelp.java new file mode 100644 index 00000000..38a0a90e --- /dev/null +++ b/distributor-common/src/main/java/com/xpdustry/distributor/core/command/CommandHelp.java @@ -0,0 +1,71 @@ +/* + * Distributor, a feature-rich framework for Mindustry plugins. + * + * Copyright (C) 2024 Xpdustry + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.xpdustry.distributor.core.command; + +import com.xpdustry.distributor.core.internal.GeneratedDataClass; +import java.util.List; +import org.immutables.value.Value; + +public sealed interface CommandHelp { + + @GeneratedDataClass + @Value.Immutable + sealed interface Entry extends CommandHelp permits ImmutableEntry { + + static Entry of( + final String syntax, + final CommandDescription description, + final List arguments, + final List flags) { + return ImmutableEntry.of(syntax, description, arguments, flags); + } + + String getSyntax(); + + CommandDescription getDescription(); + + List getArguments(); + + List getFlags(); + } + + @GeneratedDataClass + @Value.Immutable + sealed interface Suggestion extends CommandHelp permits ImmutableSuggestion { + + static Suggestion of(final String longestSharedPath, final List childSuggestions) { + return ImmutableSuggestion.of(longestSharedPath, childSuggestions); + } + + String getLongestSharedPath(); + + List getChildSuggestions(); + } + + final class Empty implements CommandHelp { + + private static final Empty INSTANCE = new Empty(); + + private Empty() {} + + public static Empty getInstance() { + return INSTANCE; + } + } +} diff --git a/distributor-common/src/main/java/com/xpdustry/distributor/core/command/CommandSender.java b/distributor-common/src/main/java/com/xpdustry/distributor/core/command/CommandSender.java new file mode 100644 index 00000000..651dcfd0 --- /dev/null +++ b/distributor-common/src/main/java/com/xpdustry/distributor/core/command/CommandSender.java @@ -0,0 +1,60 @@ +/* + * Distributor, a feature-rich framework for Mindustry plugins. + * + * Copyright (C) 2024 Xpdustry + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.xpdustry.distributor.core.command; + +import mindustry.gen.Player; + +public interface CommandSender { + + default void sendWarning(final String text) {} + + default void sendMessage(final String text) {} + + static CommandSender player(final Player player) { + return new CommandSender() { + @Override + public boolean isPlayer() { + return true; + } + + @Override + public boolean isServer() { + return false; + } + }; + } + + static CommandSender server() { + return new CommandSender() { + @Override + public boolean isPlayer() { + return false; + } + + @Override + public boolean isServer() { + return true; + } + }; + } + + boolean isPlayer(); + + boolean isServer(); +} diff --git a/distributor-api/src/test/java/fr/xpdustry/distributor/api/command/argument/PlayerArgumentTest.java b/distributor-common/src/main/java/com/xpdustry/distributor/core/command/SimpleCommandFacadeFactory.java similarity index 56% rename from distributor-api/src/test/java/fr/xpdustry/distributor/api/command/argument/PlayerArgumentTest.java rename to distributor-common/src/main/java/com/xpdustry/distributor/core/command/SimpleCommandFacadeFactory.java index fd0d1dbc..a3bf06f1 100644 --- a/distributor-api/src/test/java/fr/xpdustry/distributor/api/command/argument/PlayerArgumentTest.java +++ b/distributor-common/src/main/java/com/xpdustry/distributor/core/command/SimpleCommandFacadeFactory.java @@ -1,7 +1,7 @@ /* * Distributor, a feature-rich framework for Mindustry plugins. * - * Copyright (C) 2023 Xpdustry + * Copyright (C) 2024 Xpdustry * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,19 +16,24 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package fr.xpdustry.distributor.api.command.argument; +package com.xpdustry.distributor.core.command; -import mindustry.gen.Player; +import arc.util.CommandHandler; -public final class PlayerArgumentTest extends AbstractPlayerLookupArgumentTest, Player> { +final class SimpleCommandFacadeFactory implements CommandFacade.Factory { - @Override - protected PlayerArgument createArgument() { - return PlayerArgument.of("argument"); + private static final SimpleCommandFacadeFactory INSTANCE = new SimpleCommandFacadeFactory(); + + static SimpleCommandFacadeFactory getInstance() { + return INSTANCE; } @Override - protected Player mapPlayer(final Player player) { - return player; + public CommandFacade create(final CommandHandler.Command command) { + if (command instanceof CommandFacade facade) { + return facade; + } else { + return new ArcCommandFacade(command); + } } } diff --git a/distributor-common/src/main/java/com/xpdustry/distributor/core/command/package-info.java b/distributor-common/src/main/java/com/xpdustry/distributor/core/command/package-info.java new file mode 100644 index 00000000..e9ed8b4f --- /dev/null +++ b/distributor-common/src/main/java/com/xpdustry/distributor/core/command/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package com.xpdustry.distributor.core.command; + +import org.jspecify.annotations.NullMarked; diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/event/EventHandler.java b/distributor-common/src/main/java/com/xpdustry/distributor/core/internal/GeneratedDataClass.java similarity index 61% rename from distributor-api/src/main/java/fr/xpdustry/distributor/api/event/EventHandler.java rename to distributor-common/src/main/java/com/xpdustry/distributor/core/internal/GeneratedDataClass.java index 20aa2286..31a84522 100644 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/event/EventHandler.java +++ b/distributor-common/src/main/java/com/xpdustry/distributor/core/internal/GeneratedDataClass.java @@ -1,7 +1,7 @@ /* * Distributor, a feature-rich framework for Mindustry plugins. * - * Copyright (C) 2023 Xpdustry + * Copyright (C) 2024 Xpdustry * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,26 +16,22 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package fr.xpdustry.distributor.api.event; +package com.xpdustry.distributor.core.internal; -import fr.xpdustry.distributor.api.util.Priority; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.immutables.value.Value; -/** - * Marks a method as an event handler, meaning it will be called by a {@link EventBus} when its corresponding event is - * posted. - *
- * The annotated method must have exactly one parameter, which is the event class. - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface EventHandler { - - /** - * The priority of the event handler. - */ - Priority priority() default Priority.NORMAL; -} +@Target({ElementType.PACKAGE, ElementType.TYPE}) +@Retention(RetentionPolicy.CLASS) +@Value.Style( + get = {"is*", "get*"}, + init = "set*", + build = "create", + allParameters = true, + visibility = Value.Style.ImplementationVisibility.PACKAGE, + builderVisibility = Value.Style.BuilderVisibility.PACKAGE, + defaults = @Value.Immutable(copy = false, builder = false)) +public @interface GeneratedDataClass {} diff --git a/distributor-common/src/main/java/com/xpdustry/distributor/core/package-info.java b/distributor-common/src/main/java/com/xpdustry/distributor/core/package-info.java new file mode 100644 index 00000000..549790b7 --- /dev/null +++ b/distributor-common/src/main/java/com/xpdustry/distributor/core/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package com.xpdustry.distributor.core; + +import org.jspecify.annotations.NullMarked; diff --git a/distributor-common/src/main/java/com/xpdustry/distributor/core/permission/Permissible.java b/distributor-common/src/main/java/com/xpdustry/distributor/core/permission/Permissible.java new file mode 100644 index 00000000..ba37c29b --- /dev/null +++ b/distributor-common/src/main/java/com/xpdustry/distributor/core/permission/Permissible.java @@ -0,0 +1,21 @@ +/* + * Distributor, a feature-rich framework for Mindustry plugins. + * + * Copyright (C) 2024 Xpdustry + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.xpdustry.distributor.core.permission; + +public interface Permissible {} diff --git a/distributor-common/src/main/java/com/xpdustry/distributor/core/permission/package-info.java b/distributor-common/src/main/java/com/xpdustry/distributor/core/permission/package-info.java new file mode 100644 index 00000000..f7f2d8a3 --- /dev/null +++ b/distributor-common/src/main/java/com/xpdustry/distributor/core/permission/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package com.xpdustry.distributor.core.permission; + +import org.jspecify.annotations.NullMarked; diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/util/MUUID.java b/distributor-common/src/main/java/com/xpdustry/distributor/core/player/MUUID.java similarity index 97% rename from distributor-api/src/main/java/fr/xpdustry/distributor/api/util/MUUID.java rename to distributor-common/src/main/java/com/xpdustry/distributor/core/player/MUUID.java index 9d6580d5..3038cf5c 100644 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/util/MUUID.java +++ b/distributor-common/src/main/java/com/xpdustry/distributor/core/player/MUUID.java @@ -1,7 +1,7 @@ /* * Distributor, a feature-rich framework for Mindustry plugins. * - * Copyright (C) 2023 Xpdustry + * Copyright (C) 2024 Xpdustry * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,13 +16,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package fr.xpdustry.distributor.api.util; +package com.xpdustry.distributor.core.player; import java.util.Base64; import java.util.Objects; import mindustry.gen.Player; import mindustry.net.Administration; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * The mindustry identity format. A combination of a UUID and a USID. diff --git a/distributor-api/src/test/java/fr/xpdustry/distributor/api/TestPlayer.java b/distributor-common/src/main/java/com/xpdustry/distributor/core/player/PlayerLookup.java similarity index 61% rename from distributor-api/src/test/java/fr/xpdustry/distributor/api/TestPlayer.java rename to distributor-common/src/main/java/com/xpdustry/distributor/core/player/PlayerLookup.java index 8a70b816..6425f5ee 100644 --- a/distributor-api/src/test/java/fr/xpdustry/distributor/api/TestPlayer.java +++ b/distributor-common/src/main/java/com/xpdustry/distributor/core/player/PlayerLookup.java @@ -1,7 +1,7 @@ /* * Distributor, a feature-rich framework for Mindustry plugins. * - * Copyright (C) 2023 Xpdustry + * Copyright (C) 2024 Xpdustry * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,27 +16,20 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package fr.xpdustry.distributor.api; +package com.xpdustry.distributor.core.player; +import java.util.List; +import java.util.concurrent.CompletableFuture; import mindustry.gen.Player; +import mindustry.net.Administration; -public final class TestPlayer extends Player { +public interface PlayerLookup { - private final String uuid; - - public TestPlayer(final String name, final String uuid) { - this.name(name); - this.uuid = uuid; + static PlayerLookup simple() { + return SimplePlayerLookup.INSTANCE; } - public TestPlayer(final String name, final String uuid, final int id) { - this.name(name); - this.id(id); - this.uuid = uuid; - } + List findOnlinePlayers(final String query, final boolean admin); - @Override - public String uuid() { - return this.uuid; - } + CompletableFuture> findOfflinePlayers(final String query, final boolean admin); } diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/util/Players.java b/distributor-common/src/main/java/com/xpdustry/distributor/core/player/SimplePlayerLookup.java similarity index 66% rename from distributor-api/src/main/java/fr/xpdustry/distributor/api/util/Players.java rename to distributor-common/src/main/java/com/xpdustry/distributor/core/player/SimplePlayerLookup.java index 127fd2f7..fe1d60fa 100644 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/util/Players.java +++ b/distributor-common/src/main/java/com/xpdustry/distributor/core/player/SimplePlayerLookup.java @@ -1,7 +1,7 @@ /* * Distributor, a feature-rich framework for Mindustry plugins. * - * Copyright (C) 2023 Xpdustry + * Copyright (C) 2024 Xpdustry * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,42 +16,36 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package fr.xpdustry.distributor.api.util; +package com.xpdustry.distributor.core.player; import arc.util.Strings; +import com.xpdustry.distributor.core.collection.ArcCollections; import java.text.Normalizer; import java.util.ArrayList; import java.util.Collections; +import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import mindustry.Vars; import mindustry.gen.Groups; import mindustry.gen.Player; +import mindustry.net.Administration; -/** - * A collection of random utilities for manipulating players. - */ -public final class Players { - - private Players() {} +final class SimplePlayerLookup implements PlayerLookup { - /** - * Finds online players by their name or entity ID. - * - * @param query the query - * @return the list player of matching players - */ - public static List findPlayers(final String query) { - return findPlayers(query, false); - } + static final SimplePlayerLookup INSTANCE = new SimplePlayerLookup(); /** * Finds online players their its name, UUID or entity ID. * * @param query the query - * @param uuid whether the query should also search by UUID + * @param admin whether the query should also search by sensitive IDs such as MUUID * @return the list player of matching players */ - public static List findPlayers(final String query, final boolean uuid) { + @Override + public List findOnlinePlayers(final String query, final boolean admin) { if (query.startsWith("#")) { final var id = Strings.parseInt(query.substring(1), -1); final var players = ArcCollections.immutableList(Groups.player).stream() @@ -62,7 +56,7 @@ public static List findPlayers(final String query, final boolean uuid) { } } - if (uuid && MUUID.isUuid(query)) { + if (admin && MUUID.isUuid(query)) { return ArcCollections.immutableList(Groups.player).stream() .filter(p -> p.uuid().equals(query)) .toList(); @@ -88,14 +82,20 @@ public static List findPlayers(final String query, final boolean uuid) { return matches == 1 ? Collections.singletonList(match) : Collections.unmodifiableList(result); } - /** - * Returns the locale of a player. - * - * @param player the player - * @return the locale of the player - */ - public static Locale getLocale(final Player player) { - return Locale.forLanguageTag(player.locale().replace('_', '-')); + @Override + public CompletableFuture> findOfflinePlayers( + final String query, final boolean admin) { + final Set result = new LinkedHashSet<>(); + for (final var online : findOnlinePlayers(query, admin)) { + result.add(online.getInfo()); + } + if (admin && MUUID.isUuid(query)) { + final var info = Vars.netServer.admins.getInfoOptional(query); + if (info != null) { + result.add(info); + } + } + return CompletableFuture.completedFuture(List.copyOf(result)); } // https://stackoverflow.com/a/4122207 diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/plugin/AbstractMindustryPlugin.java b/distributor-common/src/main/java/com/xpdustry/distributor/core/plugin/AbstractMindustryPlugin.java similarity index 97% rename from distributor-api/src/main/java/fr/xpdustry/distributor/api/plugin/AbstractMindustryPlugin.java rename to distributor-common/src/main/java/com/xpdustry/distributor/core/plugin/AbstractMindustryPlugin.java index 416dd2a9..547ff0d3 100644 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/plugin/AbstractMindustryPlugin.java +++ b/distributor-common/src/main/java/com/xpdustry/distributor/core/plugin/AbstractMindustryPlugin.java @@ -1,7 +1,7 @@ /* * Distributor, a feature-rich framework for Mindustry plugins. * - * Copyright (C) 2023 Xpdustry + * Copyright (C) 2024 Xpdustry * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,7 +16,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package fr.xpdustry.distributor.api.plugin; +package com.xpdustry.distributor.core.plugin; import arc.ApplicationListener; import arc.Core; @@ -89,7 +89,7 @@ protected final List getListeners() { } protected PluginAnnotationParser getPluginAnnotationParser() { - return PluginAnnotationParser.simple(this); + return PluginAnnotationParser.noop(this); } @Override diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/plugin/MindustryPlugin.java b/distributor-common/src/main/java/com/xpdustry/distributor/core/plugin/MindustryPlugin.java similarity index 97% rename from distributor-api/src/main/java/fr/xpdustry/distributor/api/plugin/MindustryPlugin.java rename to distributor-common/src/main/java/com/xpdustry/distributor/core/plugin/MindustryPlugin.java index a45b4c36..ea4b34df 100644 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/plugin/MindustryPlugin.java +++ b/distributor-common/src/main/java/com/xpdustry/distributor/core/plugin/MindustryPlugin.java @@ -1,7 +1,7 @@ /* * Distributor, a feature-rich framework for Mindustry plugins. * - * Copyright (C) 2023 Xpdustry + * Copyright (C) 2024 Xpdustry * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,7 +16,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package fr.xpdustry.distributor.api.plugin; +package com.xpdustry.distributor.core.plugin; import arc.util.CommandHandler; import java.nio.file.Path; diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/security/PlayerValidatorEvent.java b/distributor-common/src/main/java/com/xpdustry/distributor/core/plugin/PluginAnnotationParser.java similarity index 74% rename from distributor-api/src/main/java/fr/xpdustry/distributor/api/security/PlayerValidatorEvent.java rename to distributor-common/src/main/java/com/xpdustry/distributor/core/plugin/PluginAnnotationParser.java index 725c8e13..3ed9c859 100644 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/security/PlayerValidatorEvent.java +++ b/distributor-common/src/main/java/com/xpdustry/distributor/core/plugin/PluginAnnotationParser.java @@ -1,7 +1,7 @@ /* * Distributor, a feature-rich framework for Mindustry plugins. * - * Copyright (C) 2023 Xpdustry + * Copyright (C) 2024 Xpdustry * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,14 +16,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package fr.xpdustry.distributor.api.security; +package com.xpdustry.distributor.core.plugin; -import mindustry.gen.Player; +public interface PluginAnnotationParser { -public record PlayerValidatorEvent(Player player, Type type) { - public enum Type { - VALIDATED, - INVALIDATED, - REMOVED + static PluginAnnotationParser noop(final MindustryPlugin plugin) { + return object -> {}; } + + void parse(final Object object); } diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/plugin/PluginAware.java b/distributor-common/src/main/java/com/xpdustry/distributor/core/plugin/PluginAware.java similarity index 92% rename from distributor-api/src/main/java/fr/xpdustry/distributor/api/plugin/PluginAware.java rename to distributor-common/src/main/java/com/xpdustry/distributor/core/plugin/PluginAware.java index d37a81c0..752c5078 100644 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/plugin/PluginAware.java +++ b/distributor-common/src/main/java/com/xpdustry/distributor/core/plugin/PluginAware.java @@ -1,7 +1,7 @@ /* * Distributor, a feature-rich framework for Mindustry plugins. * - * Copyright (C) 2023 Xpdustry + * Copyright (C) 2024 Xpdustry * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,7 +16,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package fr.xpdustry.distributor.api.plugin; +package com.xpdustry.distributor.core.plugin; /** * Marks an object as belonging to a plugin. diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/plugin/PluginDescriptor.java b/distributor-common/src/main/java/com/xpdustry/distributor/core/plugin/PluginDescriptor.java similarity index 97% rename from distributor-api/src/main/java/fr/xpdustry/distributor/api/plugin/PluginDescriptor.java rename to distributor-common/src/main/java/com/xpdustry/distributor/core/plugin/PluginDescriptor.java index 1ded3cee..ab220f9f 100644 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/plugin/PluginDescriptor.java +++ b/distributor-common/src/main/java/com/xpdustry/distributor/core/plugin/PluginDescriptor.java @@ -1,7 +1,7 @@ /* * Distributor, a feature-rich framework for Mindustry plugins. * - * Copyright (C) 2023 Xpdustry + * Copyright (C) 2024 Xpdustry * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,10 +16,10 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package fr.xpdustry.distributor.api.plugin; +package com.xpdustry.distributor.core.plugin; import arc.util.serialization.Json; -import fr.xpdustry.distributor.api.util.ArcCollections; +import com.xpdustry.distributor.core.collection.ArcCollections; import java.io.IOException; import java.util.List; import java.util.Objects; diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/plugin/PluginListener.java b/distributor-common/src/main/java/com/xpdustry/distributor/core/plugin/PluginListener.java similarity index 95% rename from distributor-api/src/main/java/fr/xpdustry/distributor/api/plugin/PluginListener.java rename to distributor-common/src/main/java/com/xpdustry/distributor/core/plugin/PluginListener.java index bf80882c..a9c95556 100644 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/plugin/PluginListener.java +++ b/distributor-common/src/main/java/com/xpdustry/distributor/core/plugin/PluginListener.java @@ -1,7 +1,7 @@ /* * Distributor, a feature-rich framework for Mindustry plugins. * - * Copyright (C) 2023 Xpdustry + * Copyright (C) 2024 Xpdustry * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,7 +16,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package fr.xpdustry.distributor.api.plugin; +package com.xpdustry.distributor.core.plugin; import arc.util.CommandHandler; diff --git a/distributor-api/src/main/java/fr/xpdustry/distributor/api/plugin/WrappingMindustryPlugin.java b/distributor-common/src/main/java/com/xpdustry/distributor/core/plugin/WrappingMindustryPlugin.java similarity index 96% rename from distributor-api/src/main/java/fr/xpdustry/distributor/api/plugin/WrappingMindustryPlugin.java rename to distributor-common/src/main/java/com/xpdustry/distributor/core/plugin/WrappingMindustryPlugin.java index 9b4fe1b8..79d9398b 100644 --- a/distributor-api/src/main/java/fr/xpdustry/distributor/api/plugin/WrappingMindustryPlugin.java +++ b/distributor-common/src/main/java/com/xpdustry/distributor/core/plugin/WrappingMindustryPlugin.java @@ -1,7 +1,7 @@ /* * Distributor, a feature-rich framework for Mindustry plugins. * - * Copyright (C) 2023 Xpdustry + * Copyright (C) 2024 Xpdustry * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,7 +16,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package fr.xpdustry.distributor.api.plugin; +package com.xpdustry.distributor.core.plugin; import arc.util.CommandHandler; import java.nio.file.Path; diff --git a/distributor-common/src/main/java/com/xpdustry/distributor/core/plugin/package-info.java b/distributor-common/src/main/java/com/xpdustry/distributor/core/plugin/package-info.java new file mode 100644 index 00000000..e944e0d1 --- /dev/null +++ b/distributor-common/src/main/java/com/xpdustry/distributor/core/plugin/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package com.xpdustry.distributor.core.plugin; + +import org.jspecify.annotations.NullMarked; diff --git a/distributor-core/build.gradle.kts b/distributor-core/build.gradle.kts deleted file mode 100644 index 25ac074e..00000000 --- a/distributor-core/build.gradle.kts +++ /dev/null @@ -1,48 +0,0 @@ -plugins { - id("distributor.base-conventions") - id("distributor.publishing-conventions") - id("distributor.mindustry-conventions") -} - -dependencies { - api(project(":distributor-api")) - api("org.aeonbits.owner:owner-java8:1.0.12") - api("com.zaxxer:HikariCP:5.0.1") - api("com.mysql:mysql-connector-j:8.1.0") - api("com.password4j:password4j:1.7.3") - api("org.slf4j:jul-to-slf4j:2.0.9") - testImplementation("org.xerial:sqlite-jdbc:3.43.0.0") -} - -val metadata = fr.xpdustry.toxopid.spec.ModMetadata.fromJson(rootProject.file("plugin.json")) -metadata.version = rootProject.version.toString() -metadata.description = rootProject.description.toString() -metadata.name = "distributor-core" -metadata.displayName = "DistributorCore" -metadata.main = "fr.xpdustry.distributor.core.DistributorCorePlugin" - -tasks.shadowJar { - archiveFileName.set("distributor-core.jar") - - doFirst { - val temp = temporaryDir.resolve("plugin.json") - temp.writeText(metadata.toJson(true)) - from(temp) - } - - minimize { - exclude(dependency("fr.xpdustry:distributor-.*:.*")) - exclude(dependency("cloud.commandframework:cloud-.*:.*")) - exclude(dependency("org.slf4j:slf4j-api:.*")) - exclude(dependency("io.leangen.geantyref:geantyref:.*")) - exclude(dependency("com.mysql:mysql-connector-j:.*")) - } - - val shadowPackage = "fr.xpdustry.distributor.core.shadow" - relocate("org.aeonbits.owner", "$shadowPackage.owner") - relocate("com.zaxxer.hikari", "$shadowPackage.hikari") - relocate("com.mysql", "$shadowPackage.mysql") - relocate("com.google.protobuf", "$shadowPackage.protobuf") - relocate("com.password4j", "$shadowPackage.password4j") - relocate("google", "$shadowPackage.google") -} diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/DistributorConfiguration.java b/distributor-core/src/main/java/fr/xpdustry/distributor/core/DistributorConfiguration.java deleted file mode 100644 index f4668a72..00000000 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/DistributorConfiguration.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core; - -import org.aeonbits.owner.Accessible; -import org.aeonbits.owner.Config; - -public interface DistributorConfiguration extends Accessible { - - @Config.Key("distributor.scheduler.workers") - @Config.DefaultValue("-1") - int getSchedulerWorkers(); - - @Config.Key("distributor.database.type") - @Config.DefaultValue("SQLITE") - DatabaseType getDatabaseType(); - - @Config.Key("distributor.database.address") - @Config.DefaultValue("") - String getDatabaseAddress(); - - @Config.Key("distributor.database.prefix") - @Config.DefaultValue("") - String getDatabasePrefix(); - - @Config.Key("distributor.database.name") - @Config.DefaultValue("distributor") - String getDatabaseName(); - - @Config.Key("distributor.database.username") - @Config.DefaultValue("") - String getDatabaseUsername(); - - @Config.Key("distributor.database.password") - @Config.DefaultValue("") - String getDatabasePassword(); - - @Config.Key("distributor.database.pool.min") - @Config.DefaultValue("1") - int getDatabaseMinPoolSize(); - - @Config.Key("distributor.database.pool.max") - @Config.DefaultValue("4") - int getDatabaseMaxPoolSize(); - - @Config.Key("distributor.security.validation.policy") - @Config.DefaultValue("VALIDATE_UNKNOWN") - PlayerValidationPolicy getIdentityValidationPolicy(); - - @Config.Key("distributor.security.validation.auto-admin") - @Config.DefaultValue("false") - boolean isValidationAutoAdminEnabled(); - - @Config.Key("distributor.security.permission.primary-group") - @Config.DefaultValue("default") - String getPermissionPrimaryGroup(); - - @Config.Key("distributor.security.permission.ignore-admin") - @Config.DefaultValue("false") - boolean isAdminIgnored(); - - enum DatabaseType { - SQLITE, - MYSQL - } - - enum PlayerValidationPolicy { - VALIDATE_UNKNOWN, - VALIDATE_ALL, - VALIDATE_NONE - } -} diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/DistributorCorePlugin.java b/distributor-core/src/main/java/fr/xpdustry/distributor/core/DistributorCorePlugin.java deleted file mode 100644 index 38ba6229..00000000 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/DistributorCorePlugin.java +++ /dev/null @@ -1,259 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core; - -import arc.Core; -import arc.util.CommandHandler; -import arc.util.Log; -import fr.xpdustry.distributor.api.Distributor; -import fr.xpdustry.distributor.api.DistributorProvider; -import fr.xpdustry.distributor.api.command.ArcCommandManager; -import fr.xpdustry.distributor.api.command.sender.CommandSender; -import fr.xpdustry.distributor.api.event.EventBus; -import fr.xpdustry.distributor.api.localization.LocalizationSource; -import fr.xpdustry.distributor.api.localization.LocalizationSourceRegistry; -import fr.xpdustry.distributor.api.localization.MultiLocalizationSource; -import fr.xpdustry.distributor.api.plugin.AbstractMindustryPlugin; -import fr.xpdustry.distributor.api.scheduler.PluginScheduler; -import fr.xpdustry.distributor.api.security.PlayerValidator; -import fr.xpdustry.distributor.api.security.permission.PermissionService; -import fr.xpdustry.distributor.core.commands.GroupPermissibleCommands; -import fr.xpdustry.distributor.core.commands.PlayerPermissibleCommands; -import fr.xpdustry.distributor.core.commands.PlayerValidatorCommands; -import fr.xpdustry.distributor.core.database.ConnectionFactory; -import fr.xpdustry.distributor.core.database.MySQLConnectionFactory; -import fr.xpdustry.distributor.core.database.SQLiteConnectionFactory; -import fr.xpdustry.distributor.core.dependency.DependencyManager; -import fr.xpdustry.distributor.core.event.SimpleEventBus; -import fr.xpdustry.distributor.core.logging.ArcLoggerFactory; -import fr.xpdustry.distributor.core.scheduler.SimplePluginScheduler; -import fr.xpdustry.distributor.core.scheduler.TimeSource; -import fr.xpdustry.distributor.core.security.PlayerValidatorListener; -import fr.xpdustry.distributor.core.security.SQLPlayerValidator; -import fr.xpdustry.distributor.core.security.permission.SQLPermissionService; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; -import java.util.Properties; -import org.aeonbits.owner.ConfigFactory; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; -import org.slf4j.LoggerFactory; -import org.slf4j.bridge.SLF4JBridgeHandler; - -public final class DistributorCorePlugin extends AbstractMindustryPlugin implements Distributor { - - static { - // Class loader trickery to use the ModClassLoader instead of the root - final var temp = Thread.currentThread().getContextClassLoader(); - Thread.currentThread().setContextClassLoader(DistributorCorePlugin.class.getClassLoader()); - if (!(LoggerFactory.getILoggerFactory() instanceof ArcLoggerFactory)) { - Log.err( - """ - The slf4j Logger factory isn't provided by Distributor (got @ instead of ArcLoggerFactory). - Make sure another plugin doesn't set it's own logging implementation or that it's logging implementation is relocated correctly.""", - LoggerFactory.getILoggerFactory().getClass().getName()); - } - // Redirect JUL to SLF4J - SLF4JBridgeHandler.removeHandlersForRootLogger(); - SLF4JBridgeHandler.install(); - // Restore the class loader - Thread.currentThread().setContextClassLoader(temp); - } - - private final MultiLocalizationSource source = MultiLocalizationSource.create(); - private final ArcCommandManager serverCommands = ArcCommandManager.standardAsync(this); - private final ArcCommandManager clientCommands = ArcCommandManager.standardAsync(this); - private final Map connections = new HashMap<>(); - private final EventBus eventBus = new SimpleEventBus(); - - private @MonotonicNonNull SQLPermissionService permissions = null; - private @MonotonicNonNull SimplePluginScheduler scheduler = null; - private @MonotonicNonNull DistributorConfiguration configuration = null; - private @MonotonicNonNull DependencyManager dependencyManager = null; - private @MonotonicNonNull PlayerValidator playerValidator = null; - - @SuppressWarnings({"MissingCasesInEnumSwitch", "resource"}) - @Override - public void onInit() { - // This may be dangerous, but I need to use the API here too - DistributorProvider.set(this); - - // Display the cool ass banner - final var banner = - this.getClass().getClassLoader().getResourceAsStream("fr/xpdustry/distributor/assets/banner.txt"); - if (banner == null) { - throw new RuntimeException("The Distributor banner cannot be found, are you sure the plugin is valid ?"); - } - try (final var reader = new BufferedReader(new InputStreamReader(banner, StandardCharsets.UTF_8))) { - reader.lines().forEach(line -> LoggerFactory.getLogger("ROOT").info("> {}", line)); - this.getLogger() - .info("> Loaded Distributor {}", "v" + this.getDescriptor().getVersion()); - } catch (final IOException e) { - this.getLogger().error("An error occurred while displaying distributor banner, very unexpected...", e); - } - - // Load configuration - final var file = this.getDirectory().resolve("config.properties"); - if (Files.exists(file)) { - final var properties = new Properties(); - try (final var reader = Files.newBufferedReader(file, StandardCharsets.UTF_8)) { - properties.load(reader); - } catch (final IOException e) { - throw new RuntimeException("Invalid config.", e); - } - this.configuration = ConfigFactory.create(DistributorConfiguration.class, properties); - } else { - this.configuration = ConfigFactory.create(DistributorConfiguration.class); - try (final var writer = Files.newBufferedWriter(file, StandardCharsets.UTF_8)) { - final var properties = new Properties(); - this.configuration.fill(properties); - properties.store(writer, null); - } catch (final IOException e) { - throw new RuntimeException("Can't create default config for Javelin.", e); - } - } - - // Start scheduler - final var parallelism = this.configuration.getSchedulerWorkers() < 1 - ? Math.max(4, Runtime.getRuntime().availableProcessors()) - : this.configuration.getSchedulerWorkers(); - this.addListener(this.scheduler = new SimplePluginScheduler(TimeSource.arc(), Core.app::post, parallelism)); - - // Create dependency manager - this.dependencyManager = new DependencyManager(this.getDirectory().resolve("libs")); - this.dependencyManager.addMavenCentral(); - - // Create main connection factories - final var mainConnectionFactory = - switch (this.configuration.getDatabaseType()) { - case MYSQL -> new MySQLConnectionFactory(this.configuration); - case SQLITE -> new SQLiteConnectionFactory( - this.configuration.getDatabasePrefix(), - this.getDirectory().resolve("permissions.sqlite"), - this.dependencyManager.createClassLoaderFor(SQLiteConnectionFactory.SQLITE_DRIVER)); - }; - this.addConnection("main", mainConnectionFactory); - - final var validatorConnectionFactory = new SQLiteConnectionFactory( - "", - this.getDirectory().resolve("validations.sqlite"), - this.dependencyManager.createClassLoaderFor(SQLiteConnectionFactory.SQLITE_DRIVER)); - this.addConnection("validator", validatorConnectionFactory); - - // Register bundles - final var registry = LocalizationSourceRegistry.create(Locale.ENGLISH); - registry.registerAll( - Locale.ENGLISH, - "fr/xpdustry/distributor/assets/bundles/bundle", - this.getClass().getClassLoader()); - registry.registerAll( - Locale.FRENCH, - "fr/xpdustry/distributor/assets/bundles/bundle", - this.getClass().getClassLoader()); - - this.source.addLocalizationSource(registry); - this.source.addLocalizationSource(LocalizationSource.router()); - - // Add listeners to validate players - this.playerValidator = new SQLPlayerValidator(validatorConnectionFactory); - this.addListener(new PlayerValidatorListener(this.playerValidator, this.configuration)); - - // Register permission utilities - this.permissions = new SQLPermissionService(this.configuration, mainConnectionFactory, this.playerValidator); - this.addListener(new PlayerPermissibleCommands(this, this.permissions.getPlayerPermissionManager())); - this.addListener(new GroupPermissibleCommands(this, this.permissions.getGroupPermissionManager())); - this.addListener(new PlayerValidatorCommands(this)); - } - - @Override - public void onServerCommandsRegistration(final CommandHandler handler) { - this.serverCommands.initialize(handler); - } - - @Override - public void onClientCommandsRegistration(final CommandHandler handler) { - this.clientCommands.initialize(handler); - } - - @Override - public MultiLocalizationSource getGlobalLocalizationSource() { - return this.source; - } - - @Override - public PluginScheduler getPluginScheduler() { - return this.scheduler; - } - - @Override - public PlayerValidator getPlayerValidator() { - return this.playerValidator; - } - - @Override - public PermissionService getPermissionService() { - return this.permissions; - } - - @Override - public EventBus getEventBus() { - return this.eventBus; - } - - @Override - public void onExit() { - for (final var connection : this.connections.entrySet()) { - try { - this.getLogger().debug("Closing SQL connection '{}'", connection.getKey()); - connection.getValue().close(); - } catch (final Exception e) { - this.getLogger().error("An error occurred while closing SQL connection '{}'", connection.getKey(), e); - } - } - } - - public ArcCommandManager getServerCommandManager() { - return this.serverCommands; - } - - public ArcCommandManager getClientCommandManager() { - return this.clientCommands; - } - - public DistributorConfiguration getConfiguration() { - return this.configuration; - } - - public DependencyManager getDependencyManager() { - return this.dependencyManager; - } - - private void addConnection(final String name, final ConnectionFactory connection) { - if (this.connections.put(name, connection) != null) { - throw new RuntimeException("Connection '" + name + "' already exists."); - } - this.getLogger().debug("Starting SQL connection '{}'", name); - connection.start(); - } -} diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/commands/GroupPermissibleCommands.java b/distributor-core/src/main/java/fr/xpdustry/distributor/core/commands/GroupPermissibleCommands.java deleted file mode 100644 index ea730bd4..00000000 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/commands/GroupPermissibleCommands.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.commands; - -import cloud.commandframework.ArgumentDescription; -import cloud.commandframework.arguments.standard.IntegerArgument; -import cloud.commandframework.arguments.standard.StringArgument; -import cloud.commandframework.meta.CommandMeta; -import fr.xpdustry.distributor.api.command.ArcCommandManager; -import fr.xpdustry.distributor.api.command.sender.CommandSender; -import fr.xpdustry.distributor.api.security.permission.GroupPermissible; -import fr.xpdustry.distributor.api.security.permission.PermissibleManager; -import fr.xpdustry.distributor.core.DistributorCorePlugin; -import fr.xpdustry.distributor.core.commands.parser.GroupPermissibleParser; - -public final class GroupPermissibleCommands extends PermissibleCommands { - - public GroupPermissibleCommands( - final DistributorCorePlugin distributor, final PermissibleManager manager) { - super(distributor, manager, GroupPermissibleParser::new); - } - - @Override - protected void onSharedCommandRegistration(final ArcCommandManager registry) { - super.onSharedCommandRegistration(registry); - - final var root = - registry.commandBuilder("permission", ArgumentDescription.of("Permission management commands.")); - - registry.command(root.literal("create-group") - .meta(CommandMeta.DESCRIPTION, "Creates a new group.") - .permission(this.prefixPermission("create")) - .argument(StringArgument.of("group")) - .handler(ctx -> { - final String group = ctx.get("group"); - if (this.getPermissibleManager().existsById(group)) { - ctx.getSender().sendLocalizedWarning("permission.group.create.already", group); - } else { - this.getPermissibleManager() - .save(this.getPermissibleManager().findOrCreateById(group)); - ctx.getSender().sendLocalizedMessage("permission.group.create.success", group); - } - })); - - registry.command(root.literal("list-groups") - .meta(CommandMeta.DESCRIPTION, "Lists all groups.") - .permission(this.prefixPermission("list")) - .handler(ctx -> { - final var groups = this.getPermissibleManager().findAll().iterator(); - if (!groups.hasNext()) { - ctx.getSender().sendLocalizedMessage("permission.group.list.none"); - } else { - final var builder = new StringBuilder(); - do { - builder.append("\n- ").append(groups.next().getName()); - } while (groups.hasNext()); - ctx.getSender().sendLocalizedMessage("permission.group.list.success", builder.toString()); - } - })); - - final var weight = root.literal(this.getPermissibleCategory()) - .argument( - registry.argumentBuilder(this.getPermissibleClass(), this.getPermissibleCategory()) - .withParser(this.getParser()) - .build(), - ArgumentDescription.of("The group name.")) - .literal("weight", ArgumentDescription.of("Weight management commands.")); - - registry.command(weight.literal("get") - .meta(CommandMeta.DESCRIPTION, "Gets the weight of a group.") - .permission(this.prefixPermission("weight.get")) - .handler(ctx -> - this.getGroupPermissibleWeight(ctx.getSender(), ctx.get(this.getPermissibleCategory())))); - - registry.command(weight.literal("set") - .meta(CommandMeta.DESCRIPTION, "Sets the weight of a group.") - .permission(this.prefixPermission("weight.set")) - .argument( - IntegerArgument.builder("weight") - .withMin(0) - .build(), - ArgumentDescription.of("The new weight.")) - .handler(ctx -> this.setGroupPermissibleWeight( - ctx.getSender(), ctx.get(this.getPermissibleCategory()), ctx.get("weight")))); - } - - private void getGroupPermissibleWeight(final CommandSender sender, final GroupPermissible permissible) { - sender.sendLocalizedMessage("permission.group.weight.get", permissible.getName(), permissible.getWeight()); - } - - private void setGroupPermissibleWeight( - final CommandSender sender, final GroupPermissible permissible, final int weight) { - if (permissible.getWeight() == weight) { - sender.sendLocalizedMessage("permission.group.weight.set.already", permissible.getName(), weight); - } else { - permissible.setWeight(weight); - this.getPermissibleManager().save(permissible); - sender.sendLocalizedMessage("permission.group.weight.set.success", permissible.getName(), weight); - } - } - - @Override - protected String getPermissibleCategory() { - return "group"; - } - - @Override - protected Class getPermissibleClass() { - return GroupPermissible.class; - } -} diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/commands/PermissibleCommands.java b/distributor-core/src/main/java/fr/xpdustry/distributor/core/commands/PermissibleCommands.java deleted file mode 100644 index e4a24b67..00000000 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/commands/PermissibleCommands.java +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.commands; - -import arc.util.CommandHandler; -import cloud.commandframework.ArgumentDescription; -import cloud.commandframework.arguments.CommandArgument; -import cloud.commandframework.arguments.preprocessor.RegexPreprocessor; -import cloud.commandframework.arguments.standard.BooleanArgument; -import cloud.commandframework.arguments.standard.StringArgument; -import cloud.commandframework.meta.CommandMeta; -import fr.xpdustry.distributor.api.command.ArcCommandManager; -import fr.xpdustry.distributor.api.command.sender.CommandSender; -import fr.xpdustry.distributor.api.plugin.PluginListener; -import fr.xpdustry.distributor.api.security.permission.Permissible; -import fr.xpdustry.distributor.api.security.permission.PermissibleManager; -import fr.xpdustry.distributor.api.util.Tristate; -import fr.xpdustry.distributor.core.DistributorCorePlugin; -import fr.xpdustry.distributor.core.commands.parser.PermissibleParser; -import java.util.TreeMap; -import java.util.function.Function; - -public abstract class PermissibleCommands

implements PluginListener { - - private final DistributorCorePlugin distributor; - private final PermissibleManager

manager; - private final PermissibleParser parser; - - public PermissibleCommands( - final DistributorCorePlugin distributor, - final PermissibleManager

manager, - final Function, PermissibleParser> parserFactory) { - this.distributor = distributor; - this.manager = manager; - this.parser = parserFactory.apply(manager); - } - - @Override - public void onPluginServerCommandsRegistration(final CommandHandler handler) { - this.onSharedCommandRegistration(this.distributor.getServerCommandManager()); - } - - @Override - public void onPluginClientCommandsRegistration(final CommandHandler handler) { - this.onSharedCommandRegistration(this.distributor.getClientCommandManager()); - } - - protected void onSharedCommandRegistration(final ArcCommandManager registry) { - final var root = registry.commandBuilder( - "permission", ArgumentDescription.of("Permission management commands.")) - .literal(this.getPermissibleCategory()) - .argument( - this.newPermissibleArgument(this.getPermissibleCategory()), - ArgumentDescription.of(this.formatDescription("The %s name."))); - - registry.command(root.literal("info", this.formatDescription("Get information about a %s.")) - .permission(this.prefixPermission("permission.info")) - .handler(ctx -> this.getPermissibleInfo(ctx.getSender(), ctx.get(this.getPermissibleCategory())))); - - registry.command(root.literal("set") - .meta(CommandMeta.DESCRIPTION, this.formatDescription("Set a permission for a %s.")) - .permission(this.prefixPermission("permission.set")) - .argument(this.newPermissionArgument("permission"), ArgumentDescription.of("The permission to set.")) - .argument(BooleanArgument.of("value"), ArgumentDescription.of("The permission value.")) - .handler(ctx -> this.setPermissiblePermission( - ctx.getSender(), - ctx.get(this.getPermissibleCategory()), - ctx.get("permission"), - Tristate.of(ctx.get("value"))))); - - registry.command(root.literal("unset") - .meta(CommandMeta.DESCRIPTION, this.formatDescription("Unset a permission for a %s.")) - .permission(this.prefixPermission("permission.unset")) - .argument(this.newPermissionArgument("permission"), ArgumentDescription.of("The permission to unset.")) - .handler(ctx -> this.setPermissiblePermission( - ctx.getSender(), - ctx.get(this.getPermissibleCategory()), - ctx.get("permission"), - Tristate.UNDEFINED))); - - registry.command(root.literal("delete") - .meta(CommandMeta.DESCRIPTION, this.formatDescription("Delete permission data of a %s.")) - .permission(this.prefixPermission("permission.delete")) - .handler(ctx -> - this.deletePermissiblePermissions(ctx.getSender(), ctx.get(this.getPermissibleCategory())))); - - final var parents = root.literal("parent", this.formatDescription("Subcommands for %s parent groups.")); - - registry.command(parents.literal("info") - .meta(CommandMeta.DESCRIPTION, this.formatDescription("Get information about a %s's parent groups.")) - .permission(this.prefixPermission("parent.info")) - .handler(ctx -> - this.getPermissibleParentsInfo(ctx.getSender(), ctx.get(this.getPermissibleCategory())))); - - registry.command(parents.literal("add") - .meta(CommandMeta.DESCRIPTION, this.formatDescription("Add a parent group to a %s.")) - .permission(this.prefixPermission("parent.add")) - .argument(StringArgument.of("parent"), ArgumentDescription.of("The parent group to add.")) - .handler(ctx -> this.addPermissibleParent( - ctx.getSender(), ctx.get(this.getPermissibleCategory()), ctx.get("parent")))); - - registry.command(parents.literal("remove") - .meta(CommandMeta.DESCRIPTION, this.formatDescription("Remove a parent group from a %s.")) - .permission(this.prefixPermission("parent.remove")) - .argument(StringArgument.of("parent"), ArgumentDescription.of("The parent group to remove.")) - .handler(ctx -> this.removePermissibleParent( - ctx.getSender(), ctx.get(this.getPermissibleCategory()), ctx.get("parent")))); - } - - public void getPermissibleInfo(final CommandSender sender, final P permissible) { - final var permissions = new TreeMap<>(permissible.getPermissions()); - if (permissions.isEmpty()) { - sender.sendLocalizedMessage("permission.permissible.permission.list.none", permissible.getName()); - } else { - final var builder = new StringBuilder(); - final var iterator = permissions.entrySet().iterator(); - while (iterator.hasNext()) { - final var entry = iterator.next(); - builder.append("- ").append(entry.getKey()).append(" > ").append(entry.getValue()); - if (iterator.hasNext()) { - builder.append('\n'); - } - } - sender.sendMessage(builder.toString()); - } - } - - private void setPermissiblePermission( - final CommandSender sender, final P permissible, final String permission, final Tristate state) { - if (permissible.getPermission(permission) == state) { - sender.sendLocalizedWarning( - "permission.permissible.permission.set.already", permission, permissible.getName(), state); - } else { - permissible.setPermission(permission, state); - this.getPermissibleManager().save(permissible); - sender.sendLocalizedMessage( - "permission.permissible.permission.set.success", permission, permissible.getName(), state); - } - } - - private void getPermissibleParentsInfo(final CommandSender sender, final P permissible) { - if (permissible.getParentGroups().isEmpty()) { - sender.sendLocalizedMessage("permission.permissible.parent.list.none", permissible.getName()); - } else { - final var builder = new StringBuilder(); - final var groups = permissible.getParentGroups(); - for (int i = 0; i < groups.size(); i++) { - builder.append(i).append(". ").append(groups); - if (i != groups.size() - 1) { - builder.append('\n'); - } - } - sender.sendMessage(builder.toString()); - } - } - - private void addPermissibleParent(final CommandSender sender, final P permissible, final String parent) { - if (permissible.getParentGroups().contains(parent)) { - sender.sendLocalizedWarning("permission.permissible.parent.add.already", permissible.getName(), parent); - } else { - permissible.addParentGroup(parent); - this.getPermissibleManager().save(permissible); - sender.sendLocalizedMessage("permission.permissible.parent.add.success", permissible.getName(), parent); - } - } - - private void removePermissibleParent(final CommandSender sender, final P permissible, final String parent) { - if (permissible.getParentGroups().contains(parent)) { - permissible.removeParentGroup(parent); - this.getPermissibleManager().save(permissible); - sender.sendLocalizedMessage("permission.permissible.parent.remove.success", permissible.getName(), parent); - } else { - sender.sendLocalizedWarning("permission.permissible.parent.remove.already", permissible.getName(), parent); - } - } - - private void deletePermissiblePermissions(final CommandSender sender, final P permissible) { - if (this.getPermissibleManager().exists(permissible)) { - this.getPermissibleManager().delete(permissible); - sender.sendLocalizedMessage("permission.permissible.delete.success", permissible.getName()); - } else { - sender.sendLocalizedWarning("permission.permissible.delete.already", permissible.getName()); - } - } - - @SuppressWarnings("SameParameterValue") - protected final CommandArgument newPermissionArgument(final String name) { - return StringArgument.single(name) - .addPreprocessor(RegexPreprocessor.of(Permissible.PERMISSION_REGEX)); - } - - protected final CommandArgument newPermissibleArgument(final String name) { - return CommandArgument.ofType(this.getPermissibleClass(), name) - .withParser(this.parser) - .build(); - } - - protected final PermissibleManager

getPermissibleManager() { - return this.manager; - } - - protected final PermissibleParser getParser() { - return this.parser; - } - - protected final String formatDescription(final String description) { - return description.formatted(this.getPermissibleCategory()); - } - - protected final String prefixPermission(final String permission) { - return "distributor.permission." + this.getPermissibleCategory() + "." + permission; - } - - protected abstract String getPermissibleCategory(); - - protected abstract Class

getPermissibleClass(); -} diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/commands/PlayerPermissibleCommands.java b/distributor-core/src/main/java/fr/xpdustry/distributor/core/commands/PlayerPermissibleCommands.java deleted file mode 100644 index 622acdab..00000000 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/commands/PlayerPermissibleCommands.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.commands; - -import fr.xpdustry.distributor.api.security.permission.PermissibleManager; -import fr.xpdustry.distributor.api.security.permission.PlayerPermissible; -import fr.xpdustry.distributor.core.DistributorCorePlugin; -import fr.xpdustry.distributor.core.commands.parser.PlayerPermissibleParser; - -public final class PlayerPermissibleCommands extends PermissibleCommands { - - public PlayerPermissibleCommands( - final DistributorCorePlugin distributor, final PermissibleManager manager) { - super(distributor, manager, PlayerPermissibleParser::new); - } - - @Override - protected String getPermissibleCategory() { - return "player"; - } - - @Override - protected Class getPermissibleClass() { - return PlayerPermissible.class; - } -} diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/commands/PlayerValidatorCommands.java b/distributor-core/src/main/java/fr/xpdustry/distributor/core/commands/PlayerValidatorCommands.java deleted file mode 100644 index a44dc8a0..00000000 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/commands/PlayerValidatorCommands.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.commands; - -import arc.util.CommandHandler; -import cloud.commandframework.ArgumentDescription; -import cloud.commandframework.meta.CommandMeta; -import fr.xpdustry.distributor.api.command.ArcCommandManager; -import fr.xpdustry.distributor.api.command.argument.PlayerArgument; -import fr.xpdustry.distributor.api.command.argument.PlayerInfoArgument; -import fr.xpdustry.distributor.api.command.sender.CommandSender; -import fr.xpdustry.distributor.api.plugin.PluginListener; -import fr.xpdustry.distributor.api.util.MUUID; -import fr.xpdustry.distributor.core.DistributorConfiguration.PlayerValidationPolicy; -import fr.xpdustry.distributor.core.DistributorCorePlugin; -import mindustry.gen.Groups; -import mindustry.gen.Player; -import mindustry.net.Administration.PlayerInfo; - -public final class PlayerValidatorCommands implements PluginListener { - - private final DistributorCorePlugin distributor; - - public PlayerValidatorCommands(final DistributorCorePlugin distributor) { - this.distributor = distributor; - } - - @Override - public void onPluginServerCommandsRegistration(final CommandHandler handler) { - this.onSharedCommandRegistration(this.distributor.getServerCommandManager()); - } - - @Override - public void onPluginClientCommandsRegistration(final CommandHandler handler) { - this.onSharedCommandRegistration(this.distributor.getClientCommandManager()); - } - - private void onSharedCommandRegistration(final ArcCommandManager manager) { - final var identity = manager.commandBuilder("identity", ArgumentDescription.of("Manage player identities.")); - - manager.command(identity.literal("validate") - .meta(CommandMeta.DESCRIPTION, "Validate an online player.") - .permission("distributor.identity.validate") - .argument(PlayerArgument.of("player")) - .handler(ctx -> { - final var muuid = MUUID.of(ctx.get("player")); - this.distributor.getPlayerValidator().validate(muuid); - ctx.getSender().sendLocalizedMessage("distributor.identity.validate"); - })); - - manager.command(identity.literal("invalidate") - .meta(CommandMeta.DESCRIPTION, "Invalidate all identities matching with the given UUID.") - .permission("distributor.identity.invalidate") - .argument(PlayerInfoArgument.of("player")) - .handler(ctx -> { - this.distributor.getPlayerValidator().invalidate(ctx.get("player").id); - ctx.getSender().sendLocalizedMessage("distributor.identity.invalidate"); - })); - - manager.command(identity.literal("validity") - .meta(CommandMeta.DESCRIPTION, "Check if an online player is valid.") - .permission("distributor.identity.validity") - .argument(PlayerArgument.of("player")) - .handler(ctx -> { - final var muuid = MUUID.of(ctx.get("player")); - final var status = this.distributor.getPlayerValidator().isValid(muuid) ? "valid" : "invalid"; - ctx.getSender().sendLocalizedMessage("distributor.identity.validity." + status); - })); - - manager.command(identity.literal("clear") - .meta(CommandMeta.DESCRIPTION, "Clear all player identities data.") - .flag(manager.flagBuilder("confirm").withDescription(ArgumentDescription.of("Confirm the action."))) - .permission("distributor.identity.clear") - .handler(ctx -> { - if (!ctx.flags().contains("confirm")) { - ctx.getSender().sendLocalizedMessage("distributor.identity.clear.warning"); - return; - } - this.distributor.getPlayerValidator().removeAll(); - ctx.getSender().sendLocalizedMessage("distributor.identity.clear.success"); - - final var policy = this.distributor.getConfiguration().getIdentityValidationPolicy(); - if (policy == PlayerValidationPolicy.VALIDATE_ALL - || policy == PlayerValidationPolicy.VALIDATE_UNKNOWN) { - Groups.player.forEach( - player -> this.distributor.getPlayerValidator().validate(MUUID.of(player))); - } - })); - } -} diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/commands/parser/GroupPermissibleParser.java b/distributor-core/src/main/java/fr/xpdustry/distributor/core/commands/parser/GroupPermissibleParser.java deleted file mode 100644 index 1cd72164..00000000 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/commands/parser/GroupPermissibleParser.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.commands.parser; - -import fr.xpdustry.distributor.api.security.permission.GroupPermissible; -import fr.xpdustry.distributor.api.security.permission.PermissibleManager; -import java.util.Locale; -import java.util.Optional; - -public final class GroupPermissibleParser extends PermissibleParser { - - private final PermissibleManager manager; - - public GroupPermissibleParser(final PermissibleManager manager) { - this.manager = manager; - } - - @Override - protected Optional findPermissible(final String name) { - return this.manager.findById(name.toLowerCase(Locale.ROOT)); - } -} diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/commands/parser/PermissibleParser.java b/distributor-core/src/main/java/fr/xpdustry/distributor/core/commands/parser/PermissibleParser.java deleted file mode 100644 index 67227f03..00000000 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/commands/parser/PermissibleParser.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.commands.parser; - -import cloud.commandframework.arguments.parser.ArgumentParseResult; -import cloud.commandframework.arguments.parser.ArgumentParser; -import cloud.commandframework.captions.Caption; -import cloud.commandframework.captions.CaptionVariable; -import cloud.commandframework.context.CommandContext; -import cloud.commandframework.exceptions.parsing.NoInputProvidedException; -import cloud.commandframework.exceptions.parsing.ParserException; -import fr.xpdustry.distributor.api.security.permission.Permissible; -import java.io.Serial; -import java.util.Optional; -import java.util.Queue; - -public abstract class PermissibleParser implements ArgumentParser { - - @SuppressWarnings("unchecked") - @Override - public ArgumentParseResult

parse(final CommandContext ctx, final Queue inputQueue) { - final var input = inputQueue.peek(); - if (input == null) { - return ArgumentParseResult.failure(new NoInputProvidedException(this.getClass(), ctx)); - } - final var permissible = this.findPermissible(input); - if (permissible.isPresent()) { - inputQueue.remove(); - return ArgumentParseResult.success(permissible.get()); - } - return ArgumentParseResult.failure( - new PermissibleParseException((Class>) this.getClass(), input, ctx)); - } - - protected abstract Optional

findPermissible(final String name); - - public static final class PermissibleParseException extends ParserException { - - @Serial - private static final long serialVersionUID = 4995911354536184580L; - - private static final Caption PERMISSIBLE_PARSE_FAILURE_CAPTION = - Caption.of("argument.parse.failure.permissible"); - - private final String input; - - private PermissibleParseException( - final Class> clazz, final String input, final CommandContext ctx) { - super(clazz, ctx, PERMISSIBLE_PARSE_FAILURE_CAPTION, CaptionVariable.of("input", input)); - this.input = input; - } - - public String getInput() { - return this.input; - } - } -} diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/commands/parser/PlayerPermissibleParser.java b/distributor-core/src/main/java/fr/xpdustry/distributor/core/commands/parser/PlayerPermissibleParser.java deleted file mode 100644 index 98bb8fa2..00000000 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/commands/parser/PlayerPermissibleParser.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.commands.parser; - -import fr.xpdustry.distributor.api.security.permission.PermissibleManager; -import fr.xpdustry.distributor.api.security.permission.PlayerPermissible; -import fr.xpdustry.distributor.api.util.MUUID; -import fr.xpdustry.distributor.api.util.Players; -import java.util.Optional; - -public final class PlayerPermissibleParser extends PermissibleParser { - - private final PermissibleManager manager; - - public PlayerPermissibleParser(final PermissibleManager manager) { - this.manager = manager; - } - - // TODO Should cases with players having the same plain name be handled? - @Override - protected Optional findPermissible(final String name) { - if (MUUID.isUuid(name)) { - return Optional.of(this.manager.findOrCreateById(name)); - } - final var players = Players.findPlayers(name); - if (players.size() == 1) { - return Optional.of(this.manager.findOrCreateById(players.get(0).uuid())); - } else { - return Optional.empty(); - } - } -} diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/database/ConnectionConsumer.java b/distributor-core/src/main/java/fr/xpdustry/distributor/core/database/ConnectionConsumer.java deleted file mode 100644 index da1154cd..00000000 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/database/ConnectionConsumer.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.database; - -import java.sql.Connection; -import java.sql.SQLException; - -@FunctionalInterface -public interface ConnectionConsumer { - - void accept(final Connection connection) throws SQLException; -} diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/database/ConnectionFactory.java b/distributor-core/src/main/java/fr/xpdustry/distributor/core/database/ConnectionFactory.java deleted file mode 100644 index 9f65fdec..00000000 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/database/ConnectionFactory.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.database; - -import java.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.sql.Connection; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; -import java.util.function.Function; - -// This code is provided to you by LuckPerms, under the MIT license. -// TODO For future versions, rename this class to DatabaseContext or something like that. -public interface ConnectionFactory extends AutoCloseable { - - Connection getConnection() throws SQLException; - - default void withConsumer(final ConnectionConsumer consumer) { - try (final var con = this.getConnection()) { - consumer.accept(con); - } catch (final SQLException e) { - throw new RuntimeException(e); - } - } - - default T withFunction(final ConnectionFunction function) { - try (final var con = this.getConnection()) { - return function.apply(con); - } catch (final SQLException e) { - throw new RuntimeException(e); - } - } - - Function getStatementProcessor(); - - void start(); - - @Override - void close() throws SQLException; - - default void executeScript(final InputStream stream) { - try (final var con = this.getConnection(); - final var statements = con.createStatement()) { - for (final var statement : this.readStatements(stream)) { - statements.addBatch(this.getStatementProcessor().apply(statement)); - } - statements.executeBatch(); - } catch (final SQLException | IOException e) { - throw new RuntimeException(e); - } - } - - default void executeScript(final String script) { - this.executeScript(new ByteArrayInputStream(script.getBytes(StandardCharsets.UTF_8))); - } - - private List readStatements(final InputStream stream) throws IOException { - final List statements = new ArrayList<>(); - - try (final var reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8))) { - var builder = new StringBuilder(); - String line; - while ((line = reader.readLine()) != null) { - if (line.startsWith("--") || line.startsWith("#")) { - continue; - } - - builder.append(line); - - // check for end of declaration - if (line.endsWith(";")) { - builder.deleteCharAt(builder.length() - 1); - - final String result = builder.toString().trim(); - if (!result.isEmpty()) { - statements.add(result); - } - - // reset - builder = new StringBuilder(); - } - } - } - - return statements; - } -} diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/database/ConnectionFunction.java b/distributor-core/src/main/java/fr/xpdustry/distributor/core/database/ConnectionFunction.java deleted file mode 100644 index 9eef301d..00000000 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/database/ConnectionFunction.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.database; - -import java.sql.Connection; -import java.sql.SQLException; - -@FunctionalInterface -public interface ConnectionFunction { - - V apply(final Connection connection) throws SQLException; -} diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/database/HikariConnectionFactory.java b/distributor-core/src/main/java/fr/xpdustry/distributor/core/database/HikariConnectionFactory.java deleted file mode 100644 index 33d61a19..00000000 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/database/HikariConnectionFactory.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.database; - -import com.zaxxer.hikari.HikariConfig; -import com.zaxxer.hikari.HikariDataSource; -import fr.xpdustry.distributor.core.DistributorConfiguration; -import java.sql.Connection; -import java.sql.SQLException; -import java.util.HashMap; -import java.util.Map; -import java.util.function.Function; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; - -// This code is provided to you by LuckPerms, under the MIT license. -public abstract class HikariConnectionFactory implements ConnectionFactory { - - private final DistributorConfiguration configuration; - private @MonotonicNonNull HikariDataSource dataSource = null; - - public HikariConnectionFactory(final DistributorConfiguration configuration) { - this.configuration = configuration; - } - - @Override - public Connection getConnection() throws SQLException { - if (this.dataSource == null) { - throw new SQLException("Unable to get a connection from the pool. (hikari is null)"); - } - final var connection = this.dataSource.getConnection(); - if (connection == null) { - throw new SQLException("Unable to get a connection from the pool. (getConnection returned null)"); - } - return connection; - } - - @Override - public void start() { - final var config = new HikariConfig(); - - final var parts = this.configuration.getDatabaseAddress().split(":", 2); - final var host = parts[0]; - final var port = parts.length == 1 ? this.getDefaultPort() : Integer.parseInt(parts[1]); - this.configure( - config, - host, - port, - this.configuration.getDatabaseName(), - this.configuration.getDatabaseUsername(), - this.configuration.getDatabasePassword()); - - config.setPoolName("distributor-pool"); - config.setMinimumIdle(this.configuration.getDatabaseMinPoolSize()); - config.setMaximumPoolSize(this.configuration.getDatabaseMaxPoolSize()); - this.getExtraProperties().forEach(config::addDataSourceProperty); - // No need to initialize the pool now, the database is always initialized after this method is called. - config.setInitializationFailTimeout(-1); - this.dataSource = new HikariDataSource(config); - } - - @Override - public void close() { - if (this.dataSource != null) { - this.dataSource.close(); - } - } - - protected Map getExtraProperties() { - return new HashMap<>(); - } - - @Override - public Function getStatementProcessor() { - return statement -> statement.replace("{prefix}", this.configuration.getDatabasePrefix()); - } - - protected abstract int getDefaultPort(); - - protected abstract void configure( - final HikariConfig hikari, String host, int port, String database, String username, String password); -} diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/database/MySQLConnectionFactory.java b/distributor-core/src/main/java/fr/xpdustry/distributor/core/database/MySQLConnectionFactory.java deleted file mode 100644 index 02d0414c..00000000 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/database/MySQLConnectionFactory.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.database; - -import com.zaxxer.hikari.HikariConfig; -import fr.xpdustry.distributor.core.DistributorConfiguration; -import java.sql.Driver; -import java.sql.DriverManager; -import java.sql.SQLException; -import java.util.Enumeration; -import java.util.Map; -import java.util.concurrent.TimeUnit; -import java.util.function.Function; - -// This code is provided to you by LuckPerms, under the MIT license. -public final class MySQLConnectionFactory extends HikariConnectionFactory { - - public MySQLConnectionFactory(final DistributorConfiguration configuration) { - super(configuration); - } - - @Override - public Function getStatementProcessor() { - return super.getStatementProcessor().andThen(statement -> statement.replace('\'', '`')); - } - - @Override - public void start() { - super.start(); - - // Calling Class.forName("com.mysql.cj.jdbc.Driver") is enough to call the static initializer - // which makes our driver available in DriverManager. We don't want that, so unregister it after - // the pool has been set up. - final Enumeration drivers = DriverManager.getDrivers(); - while (drivers.hasMoreElements()) { - final Driver driver = drivers.nextElement(); - if (driver.getClass().getName().equals("com.mysql.cj.jdbc.Driver")) { - try { - DriverManager.deregisterDriver(driver); - } catch (final SQLException e) { - // ignore - } - } - } - } - - @Override - protected int getDefaultPort() { - return 3306; - } - - @Override - protected void configure( - final HikariConfig config, - final String host, - final int port, - final String database, - final String username, - final String password) { - config.setDriverClassName("com.mysql.cj.jdbc.Driver"); - config.setJdbcUrl("jdbc:mysql://" + host + ":" + port + "/" + database); - config.setUsername(username); - config.setPassword(password); - } - - @Override - protected Map getExtraProperties() { - final var properties = super.getExtraProperties(); - - // https://github.com/brettwooldridge/HikariCP/wiki/Rapid-Recovery - properties.putIfAbsent("socketTimeout", String.valueOf(TimeUnit.SECONDS.toMillis(30))); - - // https://github.com/brettwooldridge/HikariCP/wiki/MySQL-Configuration - properties.putIfAbsent("cachePrepStmts", "true"); - properties.putIfAbsent("prepStmtCacheSize", "250"); - properties.putIfAbsent("prepStmtCacheSqlLimit", "2048"); - properties.putIfAbsent("useServerPrepStmts", "true"); - properties.putIfAbsent("useLocalSessionState", "true"); - properties.putIfAbsent("rewriteBatchedStatements", "true"); - properties.putIfAbsent("cacheResultSetMetadata", "true"); - properties.putIfAbsent("cacheServerConfiguration", "true"); - properties.putIfAbsent("elideSetAutoCommits", "true"); - properties.putIfAbsent("maintainTimeStats", "false"); - properties.putIfAbsent("alwaysSendSetIsolation", "false"); - properties.putIfAbsent("cacheCallableStmts", "true"); - - // https://stackoverflow.com/a/54256150 - properties.putIfAbsent("serverTimezone", "UTC"); - - return properties; - } -} diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/database/NonClosableConnection.java b/distributor-core/src/main/java/fr/xpdustry/distributor/core/database/NonClosableConnection.java deleted file mode 100644 index 009f595b..00000000 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/database/NonClosableConnection.java +++ /dev/null @@ -1,338 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.database; - -import java.sql.Array; -import java.sql.Blob; -import java.sql.CallableStatement; -import java.sql.Clob; -import java.sql.Connection; -import java.sql.DatabaseMetaData; -import java.sql.NClob; -import java.sql.PreparedStatement; -import java.sql.SQLClientInfoException; -import java.sql.SQLException; -import java.sql.SQLWarning; -import java.sql.SQLXML; -import java.sql.Savepoint; -import java.sql.Statement; -import java.sql.Struct; -import java.util.Map; -import java.util.Properties; -import java.util.concurrent.Executor; - -// This code is provided to you by LuckPerms, under the MIT license. - -/** - * A wrapper around a {@link Connection} which blocks usage of the default {@link #close()} method. - */ -public final class NonClosableConnection implements Connection { - private final Connection delegate; - - public NonClosableConnection(final Connection delegate) { - this.delegate = delegate; - } - - public void close0() throws SQLException { - this.delegate.close(); - } - - @Override - public void close() { - // do nothing - } - - @Override - public boolean isWrapperFor(final Class inter) throws SQLException { - return inter.isInstance(this.delegate) || this.delegate.isWrapperFor(inter); - } - - @SuppressWarnings("unchecked") - @Override - public T unwrap(final Class inter) throws SQLException { - if (inter.isInstance(this.delegate)) { - return (T) this.delegate; - } - return this.delegate.unwrap(inter); - } - - // Forward to the delegate connection - @Override - public Statement createStatement() throws SQLException { - return this.delegate.createStatement(); - } - - @Override - public PreparedStatement prepareStatement(final String sql) throws SQLException { - return this.delegate.prepareStatement(sql); - } - - @Override - public CallableStatement prepareCall(final String sql) throws SQLException { - return this.delegate.prepareCall(sql); - } - - @Override - public String nativeSQL(final String sql) throws SQLException { - return this.delegate.nativeSQL(sql); - } - - @Override - public void setAutoCommit(final boolean autoCommit) throws SQLException { - this.delegate.setAutoCommit(autoCommit); - } - - @Override - public boolean getAutoCommit() throws SQLException { - return this.delegate.getAutoCommit(); - } - - @Override - public void commit() throws SQLException { - this.delegate.commit(); - } - - @Override - public void rollback() throws SQLException { - this.delegate.rollback(); - } - - @Override - public boolean isClosed() throws SQLException { - return this.delegate.isClosed(); - } - - @Override - public DatabaseMetaData getMetaData() throws SQLException { - return this.delegate.getMetaData(); - } - - @Override - public void setReadOnly(final boolean readOnly) throws SQLException { - this.delegate.setReadOnly(readOnly); - } - - @Override - public boolean isReadOnly() throws SQLException { - return this.delegate.isReadOnly(); - } - - @Override - public void setCatalog(final String catalog) throws SQLException { - this.delegate.setCatalog(catalog); - } - - @Override - public String getCatalog() throws SQLException { - return this.delegate.getCatalog(); - } - - @Override - public void setTransactionIsolation(final int level) throws SQLException { - this.delegate.setTransactionIsolation(level); - } - - @Override - public int getTransactionIsolation() throws SQLException { - return this.delegate.getTransactionIsolation(); - } - - @Override - public SQLWarning getWarnings() throws SQLException { - return this.delegate.getWarnings(); - } - - @Override - public void clearWarnings() throws SQLException { - this.delegate.clearWarnings(); - } - - @Override - public Statement createStatement(final int resultSetType, final int resultSetConcurrency) throws SQLException { - return this.delegate.createStatement(resultSetType, resultSetConcurrency); - } - - @Override - public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency) - throws SQLException { - return this.delegate.prepareStatement(sql, resultSetType, resultSetConcurrency); - } - - @Override - public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency) - throws SQLException { - return this.delegate.prepareCall(sql, resultSetType, resultSetConcurrency); - } - - @Override - public Map> getTypeMap() throws SQLException { - return this.delegate.getTypeMap(); - } - - @Override - public void setTypeMap(final Map> map) throws SQLException { - this.delegate.setTypeMap(map); - } - - @Override - public void setHoldability(final int holdability) throws SQLException { - this.delegate.setHoldability(holdability); - } - - @Override - public int getHoldability() throws SQLException { - return this.delegate.getHoldability(); - } - - @Override - public Savepoint setSavepoint() throws SQLException { - return this.delegate.setSavepoint(); - } - - @Override - public Savepoint setSavepoint(final String name) throws SQLException { - return this.delegate.setSavepoint(name); - } - - @Override - public void rollback(final Savepoint savepoint) throws SQLException { - this.delegate.rollback(savepoint); - } - - @Override - public void releaseSavepoint(final Savepoint savepoint) throws SQLException { - this.delegate.releaseSavepoint(savepoint); - } - - @Override - public Statement createStatement( - final int resultSetType, final int resultSetConcurrency, final int resultSetHoldability) - throws SQLException { - return this.delegate.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability); - } - - @Override - public PreparedStatement prepareStatement( - final String sql, final int resultSetType, final int resultSetConcurrency, final int resultSetHoldability) - throws SQLException { - return this.delegate.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); - } - - @Override - public CallableStatement prepareCall( - final String sql, final int resultSetType, final int resultSetConcurrency, final int resultSetHoldability) - throws SQLException { - return this.delegate.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability); - } - - @Override - public PreparedStatement prepareStatement(final String sql, final int autoGeneratedKeys) throws SQLException { - return this.delegate.prepareStatement(sql, autoGeneratedKeys); - } - - @Override - public PreparedStatement prepareStatement(final String sql, final int[] columnIndexes) throws SQLException { - return this.delegate.prepareStatement(sql, columnIndexes); - } - - @Override - public PreparedStatement prepareStatement(final String sql, final String[] columnNames) throws SQLException { - return this.delegate.prepareStatement(sql, columnNames); - } - - @Override - public Clob createClob() throws SQLException { - return this.delegate.createClob(); - } - - @Override - public Blob createBlob() throws SQLException { - return this.delegate.createBlob(); - } - - @Override - public NClob createNClob() throws SQLException { - return this.delegate.createNClob(); - } - - @Override - public SQLXML createSQLXML() throws SQLException { - return this.delegate.createSQLXML(); - } - - @Override - public boolean isValid(final int timeout) throws SQLException { - return this.delegate.isValid(timeout); - } - - @Override - public void setClientInfo(final String name, final String value) throws SQLClientInfoException { - this.delegate.setClientInfo(name, value); - } - - @Override - public void setClientInfo(final Properties properties) throws SQLClientInfoException { - this.delegate.setClientInfo(properties); - } - - @Override - public String getClientInfo(final String name) throws SQLException { - return this.delegate.getClientInfo(name); - } - - @Override - public Properties getClientInfo() throws SQLException { - return this.delegate.getClientInfo(); - } - - @Override - public Array createArrayOf(final String typeName, final Object[] elements) throws SQLException { - return this.delegate.createArrayOf(typeName, elements); - } - - @Override - public Struct createStruct(final String typeName, final Object[] attributes) throws SQLException { - return this.delegate.createStruct(typeName, attributes); - } - - @Override - public void setSchema(final String schema) throws SQLException { - this.delegate.setSchema(schema); - } - - @Override - public String getSchema() throws SQLException { - return this.delegate.getSchema(); - } - - @Override - public void abort(final Executor executor) throws SQLException { - this.delegate.abort(executor); - } - - @Override - public void setNetworkTimeout(final Executor executor, final int milliseconds) throws SQLException { - this.delegate.setNetworkTimeout(executor, milliseconds); - } - - @Override - public int getNetworkTimeout() throws SQLException { - return this.delegate.getNetworkTimeout(); - } -} diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/database/SQLiteConnectionFactory.java b/distributor-core/src/main/java/fr/xpdustry/distributor/core/database/SQLiteConnectionFactory.java deleted file mode 100644 index a5fdbf41..00000000 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/database/SQLiteConnectionFactory.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.database; - -import fr.xpdustry.distributor.core.dependency.Dependency; -import java.lang.reflect.Constructor; -import java.nio.file.Path; -import java.sql.Connection; -import java.sql.SQLException; -import java.util.Properties; -import java.util.function.Function; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; - -// This code is provided to you by LuckPerms, under the MIT license. -public final class SQLiteConnectionFactory implements ConnectionFactory { - - public static final Dependency SQLITE_DRIVER = - new Dependency("org.xerial", "sqlite-jdbc", "3.43.0.0", "UFJLFrZJ+wP4HfbmHexpkRuISeaUPGG4X6ok5Jv9mPw="); - - private final String prefix; - private final Path path; - private final ClassLoader classLoader; - private @MonotonicNonNull Constructor constructor; - private @MonotonicNonNull NonClosableConnection connection; - - public SQLiteConnectionFactory(final String prefix, final Path path, final ClassLoader classLoader) { - this.prefix = prefix; - this.path = path; - this.classLoader = classLoader; - } - - @Override - public Connection getConnection() throws SQLException { - var connection = this.connection; - if (connection == null || connection.isClosed()) { - this.connection = connection = new NonClosableConnection(this.createConnection()); - } - return connection; - } - - @Override - public void start() { - try { - final var connectionClass = this.classLoader.loadClass("org.sqlite.jdbc4.JDBC4Connection"); - this.constructor = connectionClass.getConstructor(String.class, String.class, Properties.class); - } catch (final ReflectiveOperationException e) { - throw new RuntimeException(e); - } - } - - @Override - public void close() throws SQLException { - if (this.connection != null) { - this.connection.close0(); - } - } - - private Connection createConnection() throws SQLException { - try { - return (Connection) - this.constructor.newInstance("jdbc:sqlite:" + this.path, this.path.toString(), new Properties()); - } catch (final ReflectiveOperationException e) { - if (e.getCause() instanceof SQLException) { - throw (SQLException) e.getCause(); - } - throw new RuntimeException(e); - } - } - - @Override - public Function getStatementProcessor() { - return statement -> statement.replace("{prefix}", this.prefix).replace('\'', '`'); - } -} diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/dependency/Dependency.java b/distributor-core/src/main/java/fr/xpdustry/distributor/core/dependency/Dependency.java deleted file mode 100644 index a6d2af54..00000000 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/dependency/Dependency.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.dependency; - -import java.util.Base64; - -// Simpler Library class from https://github.com/Byteflux/libby, under the MIT license. -public record Dependency(String group, String artifact, String version, byte[] checksum) { - public Dependency(final String group, final String artifact, final String version, final byte[] checksum) { - this.group = group; - this.artifact = artifact; - this.version = version; - this.checksum = checksum.clone(); - } - - public Dependency(final String group, final String artifact, final String version, final String checksum) { - this(group, artifact, version, Base64.getDecoder().decode(checksum)); - } - - public Dependency(final String group, final String artifact, final String version) { - this(group, artifact, version, new byte[0]); - } - - public String path() { - return this.group.replace('.', '/') - + '/' + this.artifact - + '/' + this.version - + '/' + this.artifact - + '-' + this.version + ".jar"; - } - - @Override - public byte[] checksum() { - return this.checksum.clone(); - } -} diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/dependency/DependencyManager.java b/distributor-core/src/main/java/fr/xpdustry/distributor/core/dependency/DependencyManager.java deleted file mode 100644 index 7d7bb7cf..00000000 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/dependency/DependencyManager.java +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.dependency; - -import java.io.ByteArrayOutputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.io.UncheckedIOException; -import java.net.MalformedURLException; -import java.net.SocketTimeoutException; -import java.net.URL; -import java.net.UnknownHostException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Base64; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -// Simpler LibraryManager from https://github.com/Byteflux/libby, under the MIT license. -public final class DependencyManager { - - private static final Logger logger = LoggerFactory.getLogger(DependencyManager.class); - - private final List repositories = new ArrayList<>(); - private final Map, IsolatedClassLoader> loaders = new HashMap<>(); - private final Path directory; - - public DependencyManager(final Path directory) { - this.directory = directory; - } - - public void addRepository(final String url) { - final var repo = url.endsWith("/") ? url : url + '/'; - synchronized (this.repositories) { - this.repositories.add(repo); - } - } - - public void addMavenLocal() { - this.addRepository(Paths.get(System.getProperty("user.home")) - .resolve(".m2/repository") - .toUri() - .toString()); - } - - public void addMavenCentral() { - this.addRepository("https://repo1.maven.org/maven2/"); - } - - public void addSonatype() { - this.addRepository("https://oss.sonatype.org/content/groups/public/"); - } - - public void addXpdustryMaven() { - this.addRepository("https://maven.xpdustry.com/releases/"); - } - - public void addJitPack() { - this.addRepository("https://jitpack.io/"); - } - - public Collection resolve(final Dependency dependency) { - final List urls = new ArrayList<>(); - for (final var repository : this.repositories) { - urls.add(repository + dependency.path()); - } - return Collections.unmodifiableList(urls); - } - - public IsolatedClassLoader createClassLoaderFor(final Dependency... dependencies) { - return this.loaders.computeIfAbsent(Set.of(dependencies), key -> { - final var urls = new ArrayList(); - for (final var dependency : key) { - try { - urls.add(this.downloadDependency(dependency).toUri().toURL()); - } catch (final MalformedURLException e) { - throw new UncheckedIOException(e); - } - } - return new IsolatedClassLoader(urls.toArray(new URL[0])); - }); - } - - public Path downloadDependency(final Dependency dependency) { - final Path file = this.directory.resolve(dependency.path()); - if (Files.exists(file)) { - return file; - } - - final List urls = new ArrayList<>(); - for (final var repository : this.repositories) { - urls.add(repository + dependency.path()); - } - - if (urls.isEmpty()) { - throw new RuntimeException("Dependency '" + dependency + "' couldn't be resolved, add a repository"); - } - - MessageDigest md = null; - if (dependency.checksum().length != 0) { - try { - md = MessageDigest.getInstance("SHA-256"); - } catch (final NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } - } - - final Path out = file.resolveSibling(file.getFileName() + ".tmp"); - out.toFile().deleteOnExit(); - - try { - Files.createDirectories(file.getParent()); - - for (final String url : urls) { - final byte[] bytes = this.downloadDependency(url); - if (bytes == null) { - continue; - } - - if (md != null) { - final byte[] checksum = md.digest(bytes); - if (!Arrays.equals(checksum, dependency.checksum())) { - logger.error("*** INVALID CHECKSUM ***"); - logger.error(" Dependency : " + dependency); - logger.error(" URL : " + url); - logger.error(" Expected : " + Base64.getEncoder().encodeToString(dependency.checksum())); - logger.error(" Actual : " + Base64.getEncoder().encodeToString(checksum)); - throw new RuntimeException("Invalid checksum for dependency '" + dependency + "'"); - } - } - - Files.write(out, bytes); - Files.move(out, file); - - return file; - } - } catch (final IOException e) { - throw new UncheckedIOException(e); - } finally { - try { - Files.deleteIfExists(out); - } catch (final IOException ignored) { - } - } - - throw new RuntimeException("Failed to download library '" + dependency + "'"); - } - - private byte @Nullable [] downloadDependency(final String url) { - try { - logger.info("Downloading dependency from " + url); - final var connection = new URL(url).openConnection(); - - connection.setConnectTimeout(5000); - connection.setReadTimeout(5000); - - try (final InputStream in = connection.getInputStream()) { - int len; - final byte[] buf = new byte[8192]; - final var out = new ByteArrayOutputStream(); - - try { - while ((len = in.read(buf)) != -1) { - out.write(buf, 0, len); - } - } catch (final SocketTimeoutException e) { - logger.warn("Download timed out: " + connection.getURL()); - return null; - } - - logger.info("Downloaded library " + connection.getURL()); - return out.toByteArray(); - } - } catch (final MalformedURLException e) { - throw new IllegalArgumentException(e); - } catch (final IOException e) { - if (e instanceof FileNotFoundException) { - logger.debug("File not found: " + url); - } else if (e instanceof SocketTimeoutException) { - logger.debug("Connect timed out: " + url); - } else if (e instanceof UnknownHostException) { - logger.debug("Unknown host: " + url); - } else { - logger.debug("Unexpected IOException", e); - } - - return null; - } - } -} diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/dependency/IsolatedClassLoader.java b/distributor-core/src/main/java/fr/xpdustry/distributor/core/dependency/IsolatedClassLoader.java deleted file mode 100644 index 2275d3f0..00000000 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/dependency/IsolatedClassLoader.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.dependency; - -import java.net.URL; -import java.net.URLClassLoader; - -// Simpler IsolatedClassLoader from https://github.com/Byteflux/libby, under the MIT license. -public final class IsolatedClassLoader extends URLClassLoader { - - static { - ClassLoader.registerAsParallelCapable(); - } - - public IsolatedClassLoader(final URL... urls) { - super(urls, ClassLoader.getSystemClassLoader().getParent()); - } -} diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/event/SimpleEventBus.java b/distributor-core/src/main/java/fr/xpdustry/distributor/core/event/SimpleEventBus.java deleted file mode 100644 index 8f863b86..00000000 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/event/SimpleEventBus.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.event; - -import arc.Events; -import arc.func.Cons; -import arc.struct.ObjectMap; -import arc.struct.Seq; -import fr.xpdustry.distributor.api.event.EventBus; -import fr.xpdustry.distributor.api.event.EventHandler; -import fr.xpdustry.distributor.api.event.EventSubscription; -import fr.xpdustry.distributor.api.plugin.MindustryPlugin; -import fr.xpdustry.distributor.api.plugin.PluginAware; -import fr.xpdustry.distributor.api.util.Priority; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.function.Consumer; - -public final class SimpleEventBus implements EventBus { - - private static final Comparator> COMPARATOR = (a, b) -> { - final var priorityA = a instanceof PriorityCons m ? m.getPriority() : Priority.NORMAL; - final var priorityB = b instanceof PriorityCons m ? m.getPriority() : Priority.NORMAL; - return priorityA.compareTo(priorityB); - }; - - final ObjectMap>> events; - - @SuppressWarnings("unchecked") - public SimpleEventBus() { - try { - final var field = Events.class.getDeclaredField("events"); - field.setAccessible(true); - this.events = (ObjectMap>>) field.get(null); - } catch (final ReflectiveOperationException e) { - throw new RuntimeException(e); - } - } - - @Override - public EventSubscription subscribe( - final Class event, final Priority priority, final MindustryPlugin plugin, final Consumer listener) { - return this.subscribe(event, new ConsumerCons<>(listener, priority, plugin)); - } - - @Override - public > EventSubscription subscribe( - final E event, final Priority priority, final MindustryPlugin plugin, final Runnable listener) { - return this.subscribe(event, new ConsumerCons<>(e -> listener.run(), priority, plugin)); - } - - private EventSubscription subscribe(final Object event, final PriorityCons subscriber) { - this.events.get(event, () -> new Seq<>(Cons.class)).add(subscriber).sort(COMPARATOR); - return () -> { - final var subscribers = this.events.get(event); - if (subscribers != null) { - subscribers.remove(subscriber); - if (subscribers.isEmpty()) { - this.events.remove(event); - } - } - }; - } - - @Override - public void post(final E event) { - Events.fire(event.getClass(), event); - } - - @Override - public void post(final Class clazz, final E event) { - Events.fire(clazz, event); - } - - @Override - public > void post(final E event) { - Events.fire(event); - } - - @SuppressWarnings("removal") - @Override - public EventSubscription parse(final MindustryPlugin plugin, final Object listener) { - final List subscriptions = new ArrayList<>(); - for (final var method : listener.getClass().getDeclaredMethods()) { - final var annotation = method.getAnnotation(EventHandler.class); - if (annotation == null) { - continue; - } else if (method.getParameterCount() != 1) { - throw new IllegalArgumentException( - "The event handler on " + method + " hasn't the right parameter count."); - } else if (!method.canAccess(listener) || !method.trySetAccessible()) { - throw new RuntimeException("Unable to make " + method + " accessible."); - } - - final var cons = new MethodCons<>(listener, method, annotation.priority(), plugin); - subscriptions.add(this.subscribe(cons.getEventType(), cons)); - } - return () -> subscriptions.forEach(EventSubscription::unsubscribe); - } - - private interface PriorityCons extends Cons, PluginAware { - - Priority getPriority(); - } - - private static final class ConsumerCons implements PriorityCons { - - private final Consumer consumer; - private final Priority priority; - private final MindustryPlugin plugin; - - private ConsumerCons(final Consumer consumer, final Priority priority, final MindustryPlugin plugin) { - this.consumer = consumer; - this.priority = priority; - this.plugin = plugin; - } - - @Override - public void get(final T event) { - try { - this.consumer.accept(event); - } catch (final Throwable e) { - this.plugin - .getLogger() - .atError() - .setMessage("An error occurred while handling a {} event.") - .addArgument(event.getClass().getSimpleName()) - .setCause(e) - .log(); - } - } - - @Override - public Priority getPriority() { - return this.priority; - } - - @Override - public MindustryPlugin getPlugin() { - return this.plugin; - } - } - - private static final class MethodCons implements PriorityCons { - - private final Object target; - private final Method method; - private final Priority priority; - private final MindustryPlugin plugin; - - private MethodCons( - final Object target, final Method method, final Priority priority, final MindustryPlugin plugin) { - this.target = target; - this.method = method; - this.priority = priority; - this.plugin = plugin; - } - - @Override - public void get(final E event) { - try { - this.method.invoke(this.target, event); - } catch (final InvocationTargetException e) { - this.plugin - .getLogger() - .atError() - .setMessage("An error occurred while handling a {} event.") - .addArgument(event.getClass().getSimpleName()) - .setCause(e.getTargetException()) - .log(); - } catch (final ReflectiveOperationException e) { - throw new RuntimeException("Failed to call " + this.method + " on " + this.target, e); - } - } - - @SuppressWarnings("unchecked") - public Class getEventType() { - return (Class) this.method.getParameterTypes()[0]; - } - - @Override - public Priority getPriority() { - return this.priority; - } - - @Override - public MindustryPlugin getPlugin() { - return this.plugin; - } - } -} diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/package-info.java b/distributor-core/src/main/java/fr/xpdustry/distributor/core/package-info.java deleted file mode 100644 index 465eafd9..00000000 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/package-info.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2022 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -@DefaultQualifier( - value = NonNull.class, - locations = { - TypeUseLocation.CONSTRUCTOR_RESULT, - TypeUseLocation.EXCEPTION_PARAMETER, - TypeUseLocation.EXPLICIT_LOWER_BOUND, - TypeUseLocation.EXPLICIT_UPPER_BOUND, - TypeUseLocation.FIELD, - TypeUseLocation.IMPLICIT_LOWER_BOUND, - TypeUseLocation.IMPLICIT_UPPER_BOUND, - TypeUseLocation.LOWER_BOUND, - TypeUseLocation.PARAMETER, - TypeUseLocation.RECEIVER, - TypeUseLocation.RESOURCE_VARIABLE, - TypeUseLocation.RETURN, - TypeUseLocation.UPPER_BOUND, - TypeUseLocation.OTHERWISE, - }) -package fr.xpdustry.distributor.core; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.framework.qual.DefaultQualifier; -import org.checkerframework.framework.qual.TypeUseLocation; diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/scheduler/RecipePluginTask.java b/distributor-core/src/main/java/fr/xpdustry/distributor/core/scheduler/RecipePluginTask.java deleted file mode 100644 index 0030efe9..00000000 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/scheduler/RecipePluginTask.java +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.scheduler; - -import fr.xpdustry.distributor.api.plugin.MindustryPlugin; -import fr.xpdustry.distributor.api.scheduler.PluginTask; -import fr.xpdustry.distributor.api.scheduler.PluginTaskRecipe; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.function.Consumer; -import java.util.function.Function; - -final class RecipePluginTask implements ScheduledPluginTask { - - private final SimplePluginScheduler scheduler; - private final MindustryPlugin plugin; - private final Iterable> steps; - private final CompletableFuture completion = new CompletableFuture<>(); - private final Object initialObject; - - private RecipePluginTask( - final SimplePluginScheduler scheduler, final MindustryPlugin plugin, final Builder recipe) { - this.scheduler = scheduler; - this.plugin = plugin; - this.steps = new ArrayList<>(recipe.steps); - this.initialObject = recipe.initialObject; - } - - @Override - public boolean isAsync() { - return false; - } - - @Override - public boolean cancel(final boolean mayInterruptIfRunning) { - return this.completion.cancel(mayInterruptIfRunning); - } - - @Override - public boolean isCancelled() { - return this.completion.isCancelled(); - } - - @Override - public boolean isDone() { - return this.completion.isDone(); - } - - @Override - public V get() throws InterruptedException, ExecutionException { - return this.completion.get(); - } - - @Override - public V get(final long timeout, final TimeUnit unit) - throws InterruptedException, ExecutionException, TimeoutException { - return this.completion.get(timeout, unit); - } - - @Override - public MindustryPlugin getPlugin() { - return this.plugin; - } - - @Override - public void run() { - this.run0(this.initialObject, this.steps.iterator()); - } - - @SuppressWarnings({"unchecked", "rawtypes"}) - private void run0(final Object current, final Iterator> steps) { - if (this.completion.isDone()) { - return; - } - if (!steps.hasNext()) { - this.completion.complete((V) current); - return; - } - final RecipeStep step = steps.next(); - final var builder = - step.async ? this.scheduler.scheduleAsync(this.plugin) : this.scheduler.scheduleSync(this.plugin); - builder.execute(() -> { - try { - final var next = step.apply(current); - this.run0(next, steps); - } catch (final Throwable throwable) { - this.completion.completeExceptionally(throwable); - this.plugin.getLogger().error("An exception occurred in the scheduler with no handler", throwable); - } - }); - } - - @Override - public long getNextExecutionTime() { - return 0L; - } - - abstract static sealed class RecipeStep implements Function { - - public final boolean async; - - private RecipeStep(final boolean async) { - this.async = async; - } - } - - static final class ConsumerRecipeStep extends RecipeStep { - - private final Consumer consumer; - - private ConsumerRecipeStep(final Consumer consumer, final boolean async) { - super(async); - this.consumer = consumer; - } - - @Override - public T apply(final T object) { - this.consumer.accept(object); - return object; - } - } - - static final class FunctionRecipeStep extends RecipeStep { - - private final Function function; - - private FunctionRecipeStep(final Function function, final boolean async) { - super(async); - this.function = function; - } - - @Override - public R apply(final T object) { - return this.function.apply(object); - } - } - - static final class RunnableRecipeStep extends RecipeStep { - - private final Runnable runnable; - - private RunnableRecipeStep(final Runnable runnable, final boolean async) { - super(async); - this.runnable = runnable; - } - - @Override - public T apply(final T object) { - this.runnable.run(); - return object; - } - } - - static final class Builder implements PluginTaskRecipe { - - private final SimplePluginScheduler scheduler; - private final MindustryPlugin plugin; - private final Object initialObject; - private final List> steps; - - public Builder( - final SimplePluginScheduler scheduler, - final MindustryPlugin plugin, - final Object initialObject, - final List> steps) { - this.scheduler = scheduler; - this.plugin = plugin; - this.initialObject = initialObject; - this.steps = steps; - } - - @Override - public PluginTaskRecipe thenAccept(final Consumer consumer) { - return this.withStep(new ConsumerRecipeStep<>(consumer, false)); - } - - @Override - public PluginTaskRecipe thenApply(final Function function) { - return this.withStep(new FunctionRecipeStep<>(function, false)); - } - - @Override - public PluginTaskRecipe thenRun(final Runnable runnable) { - return this.withStep(new RunnableRecipeStep<>(runnable, false)); - } - - @Override - public PluginTaskRecipe thenAcceptAsync(final Consumer consumer) { - return this.withStep(new ConsumerRecipeStep<>(consumer, true)); - } - - @Override - public PluginTaskRecipe thenApplyAsync(final Function function) { - return this.withStep(new FunctionRecipeStep<>(function, true)); - } - - @Override - public PluginTaskRecipe thenRunAsync(final Runnable runnable) { - return this.withStep(new RunnableRecipeStep<>(runnable, true)); - } - - @Override - public PluginTask execute() { - final var task = new RecipePluginTask<>(this.scheduler, this.plugin, this); - this.scheduler.schedule(task); - return task; - } - - private PluginTaskRecipe withStep(final RecipeStep step) { - final var steps = new ArrayList<>(this.steps); - steps.add(step); - return new Builder<>(this.scheduler, this.plugin, this.initialObject, steps); - } - } -} diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/scheduler/SimplePluginScheduler.java b/distributor-core/src/main/java/fr/xpdustry/distributor/core/scheduler/SimplePluginScheduler.java deleted file mode 100644 index abc3aeaa..00000000 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/scheduler/SimplePluginScheduler.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.scheduler; - -import fr.xpdustry.distributor.api.plugin.MindustryPlugin; -import fr.xpdustry.distributor.api.plugin.PluginListener; -import fr.xpdustry.distributor.api.scheduler.Cancellable; -import fr.xpdustry.distributor.api.scheduler.PluginScheduler; -import fr.xpdustry.distributor.api.scheduler.PluginTask; -import fr.xpdustry.distributor.api.scheduler.PluginTaskBuilder; -import fr.xpdustry.distributor.api.scheduler.PluginTaskRecipe; -import fr.xpdustry.distributor.api.scheduler.TaskHandler; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.Queue; -import java.util.concurrent.Executor; -import java.util.concurrent.ForkJoinPool; -import java.util.concurrent.ForkJoinWorkerThread; -import java.util.concurrent.PriorityBlockingQueue; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Consumer; -import java.util.stream.Collectors; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public final class SimplePluginScheduler implements PluginScheduler, PluginListener { - - static final String DISTRIBUTOR_WORKER_BASE_NAME = "distributor-worker-"; - private static final Logger logger = LoggerFactory.getLogger("PluginScheduler"); - - private final Queue> tasks = - new PriorityBlockingQueue<>(16, Comparator.comparing(ScheduledPluginTask::getNextExecutionTime)); - private final ForkJoinPool pool; - private final Executor syncExecutor; - private final TimeSource source; - - public SimplePluginScheduler(final TimeSource source, final Executor syncExecutor, final int parallelism) { - this.pool = new ForkJoinPool(parallelism, new PluginSchedulerWorkerThreadFactory(), null, false); - this.syncExecutor = syncExecutor; - this.source = source; - } - - @Override - public PluginTaskBuilder scheduleAsync(final MindustryPlugin plugin) { - return new SimplePluginTask.Builder(this, plugin, true); - } - - @Override - public PluginTaskBuilder scheduleSync(final MindustryPlugin plugin) { - return new SimplePluginTask.Builder(this, plugin, false); - } - - @SuppressWarnings("removal") - @Override - public PluginTaskRecipe recipe(final MindustryPlugin plugin, final V value) { - return new RecipePluginTask.Builder<>(this, plugin, value, new ArrayList<>()); - } - - @SuppressWarnings("removal") - @Override - public List> parse(final MindustryPlugin plugin, final Object object) { - final List> tasks = new ArrayList<>(); - for (final var method : object.getClass().getDeclaredMethods()) { - final var annotation = method.getAnnotation(TaskHandler.class); - if (annotation == null) { - continue; - } else if (method.getParameterCount() > 1) { - throw new IllegalArgumentException( - "The event handler on " + method + " hasn't the right parameter count."); - } else if (!method.canAccess(object) || !method.trySetAccessible()) { - throw new RuntimeException("Unable to make " + method + " accessible."); - } else if (method.getParameterCount() == 1 && !Cancellable.class.equals(method.getParameterTypes()[0])) { - throw new IllegalArgumentException( - "The event handler on " + method + " hasn't the right parameter type."); - } - - final var builder = annotation.async() ? this.scheduleAsync(plugin) : this.scheduleSync(plugin); - if (annotation.interval() > -1) { - builder.repeat(annotation.interval(), annotation.unit()); - } - if (annotation.delay() > -1) { - builder.delay(annotation.delay(), annotation.unit()); - } - tasks.add(builder.execute(new MethodPluginTask(object, method))); - } - return Collections.unmodifiableList(tasks); - } - - @Override - public void onPluginUpdate() { - while (!this.tasks.isEmpty()) { - final var task = this.tasks.peek(); - if (task.isCancelled()) { - this.tasks.remove(); - } else if (task.getNextExecutionTime() < this.source.getCurrentTicks()) { - this.tasks.remove(); - final Executor executor = task.isAsync() ? this.pool : this.syncExecutor; - executor.execute(task); - } else { - break; - } - } - } - - @Override - public void onPluginExit() { - logger.info("Shutdown scheduler."); - this.pool.shutdown(); - try { - if (!this.pool.awaitTermination(20, TimeUnit.SECONDS)) { - logger.error("Timed out waiting for the scheduler to terminate properly"); - Thread.getAllStackTraces().forEach((thread, stack) -> { - if (thread.getName().startsWith(DISTRIBUTOR_WORKER_BASE_NAME)) { - logger.error( - "Worker thread {} may be blocked, possibly the reason for the slow shutdown:\n{}", - thread.getName(), - Arrays.stream(stack).map(e -> " " + e).collect(Collectors.joining("\n"))); - } - }); - } - } catch (final InterruptedException e) { - logger.error("The plugin scheduler shutdown have been interrupted.", e); - } - } - - void schedule(final ScheduledPluginTask task) { - this.tasks.add(task); - } - - TimeSource getTimeSource() { - return this.source; - } - - boolean isShutdown() { - return this.pool.isShutdown(); - } - - private static final class PluginSchedulerWorkerThreadFactory implements ForkJoinPool.ForkJoinWorkerThreadFactory { - - private static final AtomicInteger COUNT = new AtomicInteger(0); - - @Override - public ForkJoinWorkerThread newThread(final ForkJoinPool pool) { - final var thread = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool); - thread.setName(DISTRIBUTOR_WORKER_BASE_NAME + COUNT.getAndIncrement()); - return thread; - } - } - - private static final class MethodPluginTask implements Consumer { - - private final Object object; - private final Method method; - - private MethodPluginTask(final Object object, final Method method) { - this.object = object; - this.method = method; - } - - @Override - public void accept(final Cancellable cancellable) { - try { - if (this.method.getParameterCount() == 1) { - this.method.invoke(this.object, cancellable); - } else { - this.method.invoke(this.object); - } - } catch (final IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException("Unable to invoke " + this.method, e); - } - } - } -} diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/scheduler/SimplePluginTask.java b/distributor-core/src/main/java/fr/xpdustry/distributor/core/scheduler/SimplePluginTask.java deleted file mode 100644 index a6d75f3b..00000000 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/scheduler/SimplePluginTask.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.scheduler; - -import fr.xpdustry.distributor.api.plugin.MindustryPlugin; -import fr.xpdustry.distributor.api.scheduler.Cancellable; -import fr.xpdustry.distributor.api.scheduler.MindustryTimeUnit; -import fr.xpdustry.distributor.api.scheduler.PluginTask; -import fr.xpdustry.distributor.api.scheduler.PluginTaskBuilder; -import java.util.concurrent.Callable; -import java.util.concurrent.Executors; -import java.util.concurrent.FutureTask; -import java.util.function.Consumer; -import java.util.function.Supplier; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; - -public final class SimplePluginTask extends FutureTask implements ScheduledPluginTask { - - private final MindustryPlugin plugin; - private final boolean async; - private final long period; - private final SimplePluginScheduler scheduler; - private long nextRun; - - private SimplePluginTask( - final MindustryPlugin plugin, - final Callable callable, - final boolean async, - final long period, - final SimplePluginScheduler scheduler) { - super(callable); - this.plugin = plugin; - this.async = async; - this.period = period; - this.scheduler = scheduler; - } - - @Override - public void run() { - if (this.scheduler.isShutdown() - && (this.period == 0 - || this.nextRun - this.scheduler.getTimeSource().getCurrentTicks() > 0)) { - this.cancel(false); - } else if (this.period == 0) { - super.run(); - } else if (super.runAndReset()) { - this.nextRun = this.scheduler.getTimeSource().getCurrentTicks() + this.period; - this.scheduler.schedule(this); - } - } - - @Override - public boolean isAsync() { - return this.async; - } - - @Override - public MindustryPlugin getPlugin() { - return this.plugin; - } - - @Override - protected void setException(final Throwable throwable) { - super.setException(throwable); - this.plugin - .getLogger() - .error( - "An error occurred in thread {} of the plugin scheduler.", - Thread.currentThread().getName(), - throwable); - } - - @Override - public long getNextExecutionTime() { - return this.nextRun; - } - - public static final class Builder implements PluginTaskBuilder { - - private final SimplePluginScheduler scheduler; - private final MindustryPlugin plugin; - private final boolean async; - private long delay = 0; - private long repeat = 0; - - public Builder(final SimplePluginScheduler scheduler, final MindustryPlugin plugin, final boolean async) { - this.scheduler = scheduler; - this.plugin = plugin; - this.async = async; - } - - @Override - public PluginTaskBuilder delay(final long delay, final MindustryTimeUnit unit) { - this.delay = MindustryTimeUnit.TICKS.convert(delay, unit); - return this; - } - - @Override - public PluginTaskBuilder repeat(final long interval, final MindustryTimeUnit unit) { - this.repeat = MindustryTimeUnit.TICKS.convert(interval, unit); - return this; - } - - @Override - public PluginTask execute(final Runnable runnable) { - final var task = new SimplePluginTask( - this.plugin, Executors.callable(runnable, null), this.async, this.repeat, this.scheduler); - return this.schedule(task); - } - - @Override - public PluginTask execute(final Consumer consumer) { - final var cancellable = new SimplePluginTaskCancellable(); - final var task = new SimplePluginTask( - this.plugin, - Executors.callable(() -> consumer.accept(cancellable), null), - this.async, - this.repeat, - this.scheduler); - cancellable.task = task; - return this.schedule(task); - } - - @Override - public PluginTask execute(final Supplier supplier) { - final var task = - new SimplePluginTask<>(this.plugin, supplier::get, this.async, this.repeat, this.scheduler); - return this.schedule(task); - } - - private ScheduledPluginTask schedule(final SimplePluginTask task) { - task.nextRun = this.scheduler.getTimeSource().getCurrentTicks() + this.delay; - this.scheduler.schedule(task); - return task; - } - } - - private static final class SimplePluginTaskCancellable implements Cancellable { - - private @MonotonicNonNull PluginTask task = null; - - @Override - public void cancel() { - this.task.cancel(false); - } - } -} diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/scheduler/TimeSource.java b/distributor-core/src/main/java/fr/xpdustry/distributor/core/scheduler/TimeSource.java deleted file mode 100644 index eaa4ab96..00000000 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/scheduler/TimeSource.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.scheduler; - -import arc.util.Time; - -/** - * A {@code PluginTimeSource} provides the current time in milliseconds. - */ -@FunctionalInterface -public interface TimeSource { - - /** - * Returns a {@code PluginTimeSource} using {@link Time#globalTime} to provide the current time. - */ - static TimeSource arc() { - return () -> (long) Time.globalTime; - } - - /** - * Returns a {@code PluginTimeSource} using {@link System#currentTimeMillis()} to provide the current time. - */ - static TimeSource standard() { - return () -> System.currentTimeMillis() / 16L; - } - - long getCurrentTicks(); -} diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/security/PlayerValidatorListener.java b/distributor-core/src/main/java/fr/xpdustry/distributor/core/security/PlayerValidatorListener.java deleted file mode 100644 index fe193169..00000000 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/security/PlayerValidatorListener.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.security; - -import fr.xpdustry.distributor.api.DistributorProvider; -import fr.xpdustry.distributor.api.event.EventHandler; -import fr.xpdustry.distributor.api.plugin.PluginListener; -import fr.xpdustry.distributor.api.security.PlayerValidator; -import fr.xpdustry.distributor.api.security.PlayerValidatorEvent; -import fr.xpdustry.distributor.api.security.PlayerValidatorEvent.Type; -import fr.xpdustry.distributor.api.util.MUUID; -import fr.xpdustry.distributor.api.util.Players; -import fr.xpdustry.distributor.api.util.Priority; -import fr.xpdustry.distributor.core.DistributorConfiguration; -import fr.xpdustry.distributor.core.DistributorConfiguration.PlayerValidationPolicy; -import mindustry.game.EventType; -import mindustry.gen.Player; - -public final class PlayerValidatorListener implements PluginListener { - - private final PlayerValidator playerValidator; - private final DistributorConfiguration configuration; - - public PlayerValidatorListener( - final PlayerValidator playerValidator, final DistributorConfiguration configuration) { - this.playerValidator = playerValidator; - this.configuration = configuration; - } - - @EventHandler - public void onPlayerConnectionConfirmed(final EventType.PlayerConnectionConfirmed event) { - if (this.configuration.getIdentityValidationPolicy() == PlayerValidationPolicy.VALIDATE_UNKNOWN) { - if (!this.playerValidator.contains(event.player.uuid())) { - this.playerValidator.validate(MUUID.of(event.player)); - return; - } - if (!this.playerValidator.isValid(MUUID.of(event.player))) { - // TODO Use dependency injection ? Using the public and the private API is not a good idea. - event.player.sendMessage(DistributorProvider.get() - .getGlobalLocalizationSource() - .format("distributor.identity.player.failure", Players.getLocale(event.player))); - } - } else if (this.configuration.getIdentityValidationPolicy() == PlayerValidationPolicy.VALIDATE_ALL) { - this.playerValidator.validate(MUUID.of(event.player)); - } - } - - // Other listeners must be aware the player is valid, and that is an admin. - @EventHandler(priority = Priority.HIGH) - public void onPlayerConnect(final EventType.PlayerConnect event) { - if (this.configuration.isValidationAutoAdminEnabled() && this.canBeAdmin(event.player)) { - event.player.admin(true); - } - } - - @EventHandler - public void onPlayerValidatorEvent(final PlayerValidatorEvent event) { - if (!this.configuration.isValidationAutoAdminEnabled()) { - return; - } - - if (event.type() == Type.VALIDATED && this.canBeAdmin(event.player())) { - event.player().admin(true); - } else if (event.type() == Type.REMOVED && this.isNotRealAdmin(event.player())) { - event.player().admin(false); - } else if (event.type() == Type.INVALIDATED) { - event.player().admin(false); - } - } - - private boolean canBeAdmin(final Player player) { - return player.getInfo().admin; - } - - private boolean isNotRealAdmin(final Player player) { - return player.admin() && !MUUID.of(player).equals(MUUID.of(player.getInfo())); - } -} diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/security/SQLPlayerValidator.java b/distributor-core/src/main/java/fr/xpdustry/distributor/core/security/SQLPlayerValidator.java deleted file mode 100644 index d88f4943..00000000 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/security/SQLPlayerValidator.java +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.security; - -import com.password4j.Argon2Function; -import com.password4j.HashingFunction; -import com.password4j.types.Argon2; -import fr.xpdustry.distributor.api.DistributorProvider; -import fr.xpdustry.distributor.api.security.PlayerValidator; -import fr.xpdustry.distributor.api.security.PlayerValidatorEvent; -import fr.xpdustry.distributor.api.security.PlayerValidatorEvent.Type; -import fr.xpdustry.distributor.api.util.MUUID; -import fr.xpdustry.distributor.core.database.ConnectionFactory; -import java.sql.Connection; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Base64; -import java.util.List; -import java.util.Optional; -import mindustry.gen.Groups; - -public final class SQLPlayerValidator implements PlayerValidator { - - private static final HashingFunction HASH_FUNCTION = Argon2Function.getInstance(19, 2, 1, 32, Argon2.ID); - - private final ConnectionFactory factory; - - static byte[] hash(final MUUID muuid) { - return HASH_FUNCTION.hash(muuid.getUuid(), muuid.getUsid()).getBytes(); - } - - public SQLPlayerValidator(final ConnectionFactory factory) { - this.factory = factory; - this.factory.executeScript( - """ - CREATE TABLE IF NOT EXISTS muuid_validation ( - uuid VARBINARY(16) NOT NULL, - hash VARBINARY(32) NOT NULL, - valid BOOLEAN NOT NULL, - PRIMARY KEY (uuid, hash) - ); - """); - } - - @Override - public boolean isValid(final MUUID muuid) { - return this.factory.withFunction( - con -> this.findValidation(con, muuid).map(Validation::valid).orElse(false)); - } - - @Override - public boolean contains(final String uuid) { - return this.factory.withFunction(con -> !this.findValidation(con, uuid).isEmpty()); - } - - @Override - public boolean contains(final MUUID muuid) { - return this.factory.withFunction(con -> this.findValidation(con, muuid).isPresent()); - } - - @Override - public void validate(final MUUID muuid) { - this.factory.withConsumer(con -> this.saveValidation(con, new Validation(muuid, true))); - this.notifyChangeForOnlinePlayer(muuid, Type.VALIDATED); - } - - @Override - public void invalidate(final MUUID muuid) { - this.factory.withConsumer(con -> this.saveValidation(con, new Validation(muuid, false))); - this.notifyChangeForOnlinePlayer(muuid, Type.INVALIDATED); - } - - @Override - public void invalidate(final String uuid) { - this.factory.withConsumer(con -> { - for (final var validation : this.findValidation(con, uuid)) { - if (validation.valid) { - this.saveValidation(con, new Validation(validation.uuid, validation.hash, false)); - } - } - }); - this.notifyChangeForOnlinePlayer(uuid, Type.INVALIDATED); - } - - @Override - public void invalidateAll() { - this.factory.withConsumer(con -> { - try (final var statement = con.prepareStatement("UPDATE muuid_validation SET valid = FALSE")) { - statement.executeUpdate(); - } - }); - this.notifyChangeForAllOnlinePlayers(Type.INVALIDATED); - } - - @Override - public void remove(final String uuid) { - MUUID.checkUuid(uuid); - this.factory.withConsumer(con -> { - try (final var statement = con.prepareStatement("DELETE FROM muuid_validation WHERE uuid = ?")) { - statement.setBytes(1, Base64.getDecoder().decode(uuid)); - statement.executeUpdate(); - } - }); - this.notifyChangeForOnlinePlayer(uuid, Type.REMOVED); - } - - @Override - public void remove(final MUUID muuid) { - this.factory.withConsumer(con -> { - try (final var statement = - con.prepareStatement("DELETE FROM muuid_validation WHERE uuid = ? AND hash = ?")) { - statement.setBytes(1, muuid.getDecodedUuid()); - statement.setBytes(2, hash(muuid)); - statement.executeUpdate(); - } - }); - this.notifyChangeForOnlinePlayer(muuid, Type.REMOVED); - } - - @Override - public void removeAll() { - this.factory.withConsumer(con -> { - try (final var statement = con.prepareStatement("DELETE FROM muuid_validation")) { - statement.executeUpdate(); - } - }); - this.notifyChangeForAllOnlinePlayers(Type.REMOVED); - } - - private Optional findValidation(final Connection con, final MUUID muuid) throws SQLException { - try (final var statement = - con.prepareStatement("SELECT hash, valid FROM muuid_validation WHERE uuid = ? AND hash = ?")) { - statement.setBytes(1, muuid.getDecodedUuid()); - statement.setBytes(2, hash(muuid)); - try (final var result = statement.executeQuery()) { - return result.next() - ? Optional.of( - new Validation(muuid.getUuid(), result.getBytes("hash"), result.getBoolean("valid"))) - : Optional.empty(); - } - } - } - - private List findValidation(final Connection con, final String uuid) throws SQLException { - MUUID.checkUuid(uuid); - try (final var statement = con.prepareStatement("SELECT hash, valid FROM muuid_validation WHERE uuid = ?")) { - statement.setBytes(1, Base64.getDecoder().decode(uuid)); - try (final var result = statement.executeQuery()) { - final var list = new ArrayList(); - while (result.next()) { - list.add(new Validation(uuid, result.getBytes("hash"), result.getBoolean("valid"))); - } - return list; - } - } - } - - private void saveValidation(final Connection con, final Validation validation) throws SQLException { - if (this.contains(con, validation)) { - try (final var statement = - con.prepareStatement("UPDATE muuid_validation SET valid = ? WHERE uuid = ? AND hash = ?")) { - statement.setBoolean(1, validation.valid()); - statement.setBytes(2, validation.decodedUuid()); - statement.setBytes(3, validation.hash); - statement.executeUpdate(); - } - } else { - try (final var statement = - con.prepareStatement("INSERT INTO muuid_validation (uuid, hash, valid) VALUES (?, ?, ?)")) { - statement.setBytes(1, validation.decodedUuid()); - statement.setBytes(2, validation.hash); - statement.setBoolean(3, validation.valid()); - statement.executeUpdate(); - } - } - } - - private boolean contains(final Connection con, final Validation validation) throws SQLException { - try (final var statement = con.prepareStatement("SELECT 1 FROM muuid_validation WHERE uuid = ? AND hash = ?")) { - statement.setBytes(1, Base64.getDecoder().decode(validation.uuid)); - statement.setBytes(2, validation.hash); - try (final var result = statement.executeQuery()) { - return result.next(); - } - } - } - - private void notifyChangeForOnlinePlayer(final MUUID muuid, final PlayerValidatorEvent.Type type) { - Groups.player.each( - player -> player.uuid().equals(muuid.getUuid()) && player.usid().equals(muuid.getUsid()), - player -> DistributorProvider.get().getEventBus().post(new PlayerValidatorEvent(player, type))); - } - - private void notifyChangeForOnlinePlayer(final String uuid, final PlayerValidatorEvent.Type type) { - Groups.player.each( - player -> player.uuid().equals(uuid), - player -> DistributorProvider.get().getEventBus().post(new PlayerValidatorEvent(player, type))); - } - - private void notifyChangeForAllOnlinePlayers(final PlayerValidatorEvent.Type type) { - Groups.player.each( - player -> DistributorProvider.get().getEventBus().post(new PlayerValidatorEvent(player, type))); - } - - public record Validation(String uuid, byte[] hash, boolean valid) { - - public Validation(final MUUID muuid, final boolean valid) { - this(muuid.getUuid(), SQLPlayerValidator.hash(muuid), valid); - } - - public byte[] decodedUuid() { - return Base64.getDecoder().decode(this.uuid); - } - } -} diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/security/permission/AbstractPermissible.java b/distributor-core/src/main/java/fr/xpdustry/distributor/core/security/permission/AbstractPermissible.java deleted file mode 100644 index 26c07b54..00000000 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/security/permission/AbstractPermissible.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.security.permission; - -import fr.xpdustry.distributor.api.security.permission.Permissible; -import fr.xpdustry.distributor.api.util.Tristate; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import org.checkerframework.checker.nullness.qual.Nullable; - -public abstract class AbstractPermissible implements Permissible { - - private final Set parents = new HashSet<>(); - private final PermissionTree tree = new PermissionTree(); - - @Override - public Collection getParentGroups() { - return Collections.unmodifiableCollection(this.parents); - } - - @Override - public void setParentGroups(final Collection parents) { - this.parents.clear(); - this.parents.addAll(parents); - } - - @Override - public void addParentGroup(final String group) { - this.parents.add(group); - } - - @Override - public void removeParentGroup(final String group) { - this.parents.remove(group); - } - - @Override - public Tristate getPermission(final String permission) { - return this.tree.getPermission(permission); - } - - @Override - public void setPermission(final String permission, final Tristate state) { - this.tree.setPermission(permission, state); - } - - @Override - public Map getPermissions() { - return this.tree.getPermissions(); - } - - @Override - public void setPermissions(final Map permissions) { - permissions.forEach((permission, state) -> this.setPermission(permission, Tristate.of(state))); - } - - @Override - public boolean equals(final @Nullable Object o) { - if (this == o) { - return true; - } - if (!(o instanceof final AbstractPermissible that)) { - return false; - } - - if (!this.parents.equals(that.parents)) { - return false; - } - return this.tree.equals(that.tree); - } - - @Override - public int hashCode() { - int result = this.parents.hashCode(); - result = 31 * result + this.tree.hashCode(); - return result; - } -} diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/security/permission/AbstractSQLPermissibleManager.java b/distributor-core/src/main/java/fr/xpdustry/distributor/core/security/permission/AbstractSQLPermissibleManager.java deleted file mode 100644 index 233cc454..00000000 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/security/permission/AbstractSQLPermissibleManager.java +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.security.permission; - -import fr.xpdustry.distributor.api.security.permission.Permissible; -import fr.xpdustry.distributor.api.security.permission.PermissibleManager; -import fr.xpdustry.distributor.api.util.Tristate; -import fr.xpdustry.distributor.core.database.ConnectionFactory; -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Optional; -import java.util.function.Function; - -public abstract class AbstractSQLPermissibleManager

implements PermissibleManager

{ - - private final ConnectionFactory factory; - private final Function statementProcessor; - - public AbstractSQLPermissibleManager( - final ConnectionFactory factory, final String category, final String primaryKey) { - this.factory = factory; - this.statementProcessor = this.factory.getStatementProcessor().andThen(s -> s.replace("{pm}", category) - .replace("{pk}", primaryKey)); - } - - @Override - public void save(final P permissible) { - this.factory.withConsumer(con -> { - if (this.exists(permissible)) { - this.update(con, permissible); - } else { - this.insert(con, permissible); - } - this.updateParentsAndPermissions(con, permissible); - }); - } - - protected abstract void update(final Connection con, final P permissible) throws SQLException; - - protected abstract void insert(final Connection con, final P permissible) throws SQLException; - - @Override - public P findOrCreateById(final String id) { - return this.findById(id).orElseGet(() -> this.createPermissible(id)); - } - - @Override - public Optional

findById(final String id) { - return this.factory.withFunction(con -> { - try (final var statement = - con.prepareStatement(this.processStatement("SELECT * FROM '{prefix}{pm}' WHERE {pk} = ?"))) { - statement.setString(1, id); - try (final var result = statement.executeQuery()) { - if (result.next()) { - final var permissible = this.createPermissible(result); - this.selectParentAndPermissions(con, permissible); - return Optional.of(permissible); - } - } - } - return Optional.empty(); - }); - } - - @Override - public Iterable

findAll() { - return this.factory.withFunction(con -> { - final var list = new ArrayList

(); - try (final var statement = con.prepareStatement(this.processStatement("SELECT * FROM '{prefix}{pm}'"))) { - try (final var result = statement.executeQuery()) { - while (result.next()) { - final var permissible = this.createPermissible(result); - this.selectParentAndPermissions(con, permissible); - list.add(permissible); - } - } - } - return list; - }); - } - - @Override - public boolean exists(final P permissible) { - return this.factory.withFunction(con -> { - try (final var statement = - con.prepareStatement(this.processStatement("SELECT 1 FROM '{prefix}{pm}' WHERE {pk} = ?"))) { - statement.setString(1, this.getIdentifier(permissible)); - try (final var result = statement.executeQuery()) { - return result.next(); - } - } - }); - } - - @Override - public long count() { - return this.factory.withFunction(con -> { - try (final var statement = - con.prepareStatement(this.processStatement("SELECT COUNT(*) FROM '{prefix}{pm}'"))) { - try (final var result = statement.executeQuery()) { - if (result.next()) { - return result.getLong(1); - } - } - } - throw new SQLException("Could not count permissibles"); - }); - } - - @Override - public void deleteById(final String id) { - this.factory.withConsumer(con -> { - try (final var statement = - con.prepareStatement(this.processStatement("DELETE FROM '{prefix}{pm}' WHERE {pk} = ?"))) { - statement.setString(1, id); - statement.executeUpdate(); - } - }); - } - - @Override - public void delete(final P permissible) { - this.deleteById(this.getIdentifier(permissible)); - } - - @Override - public void deleteAll() { - this.factory.withConsumer(con -> { - try (final var statement = - con.prepareStatement(this.factory.getStatementProcessor().apply("DELETE FROM '{prefix}{pm}'"))) { - statement.executeUpdate(); - } - }); - } - - protected abstract String getIdentifier(final P permissible); - - protected abstract P createPermissible(final String identifier); - - protected abstract P createPermissible(final ResultSet result) throws SQLException; - - protected String processStatement(final String statement) { - return this.statementProcessor.apply(statement); - } - - private void updateParentsAndPermissions(final Connection con, final P permissible) throws SQLException { - try (final var statement = con.prepareStatement( - this.processStatement("DELETE FROM '{prefix}{pm}_parent_group' WHERE {pm}_{pk} = ?"))) { - statement.setString(1, this.getIdentifier(permissible)); - statement.executeUpdate(); - } - if (!permissible.getParentGroups().isEmpty()) { - try (final var statement = con.prepareStatement(this.processStatement( - "INSERT INTO '{prefix}{pm}_parent_group' ('{pm}_{pk}', 'parent_group') VALUES (?, ?)"))) { - for (final var parent : permissible.getParentGroups()) { - statement.setString(1, this.getIdentifier(permissible)); - statement.setString(2, parent); - statement.addBatch(); - statement.clearParameters(); - } - statement.executeBatch(); - } - } - try (final var statement = con.prepareStatement( - this.processStatement("DELETE FROM '{prefix}{pm}_permission' WHERE '{pm}_{pk}' = ?"))) { - statement.setString(1, this.getIdentifier(permissible)); - statement.executeUpdate(); - } - if (!permissible.getPermissions().isEmpty()) { - try (final var statement = con.prepareStatement(this.processStatement( - "INSERT INTO '{prefix}{pm}_permission' ('{pm}_{pk}', 'permission', 'value') VALUES (?, ?, ?)"))) { - for (final var permission : permissible.getPermissions().entrySet()) { - statement.setString(1, this.getIdentifier(permissible)); - statement.setString(2, permission.getKey()); - statement.setBoolean(3, permission.getValue()); - statement.addBatch(); - statement.clearParameters(); - } - statement.executeBatch(); - } - } - } - - private void selectParentAndPermissions(final Connection con, final P permissible) throws SQLException { - try (final var statement = con.prepareStatement( - this.processStatement("SELECT 'parent_group' FROM '{prefix}{pm}_parent_group' WHERE {pm}_{pk} = ?"))) { - statement.setString(1, this.getIdentifier(permissible)); - try (final var result = statement.executeQuery()) { - while (result.next()) { - permissible.addParentGroup(result.getString(1)); - } - } - } - try (final var statement = con.prepareStatement(this.processStatement( - "SELECT 'permission', 'value' FROM '{prefix}{pm}_permission' WHERE {pm}_{pk} = ?"))) { - statement.setString(1, this.getIdentifier(permissible)); - try (final var result = statement.executeQuery()) { - while (result.next()) { - permissible.setPermission(result.getString(1), Tristate.of(result.getBoolean(2))); - } - } - } - } -} diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/security/permission/Permissibles.java b/distributor-core/src/main/java/fr/xpdustry/distributor/core/security/permission/Permissibles.java deleted file mode 100644 index 61758db5..00000000 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/security/permission/Permissibles.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.security.permission; - -import fr.xpdustry.distributor.core.database.ConnectionFactory; -import java.io.IOException; - -public final class Permissibles { - - private Permissibles() {} - - public static void createDatabase(final ConnectionFactory factory) { - try (final var input = - Permissibles.class.getResourceAsStream("/fr/xpdustry/distributor/assets/schemas/permission.sql")) { - if (input == null) { - throw new IllegalStateException("Missing schema file."); - } - factory.executeScript(input); - } catch (final IOException e) { - throw new RuntimeException(e); - } - } -} diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/security/permission/PermissionTree.java b/distributor-core/src/main/java/fr/xpdustry/distributor/core/security/permission/PermissionTree.java deleted file mode 100644 index e6a2ffc4..00000000 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/security/permission/PermissionTree.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.security.permission; - -import fr.xpdustry.distributor.api.security.permission.Permissible; -import fr.xpdustry.distributor.api.util.Tristate; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import org.checkerframework.checker.nullness.qual.Nullable; - -public final class PermissionTree { - - private final @Nullable PermissionTree parent; - private final Map children = new HashMap<>(); - private Tristate value = Tristate.UNDEFINED; - - public PermissionTree() { - this.parent = null; - } - - private PermissionTree(final @Nullable PermissionTree parent) { - this.parent = parent; - } - - public Tristate getPermission(final String permission) { - if (!Permissible.PERMISSION_PATTERN.matcher(permission).matches()) { - throw new IllegalArgumentException("The permission doesn't match the regex: " + permission); - } - var state = Tristate.UNDEFINED; - var node = this; - for (final var part : permission.split("\\.", -1)) { - if (node.children.containsKey("*") && node.children.get("*").value != Tristate.UNDEFINED) { - state = node.children.get("*").value; - } - node = node.children.get(part); - if (node == null) { - return state; - } else if (node.value != Tristate.UNDEFINED) { - state = node.value; - } - } - return state; - } - - public void setPermission(final String permission, final Tristate state) { - if (!Permissible.PERMISSION_PATTERN.matcher(permission).matches()) { - throw new IllegalArgumentException("The permission doesn't match the regex: " + permission); - } - final var parts = permission.split("\\.", -1); - var node = this; - if (state != Tristate.UNDEFINED) { - for (final var part : parts) { - final var parent = node; - node = node.children.computeIfAbsent(part, k -> new PermissionTree(parent)); - } - node.value = state; - } else { - for (final var part : parts) { - node = node.children.get(part); - if (node == null) { - return; - } - } - node.value = state; - var index = parts.length - 1; - while (node.parent != null && node.children.isEmpty()) { - node = node.parent; - node.children.remove(parts[index--]); - } - } - } - - public Map getPermissions() { - final Map permissions = new HashMap<>(); - for (final var child : this.children.entrySet()) { - if (child.getValue().value != Tristate.UNDEFINED) { - permissions.put(child.getKey(), child.getValue().value.asBoolean()); - } - for (final var entry : child.getValue().getPermissions().entrySet()) { - permissions.put(child.getKey() + "." + entry.getKey(), entry.getValue()); - } - } - return Collections.unmodifiableMap(permissions); - } - - @Override - public boolean equals(final @Nullable Object o) { - if (this == o) { - return true; - } - if (!(o instanceof final PermissionTree that)) { - return false; - } - if (!this.children.equals(that.children)) { - return false; - } - return this.value == that.value; - } - - @Override - public int hashCode() { - int result = this.children.hashCode(); - result = 31 * result + this.value.hashCode(); - return result; - } -} diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/security/permission/SQLGroupPermissibleManager.java b/distributor-core/src/main/java/fr/xpdustry/distributor/core/security/permission/SQLGroupPermissibleManager.java deleted file mode 100644 index 488d966a..00000000 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/security/permission/SQLGroupPermissibleManager.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.security.permission; - -import fr.xpdustry.distributor.api.security.permission.GroupPermissible; -import fr.xpdustry.distributor.core.database.ConnectionFactory; -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.SQLException; - -public final class SQLGroupPermissibleManager extends AbstractSQLPermissibleManager { - - public SQLGroupPermissibleManager(final ConnectionFactory factory) { - super(factory, "group", "name"); - } - - @Override - protected void update(final Connection con, final GroupPermissible permissible) throws SQLException { - try (final var statement = con.prepareStatement( - this.processStatement("UPDATE '{prefix}group' SET 'weight' = ? WHERE 'name' = ?"))) { - statement.setInt(1, permissible.getWeight()); - statement.setString(2, permissible.getName()); - statement.executeUpdate(); - } - } - - @Override - protected void insert(final Connection con, final GroupPermissible permissible) throws SQLException { - try (final var statement = con.prepareStatement( - this.processStatement("INSERT INTO '{prefix}group' ('name', 'weight') VALUES (?, ?)"))) { - statement.setString(1, permissible.getName()); - statement.setInt(2, permissible.getWeight()); - statement.executeUpdate(); - } - } - - @Override - protected String getIdentifier(final GroupPermissible permissible) { - return permissible.getName(); - } - - @Override - protected GroupPermissible createPermissible(final String identifier) { - return new SimpleGroupPermissible(identifier); - } - - @Override - protected GroupPermissible createPermissible(final ResultSet result) throws SQLException { - final var group = new SimpleGroupPermissible(result.getString("name")); - group.setWeight(result.getInt("weight")); - return group; - } -} diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/security/permission/SQLPermissionService.java b/distributor-core/src/main/java/fr/xpdustry/distributor/core/security/permission/SQLPermissionService.java deleted file mode 100644 index a8b7bedb..00000000 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/security/permission/SQLPermissionService.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.security.permission; - -import fr.xpdustry.distributor.api.security.PlayerValidator; -import fr.xpdustry.distributor.api.security.permission.GroupPermissible; -import fr.xpdustry.distributor.api.security.permission.Permissible; -import fr.xpdustry.distributor.api.security.permission.PermissibleManager; -import fr.xpdustry.distributor.api.security.permission.PermissionService; -import fr.xpdustry.distributor.api.security.permission.PlayerPermissible; -import fr.xpdustry.distributor.api.util.MUUID; -import fr.xpdustry.distributor.api.util.Tristate; -import fr.xpdustry.distributor.core.DistributorConfiguration; -import fr.xpdustry.distributor.core.database.ConnectionFactory; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Optional; -import java.util.Set; -import mindustry.Vars; -import org.checkerframework.checker.nullness.qual.Nullable; - -public final class SQLPermissionService implements PermissionService { - - private static final Comparator GROUP_COMPARATOR = - Comparator.comparing(GroupPermissible::getWeight).reversed(); - - private final DistributorConfiguration configuration; - private final PlayerValidator validator; - private final SQLPlayerPermissibleManager players; - private final SQLGroupPermissibleManager groups; - - public SQLPermissionService( - final DistributorConfiguration configuration, - final ConnectionFactory connectionFactory, - final PlayerValidator validator) { - Permissibles.createDatabase(connectionFactory); - this.configuration = configuration; - this.validator = validator; - this.players = new SQLPlayerPermissibleManager(connectionFactory); - this.groups = new SQLGroupPermissibleManager(connectionFactory); - } - - @Override - public Tristate getPlayerPermission(final MUUID muuid, final String permission) { - if (!this.validator.isValid(muuid)) { - return Tristate.FALSE; - } - - if (!this.configuration.isAdminIgnored() && Vars.netServer.admins.isAdmin(muuid.getUuid(), muuid.getUsid())) { - return Tristate.TRUE; - } - - return this.getPlayerPermission(muuid.getUuid(), permission); - } - - @Override - public Tristate getPlayerPermission(final String uuid, final String permission) { - final var player = this.players.findById(uuid); - final var query = permission.toLowerCase(Locale.ROOT); - var state = Tristate.UNDEFINED; - - if (!Permissible.PERMISSION_PATTERN.matcher(query).matches()) { - return Tristate.UNDEFINED; - } - - final Permissible permissible; - final List parents; - - if (player.isPresent()) { - permissible = player.get(); - parents = this.getParents(player.get(), this.configuration.getPermissionPrimaryGroup()); - } else { - final var primary = this.groups.findById(this.configuration.getPermissionPrimaryGroup()); - if (primary.isPresent()) { - permissible = primary.get(); - parents = this.getParents(primary.get(), null); - } else { - return state; - } - } - - state = permissible.getPermission(query); - if (state != Tristate.UNDEFINED) { - return state; - } - - for (final var parent : parents) { - state = parent.getPermission(query); - if (state != Tristate.UNDEFINED) { - break; - } - } - - return state; - } - - @Override - public Tristate getGroupPermission(final String group, final String permission) { - final var permissible = this.groups.findById(group); - final var query = permission.toLowerCase(Locale.ROOT); - - if (!Permissible.PERMISSION_PATTERN.matcher(query).matches()) { - return Tristate.UNDEFINED; - } - - var state = Tristate.UNDEFINED; - - if (permissible.isPresent()) { - state = permissible.get().getPermission(query); - } else { - return state; - } - - if (state != Tristate.UNDEFINED) { - return state; - } - - final var parents = this.getParents(permissible.get(), null); - for (final var parent : parents) { - state = parent.getPermission(query); - if (state != Tristate.UNDEFINED) { - break; - } - } - - return state; - } - - @Override - public PermissibleManager getPlayerPermissionManager() { - return this.players; - } - - @Override - public PermissibleManager getGroupPermissionManager() { - return this.groups; - } - - private List getParents(final Permissible permissible, final @Nullable String primary) { - final Set visited = new HashSet<>(permissible.getParentGroups()); - final List parents = new ArrayList<>(permissible.getParentGroups().stream() - .map(this.groups::findById) - .filter(Optional::isPresent) - .map(Optional::get) - .toList()); - - for (int i = 0; i < parents.size(); i++) { - final var parent = parents.get(i); - parent.getParentGroups().stream() - .filter(visited::add) - .map(this.groups::findById) - .filter(Optional::isPresent) - .map(Optional::get) - .forEach(parents::add); - - if (primary != null && parents.size() - 1 == i && !visited.add(primary)) { - final var primaryGroup = this.groups.findById(primary); - primaryGroup.ifPresent(parents::add); - } - } - - parents.sort(GROUP_COMPARATOR); - - return parents; - } -} diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/security/permission/SQLPlayerPermissibleManager.java b/distributor-core/src/main/java/fr/xpdustry/distributor/core/security/permission/SQLPlayerPermissibleManager.java deleted file mode 100644 index d918427d..00000000 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/security/permission/SQLPlayerPermissibleManager.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.security.permission; - -import fr.xpdustry.distributor.api.security.permission.PlayerPermissible; -import fr.xpdustry.distributor.core.database.ConnectionFactory; -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.SQLException; - -public final class SQLPlayerPermissibleManager extends AbstractSQLPermissibleManager { - - public SQLPlayerPermissibleManager(final ConnectionFactory factory) { - super(factory, "player", "uuid"); - } - - @Override - protected void update(final Connection con, final PlayerPermissible permissible) {} - - @Override - protected void insert(final Connection con, final PlayerPermissible permissible) throws SQLException { - try (final var statement = - con.prepareStatement(this.processStatement("INSERT INTO '{prefix}player' ('uuid') VALUES (?)"))) { - statement.setString(1, permissible.getUuid()); - statement.executeUpdate(); - } - } - - @Override - protected String getIdentifier(final PlayerPermissible permissible) { - return permissible.getUuid(); - } - - @Override - protected PlayerPermissible createPermissible(final String identifier) { - return new SimplePlayerPermissible(identifier); - } - - @Override - protected PlayerPermissible createPermissible(final ResultSet result) throws SQLException { - return new SimplePlayerPermissible(result.getString("uuid")); - } -} diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/security/permission/SimpleGroupPermissible.java b/distributor-core/src/main/java/fr/xpdustry/distributor/core/security/permission/SimpleGroupPermissible.java deleted file mode 100644 index c1a2f61a..00000000 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/security/permission/SimpleGroupPermissible.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.security.permission; - -import fr.xpdustry.distributor.api.security.permission.GroupPermissible; -import org.checkerframework.checker.nullness.qual.Nullable; - -public final class SimpleGroupPermissible extends AbstractPermissible implements GroupPermissible { - - private final String name; - private int weight = 0; - - public SimpleGroupPermissible(final String name) { - this.name = name; - } - - @Override - public String getName() { - return this.name; - } - - @Override - public int getWeight() { - return this.weight; - } - - @Override - public void setWeight(final int weight) { - this.weight = weight; - } - - @Override - public boolean equals(final @Nullable Object o) { - if (this == o) { - return true; - } - if (o == null || this.getClass() != o.getClass()) { - return false; - } - if (!super.equals(o)) { - return false; - } - - final SimpleGroupPermissible that = (SimpleGroupPermissible) o; - - if (this.weight != that.weight) { - return false; - } - return this.name.equals(that.name); - } - - @Override - public int hashCode() { - int result = super.hashCode(); - result = 31 * result + this.name.hashCode(); - result = 31 * result + this.weight; - return result; - } -} diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/security/permission/SimplePlayerPermissible.java b/distributor-core/src/main/java/fr/xpdustry/distributor/core/security/permission/SimplePlayerPermissible.java deleted file mode 100644 index da76e467..00000000 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/security/permission/SimplePlayerPermissible.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.security.permission; - -import fr.xpdustry.distributor.api.security.permission.PlayerPermissible; -import mindustry.Vars; -import org.checkerframework.checker.nullness.qual.Nullable; - -public final class SimplePlayerPermissible extends AbstractPermissible implements PlayerPermissible { - - private final String uuid; - - public SimplePlayerPermissible(final String uuid) { - this.uuid = uuid; - } - - @Override - public String getName() { - if (Vars.netServer != null) { - final var info = Vars.netServer.admins.getInfoOptional(this.uuid); - return info == null ? "unknown" : info.lastName; - } - return "unknown"; - } - - @Override - public String getUuid() { - return this.uuid; - } - - @Override - public boolean equals(final @Nullable Object o) { - if (this == o) { - return true; - } - if (o == null || this.getClass() != o.getClass()) { - return false; - } - if (!super.equals(o)) { - return false; - } - - final SimplePlayerPermissible that = (SimplePlayerPermissible) o; - - return this.uuid.equals(that.uuid); - } - - @Override - public int hashCode() { - int result = super.hashCode(); - result = 31 * result + this.uuid.hashCode(); - return result; - } -} diff --git a/distributor-core/src/main/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider b/distributor-core/src/main/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider deleted file mode 100644 index 3d1ac977..00000000 --- a/distributor-core/src/main/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider +++ /dev/null @@ -1 +0,0 @@ -fr.xpdustry.distributor.core.logging.ArcServiceProvider \ No newline at end of file diff --git a/distributor-core/src/main/resources/fr/xpdustry/distributor/assets/banner.txt b/distributor-core/src/main/resources/fr/xpdustry/distributor/assets/banner.txt deleted file mode 100644 index 3144a2da..00000000 --- a/distributor-core/src/main/resources/fr/xpdustry/distributor/assets/banner.txt +++ /dev/null @@ -1,4 +0,0 @@ - ___ _ _ _ _ _ -| \ (_) ___| |_ _ _ (_)| |__ _ _ | |_ ___ _ _ -| |) || |(_-<| _|| '_|| || '_ \| || || _|/ _ \| '_| -|___/ |_|/__/ \__||_| |_||_.__/ \_,_| \__|\___/|_| \ No newline at end of file diff --git a/distributor-core/src/main/resources/fr/xpdustry/distributor/assets/bundles/bundle_en.properties b/distributor-core/src/main/resources/fr/xpdustry/distributor/assets/bundles/bundle_en.properties deleted file mode 100644 index 29130333..00000000 --- a/distributor-core/src/main/resources/fr/xpdustry/distributor/assets/bundles/bundle_en.properties +++ /dev/null @@ -1,57 +0,0 @@ -argument.parse.failure.no_input_was_provided=No input was provided. -argument.parse.failure.boolean=Could not parse boolean from ''{0}''. -argument.parse.failure.number=''{0}'' is not a valid number in the range {1} to {2}. -argument.parse.failure.char=''{0}'' is not a valid character. -argument.parse.failure.string=''{0}'' is not a valid string of type {1}. -argument.parse.failure.uuid=''{0}'' is not a valid UUID. -argument.parse.failure.regex=''{0}'' does not match the regex ''{1}''. -argument.parse.failure.enum=''{0}'' is not one of the following: {1}. -argument.parse.failure.color=''{0}'' is not a valid color. -argument.parse.failure.team=Failed to find the team ''{0}'' of type ''{1}''. -argument.parse.failure.duration=''{0}'' is not a valid duration. - -argument.parse.failure.player.not_found=The player ''{0}'' does not exist. -argument.parse.failure.player.too_many=Too many players found, be more specific. -argument.parse.failure.permissible=''{0}'' not found. - -argument.parse.failure.flag.duplicate_flag=Duplicate flag ''{0}''. -argument.parse.failure.flag.no_flag_started=No flag started. Don''t know what to do with ''{0}''. -argument.parse.failure.flag.missing_argument=Missing argument for ''{0}''. -argument.parse.failure.flag.no_permission=You don''t have permission to use ''{0}''. -argument.parse.failure.flag.unknown=Unknown flag ''{0}''. - -command.invalid.syntax=Invalid input, the correct syntax is ''{0}''. -command.invalid.permission=You do not have the required permissions to perform this action. -command.failure.no_such_command=The command ''{0}'' does not exists. -command.failure.execution=An unexpected problem occurred while executing the command. - -permission.permissible.permission.list.none=''{0}'' have no set permissions. -permission.permissible.permission.set.already=The permission ''{0}'' of ''{1}'' is already set to ''{2}''. -permission.permissible.permission.set.success=The permission ''{0}'' of ''{1}'' has been set to ''{2}''. -permission.permissible.parent.list.none=''{0}'' has no parent groups. -permission.permissible.parent.add.already=''{0}'' is already in the group ''{1}''. -permission.permissible.parent.add.success=''{0}'' has been added to the group ''{1}''. -permission.permissible.parent.remove.already=''{0}'' is not in the group ''{1}''. -permission.permissible.parent.remove.success=''{0}'' has been removed from the group ''{1}''. -permission.permissible.delete.already=There is no permission data attached to ''{0}''. -permission.permissible.delete.success=All permission data of ''{0}'' has been deleted. - -permission.group.create.already=The group ''{0}'' already exists. -permission.group.create.success=The group ''{0}'' has been created. -permission.group.weight.get=The weight of ''{0}'' is {1}. -permission.group.weight.set.already=The weight of ''{0}'' is already set to {1}. -permission.group.weight.set.success=The weight of ''{0}'' has been set to ''{1}''. -permission.group.list.none=There are no groups. -permission.group.list.success=List of the available groups: {0} - -distributor.identity.validate=Validated player. -distributor.identity.invalidate=Invalidated player. -distributor.identity.validity.valid=The player is valid. -distributor.identity.validity.invalid=The player is invalid. -distributor.identity.clear.warning=\ - This command will clear all validation data in this server, \ - run the command again with the flag ''--confirm'' to go forward. -distributor.identity.clear.success=All validation data has been cleared. -distributor.identity.player.failure=\ - [red]Warning, your identity couldn't be validated, you will not be able to use most features of this server. \ - Please contact an administrator. diff --git a/distributor-core/src/main/resources/fr/xpdustry/distributor/assets/bundles/bundle_fr.properties b/distributor-core/src/main/resources/fr/xpdustry/distributor/assets/bundles/bundle_fr.properties deleted file mode 100644 index 5fd8b77f..00000000 --- a/distributor-core/src/main/resources/fr/xpdustry/distributor/assets/bundles/bundle_fr.properties +++ /dev/null @@ -1,26 +0,0 @@ -argument.parse.failure.no_input_was_provided=Aucune entrée n''a été introduite. -argument.parse.failure.boolean=Impossible de convertir ''{0}'' en booléen. -argument.parse.failure.number=''{0}'' n''est pas un nombre valide entre {1} et {2}. -argument.parse.failure.char=''{0}'' n''est pas un caractère valide. -argument.parse.failure.string=''{0}'' n''est pas un texte valide de type {1}. -argument.parse.failure.uuid=''{0}'' n''est pas un UUID valide. -argument.parse.failure.regex=''{0}'' ne correspond pas au regex ''{1}''. -argument.parse.failure.enum=''{0}'' n''est pas l''une des valeurs suivantes : {1}. -argument.parse.failure.color=''{0}'' n''est pas une couleur valide. -argument.parse.failure.team=Impossible de trouver l''équipe ''{0}'' de type ''{1}''. -argument.parse.failure.duration=''{0}'' n''est pas une durée valide. - -argument.parse.failure.player.not_found=Le joueur ''{0}'' n''existe pas. -argument.parse.failure.player.too_many=Plusieurs joueurs ont été trouvés, soyez plus spécifique. -argument.parse.failure.permissible=''{0}'' introuvable. - -argument.parse.failure.flag.duplicate_flag=Drapeau en trop ''{0}''. -argument.parse.failure.flag.no_flag_started=Aucun drapeau n''a été fourni. Impossible d''utiliser ''{0}''. -argument.parse.failure.flag.missing_argument=Argument manquant pout le drapeau ''{0}''. -argument.parse.failure.flag.no_permission=Vous n''avez pas la permission d''utiliser le drapeau ''{0}''. -argument.parse.failure.flag.unknown=Drapeau inconnu ''{0}''. - -command.invalid.syntax=Entrée invalide, la syntaxe correcte est ''{0}''. -command.invalid.permission=Vous n''avez pas les permissions requises pour effectuer cette action. -command.failure.no_such_command=La commande ''{0}'' n''existe pas. -command.failure.execution=Un problème inattendu est survenu lors de l''exécution de la commande : ''{0}'' diff --git a/distributor-core/src/main/resources/fr/xpdustry/distributor/assets/schemas/permission.sql b/distributor-core/src/main/resources/fr/xpdustry/distributor/assets/schemas/permission.sql deleted file mode 100644 index 30f5f364..00000000 --- a/distributor-core/src/main/resources/fr/xpdustry/distributor/assets/schemas/permission.sql +++ /dev/null @@ -1,40 +0,0 @@ -CREATE TABLE IF NOT EXISTS '{prefix}player' ( - 'uuid' VARCHAR(24) NOT NULL, - PRIMARY KEY ('uuid') -); - -CREATE TABLE IF NOT EXISTS '{prefix}player_parent_group' ( - 'player_uuid' VARCHAR(24) NOT NULL, - 'parent_group' VARCHAR(63) NOT NULL, - FOREIGN KEY ('player_uuid') REFERENCES '{prefix}player' ('uuid'), - PRIMARY KEY ('player_uuid', 'parent_group') -); - -CREATE TABLE IF NOT EXISTS '{prefix}player_permission' ( - 'player_uuid' VARCHAR(24) NOT NULL, - 'permission' VARCHAR(255) NOT NULL, - 'value' BOOLEAN NOT NULL, - FOREIGN KEY ('player_uuid') REFERENCES '{prefix}player' ('uuid'), - PRIMARY KEY ('player_uuid', 'permission') -); - -CREATE TABLE IF NOT EXISTS '{prefix}group' ( - 'name' VARCHAR(63) NOT NULL, - 'weight' INT NOT NULL, - PRIMARY KEY ('name') -); - -CREATE TABLE IF NOT EXISTS '{prefix}group_parent_group' ( - 'group_name' VARCHAR(63) NOT NULL, - 'parent_group' VARCHAR(63) NOT NULL, - FOREIGN KEY ('group_name') REFERENCES '{prefix}group' ('name'), - PRIMARY KEY ('group_name', 'parent_group') -); - -CREATE TABLE IF NOT EXISTS '{prefix}group_permission' ( - 'group_name' VARCHAR(63) NOT NULL, - 'permission' VARCHAR(255) NOT NULL, - 'value' BOOLEAN NOT NULL, - FOREIGN KEY ('group_name') REFERENCES '{prefix}group' ('name'), - PRIMARY KEY ('group_name', 'permission') -); diff --git a/distributor-core/src/test/java/fr/xpdustry/distributor/core/event/SimpleEventBusTest.java b/distributor-core/src/test/java/fr/xpdustry/distributor/core/event/SimpleEventBusTest.java deleted file mode 100644 index bf3ffe0a..00000000 --- a/distributor-core/src/test/java/fr/xpdustry/distributor/core/event/SimpleEventBusTest.java +++ /dev/null @@ -1,287 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.event; - -import arc.Events; -import fr.xpdustry.distributor.api.event.EventHandler; -import fr.xpdustry.distributor.api.plugin.MindustryPlugin; -import fr.xpdustry.distributor.api.util.Priority; -import java.util.function.Consumer; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; - -import static org.assertj.core.api.Assertions.assertThat; - -public final class SimpleEventBusTest { - - @Mock - private MindustryPlugin plugin; - - private SimpleEventBus bus; - - @BeforeEach - void setup() { - this.bus = new SimpleEventBus(); - } - - @AfterEach - void clean() { - Events.clear(); - } - - @Test - void test_class_subscribe() { - final var subscriber = new ClassEventSubscriber(); - this.bus.subscribe(TestEvent1.class, this.plugin, subscriber); - this.bus.post(new TestEvent1()); - assertThat(subscriber.hasBeenTriggered()).isTrue(); - } - - @Test - void test_enum_subscribe() { - final var subscriber = new EnumEventSubscriber(); - this.bus.subscribe(TestEnum.VALUE, this.plugin, subscriber); - this.bus.post(TestEnum.VALUE); - assertThat(subscriber.hasBeenTriggered()).isTrue(); - } - - @Test - void test_arc_event_fire() { - final var subscriber1 = new ClassEventSubscriber(); - final var subscriber2 = new EnumEventSubscriber(); - - Events.on(TestEvent1.class, subscriber1::accept); - Events.run(TestEnum.VALUE, subscriber2); - Events.fire(new TestEvent1()); - Events.fire(TestEnum.VALUE); - - assertThat(subscriber1.hasBeenTriggered()).isTrue(); - assertThat(subscriber2.hasBeenTriggered()).isTrue(); - } - - @Test - void test_listener_unregister() { - final var subscriber1 = new ClassEventSubscriber(); - final var subscriber2 = new EnumEventSubscriber(); - - final var subscription1 = this.bus.subscribe(TestEvent1.class, this.plugin, subscriber1); - final var subscription2 = this.bus.subscribe(TestEnum.VALUE, this.plugin, subscriber2); - - this.bus.post(new TestEvent1()); - this.bus.post(TestEnum.VALUE); - assertThat(subscriber1.hasBeenTriggered()).isTrue(); - assertThat(subscriber2.hasBeenTriggered()).isTrue(); - - subscription1.unsubscribe(); - subscription2.unsubscribe(); - subscriber1.reset(); - subscriber2.reset(); - - this.bus.post(new TestEvent1()); - this.bus.post(TestEnum.VALUE); - assertThat(subscriber1.hasBeenTriggered()).isFalse(); - assertThat(subscriber2.hasBeenTriggered()).isFalse(); - - assertThat(this.bus.events.isEmpty()).isTrue(); - } - - @Test - void test_class_event_order() { - final var subscriber1 = new ClassEventSubscriber(); - final var subscriber2 = new ClassEventSubscriber(); - final var subscriber3 = new ClassEventSubscriber(); - - this.bus.subscribe(TestEvent1.class, Priority.HIGH, this.plugin, subscriber1); - Events.on(TestEvent1.class, subscriber2::accept); - this.bus.subscribe(TestEvent1.class, Priority.LOW, this.plugin, subscriber3); - - this.bus.post(new TestEvent1()); - - assertThat(subscriber1.hasBeenTriggered()).isTrue(); - assertThat(subscriber2.hasBeenTriggered()).isTrue(); - assertThat(subscriber3.hasBeenTriggered()).isTrue(); - - assertThat(subscriber1.triggerTime).isLessThan(subscriber2.triggerTime); - assertThat(subscriber2.triggerTime).isLessThan(subscriber3.triggerTime); - } - - @Test - void test_enum_event_order() { - final var subscriber1 = new EnumEventSubscriber(); - final var subscriber2 = new EnumEventSubscriber(); - final var subscriber3 = new EnumEventSubscriber(); - - this.bus.subscribe(TestEnum.VALUE, Priority.HIGH, this.plugin, subscriber1); - Events.run(TestEnum.VALUE, subscriber2); - this.bus.subscribe(TestEnum.VALUE, Priority.LOW, this.plugin, subscriber3); - - this.bus.post(TestEnum.VALUE); - - assertThat(subscriber1.hasBeenTriggered()).isTrue(); - assertThat(subscriber2.hasBeenTriggered()).isTrue(); - assertThat(subscriber3.hasBeenTriggered()).isTrue(); - - assertThat(subscriber1.triggerTime).isLessThan(subscriber2.triggerTime); - assertThat(subscriber2.triggerTime).isLessThan(subscriber3.triggerTime); - } - - @Test - void test_annotated_subscriber() { - final var listener = new AnnotatedEventListener(); - final var subscription = this.bus.parse(this.plugin, listener); - - assertThat(listener.hasBeenTriggered1()).isFalse(); - assertThat(listener.hasBeenTriggered2()).isFalse(); - - this.bus.post(new TestEvent1()); - - assertThat(listener.hasBeenTriggered1()).isTrue(); - assertThat(listener.hasBeenTriggered2()).isFalse(); - - this.bus.post(new TestEvent2()); - - assertThat(listener.hasBeenTriggered1()).isTrue(); - assertThat(listener.hasBeenTriggered2()).isTrue(); - - listener.reset(); - subscription.unsubscribe(); - - this.bus.post(new TestEvent1()); - this.bus.post(new TestEvent2()); - - assertThat(listener.hasBeenTriggered1()).isFalse(); - assertThat(listener.hasBeenTriggered2()).isFalse(); - } - - @Test - void test_super_class_post() { - final var subscriber = new ClassEventSubscriber(); - this.bus.subscribe(TestEvent3.class, this.plugin, subscriber); - this.bus.post(new TestEvent4()); - // SimpleEventBus does not support super class posting - assertThat(subscriber.hasBeenTriggered()).isFalse(); - // So we have to do it manually - this.bus.post(TestEvent3.class, new TestEvent4()); - assertThat(subscriber.hasBeenTriggered()).isTrue(); - } - - private enum TestEnum { - VALUE - } - - private static final class TestEvent1 {} - - private static final class TestEvent2 {} - - private static class TestEvent3 {} - - private static class TestEvent4 extends TestEvent3 {} - - private static final class ClassEventSubscriber implements Consumer { - - private long triggerTime = -1; - - @Override - public void accept(final E event) { - if (this.hasBeenTriggered()) { - throw new IllegalStateException("The subscriber has been triggered twice."); - } - this.triggerTime = System.nanoTime(); - } - - private boolean hasBeenTriggered() { - return this.triggerTime != -1; - } - - private void reset() { - if (!this.hasBeenTriggered()) { - throw new IllegalStateException("Tried to reset while not triggered."); - } - this.triggerTime = -1; - } - } - - private static final class EnumEventSubscriber implements Runnable { - - private long triggerTime = -1; - - @Override - public void run() { - if (this.hasBeenTriggered()) { - throw new IllegalStateException("The subscriber has been triggered twice."); - } - this.triggerTime = System.nanoTime(); - } - - private boolean hasBeenTriggered() { - return this.triggerTime != -1; - } - - private void reset() { - if (!this.hasBeenTriggered()) { - throw new IllegalStateException("Tried to reset while not triggered."); - } - this.triggerTime = -1; - } - } - - @SuppressWarnings("unused") - private static final class AnnotatedEventListener { - - private long triggerTime1 = -1; - private long triggerTime2 = -1; - - @EventHandler - public void listenTo1(final TestEvent1 event) { - if (this.hasBeenTriggered1()) { - throw new IllegalStateException("The subscriber has been triggered twice."); - } - this.triggerTime1 = System.nanoTime(); - } - - @EventHandler - public void listenTo2(final TestEvent2 event) { - if (this.hasBeenTriggered2()) { - throw new IllegalStateException("The subscriber has been triggered twice."); - } - this.triggerTime2 = System.nanoTime(); - } - - private boolean hasBeenTriggered1() { - return this.triggerTime1 != -1; - } - - private boolean hasBeenTriggered2() { - return this.triggerTime2 != -1; - } - - private void reset() { - if (!this.hasBeenTriggered1()) { - throw new IllegalStateException("Tried to reset while not triggered."); - } - if (!this.hasBeenTriggered2()) { - throw new IllegalStateException("Tried to reset while not triggered."); - } - this.triggerTime1 = -1; - this.triggerTime2 = -1; - } - } -} diff --git a/distributor-core/src/test/java/fr/xpdustry/distributor/core/scheduler/SimplePluginSchedulerTest.java b/distributor-core/src/test/java/fr/xpdustry/distributor/core/scheduler/SimplePluginSchedulerTest.java deleted file mode 100644 index 3526e683..00000000 --- a/distributor-core/src/test/java/fr/xpdustry/distributor/core/scheduler/SimplePluginSchedulerTest.java +++ /dev/null @@ -1,285 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.scheduler; - -import arc.Core; -import arc.mock.MockApplication; -import fr.xpdustry.distributor.api.plugin.MindustryPlugin; -import fr.xpdustry.distributor.api.scheduler.Cancellable; -import fr.xpdustry.distributor.api.scheduler.MindustryTimeUnit; -import fr.xpdustry.distributor.api.scheduler.TaskHandler; -import java.time.Duration; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicInteger; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.within; -import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively; - -// TODO The tests are so cursed, A better way to measure the time and precision of the scheduler is needed -public final class SimplePluginSchedulerTest { - - // Precision of 0.2 seconds - private static final Duration PRECISION = Duration.ofMillis(200); - private static final long PRECISION_TICKS = 12L; - - private MindustryPlugin plugin; - private TimeSource source; - private SimplePluginScheduler scheduler; - private Thread updater; - - @SuppressWarnings("BusyWait") - @BeforeEach - void before() { - Core.app = new MockApplication(); - this.plugin = Mockito.mock(MindustryPlugin.class); - this.source = TimeSource.standard(); - this.scheduler = new SimplePluginScheduler(this.source, Runnable::run, 4); - this.updater = new Thread(() -> { - while (true) { - try { - Thread.sleep(1000L / 60); - } catch (final InterruptedException e) { - break; - } - this.scheduler.onPluginUpdate(); - } - this.scheduler.onPluginExit(); - }); - this.updater.start(); - } - - @AfterEach - void after() { - this.updater.interrupt(); - } - - @Test - void test_simple_sync_schedule() { - final var future = new CompletableFuture(); - assertThat(this.scheduler.scheduleSync(this.plugin).execute(() -> future.complete(Thread.currentThread()))) - .succeedsWithin(PRECISION); - assertThat(future).isCompletedWithValueMatching(thread -> !thread.getName() - .startsWith(SimplePluginScheduler.DISTRIBUTOR_WORKER_BASE_NAME)); - } - - @Test - void test_simple_async_schedule() { - final var future = new CompletableFuture(); - assertThat(this.scheduler.scheduleAsync(this.plugin).execute(() -> future.complete(Thread.currentThread()))) - .succeedsWithin(PRECISION); - assertThat(future).isCompletedWithValueMatching(thread -> thread.getName() - .startsWith(SimplePluginScheduler.DISTRIBUTOR_WORKER_BASE_NAME)); - } - - @Test - void test_delay() { - final var future = new CompletableFuture(); - final var begin = this.source.getCurrentTicks(); - assertThat(this.scheduler - .scheduleSync(this.plugin) - .delay(1L, MindustryTimeUnit.SECONDS) - .execute(() -> future.complete(this.source.getCurrentTicks()))) - .succeedsWithin(Duration.ofSeconds(1L).plus(PRECISION)); - final var end = future.join(); - assertThat(end - begin).isCloseTo(60L, within(PRECISION_TICKS)); - } - - @Test - void test_interval() { - final var counter = new CountDownLatch(3); - final var longs = new ArrayList(); - final var future = this.scheduler - .scheduleSync(this.plugin) - .repeat(500L, MindustryTimeUnit.MILLISECONDS) - .execute(() -> { - longs.add(this.source.getCurrentTicks()); - try { - Thread.sleep(500L); - } catch (final InterruptedException e) { - throw new RuntimeException(e); - } - counter.countDown(); - }); - - // The task should execute 3 times, every second - assertTimeoutPreemptively(Duration.ofSeconds(4L), () -> { - counter.await(); - future.cancel(false); - }); - - assertThat(longs.get(1) - longs.get(0)).isCloseTo(60L, within(PRECISION_TICKS)); - assertThat(longs.get(2) - longs.get(1)).isCloseTo(60L, within(PRECISION_TICKS)); - } - - @Test - void test_cancelling() { - final var future = new CompletableFuture(); - final var counter = new AtomicInteger(3); - final var begin = this.source.getCurrentTicks(); - assertThat(this.scheduler - .scheduleSync(this.plugin) - .repeat(500L, MindustryTimeUnit.MILLISECONDS) - .execute(cancellable -> { - if (counter.decrementAndGet() == 0) { - cancellable.cancel(); - future.complete(this.source.getCurrentTicks()); - } - })) - .failsWithin(Duration.ofSeconds(1L).plus(PRECISION)); - - assertTimeoutPreemptively(Duration.ofSeconds(2L), () -> { - final var end = future.join(); - assertThat(end - begin).isCloseTo(60L, within(PRECISION_TICKS)); - }); - } - - @Test - void test_recipe() { - final var steps = new ArrayList>(); - final var task = this.scheduler - .recipe(this.plugin, "initial") - .thenAccept(value -> steps.add(new TestRecipeStep<>(value + " accept"))) - .thenApply(value -> { - final var newValue = value + " apply"; - steps.add(new TestRecipeStep<>(newValue)); - return newValue; - }) - .thenRun(() -> steps.add(new TestRecipeStep<>("run"))) - .thenAcceptAsync(value -> steps.add(new TestRecipeStep<>(value + " accept async"))) - .thenApplyAsync(value -> { - final var newValue = value + " apply async"; - steps.add(new TestRecipeStep<>(newValue)); - return newValue; - }) - .thenRunAsync(() -> steps.add(new TestRecipeStep<>("run async"))) - .execute(); - - assertThat(task).succeedsWithin(Duration.ofSeconds(1L)).isEqualTo("initial apply apply async"); - assertThat(steps).size().isEqualTo(6); - - assertThat(steps.get(0)) - .matches(TestRecipeStep::isSyncThread) - .extracting(TestRecipeStep::getValue) - .isEqualTo("initial accept"); - - assertThat(steps.get(1)) - .matches(TestRecipeStep::isSyncThread) - .extracting(TestRecipeStep::getValue) - .isEqualTo("initial apply"); - - assertThat(steps.get(2)) - .matches(TestRecipeStep::isSyncThread) - .extracting(TestRecipeStep::getValue) - .isEqualTo("run"); - - assertThat(steps.get(3)) - .matches(TestRecipeStep::isAsyncThread) - .extracting(TestRecipeStep::getValue) - .isEqualTo("initial apply accept async"); - - assertThat(steps.get(4)) - .matches(TestRecipeStep::isAsyncThread) - .extracting(TestRecipeStep::getValue) - .isEqualTo("initial apply apply async"); - - assertThat(steps.get(5)) - .matches(TestRecipeStep::isAsyncThread) - .extracting(TestRecipeStep::getValue) - .isEqualTo("run async"); - } - - @Test - void test_task_handler() { - final var handler = new TestTaskHandler(this.source); - final var tasks = this.scheduler.parse(this.plugin, handler); - - assertThat(tasks).size().isEqualTo(3); - assertTimeoutPreemptively(Duration.ofSeconds(1L), () -> handler.latch.await()); - - assertThat(handler.longs1.get(1) - handler.longs1.get(0)).isCloseTo(24L, within(PRECISION_TICKS)); - assertThat(handler.longs1.get(2) - handler.longs1.get(1)).isCloseTo(24L, within(PRECISION_TICKS)); - - assertThat(handler.longs2.size()).isEqualTo(1); - assertThat(handler.longs3.size()).isEqualTo(1); - } - - private static final class TestRecipeStep { - - private final V value; - private final String thread = Thread.currentThread().getName(); - - private TestRecipeStep(final V value) { - this.value = value; - } - - public V getValue() { - return this.value; - } - - public boolean isAsyncThread() { - return this.thread.startsWith(SimplePluginScheduler.DISTRIBUTOR_WORKER_BASE_NAME); - } - - public boolean isSyncThread() { - return !this.isAsyncThread(); - } - } - - @SuppressWarnings("UnusedMethod") - private static final class TestTaskHandler { - - private final List longs1 = new ArrayList<>(); - private final List longs2 = new ArrayList<>(); - private final List longs3 = new ArrayList<>(); - - private final CountDownLatch latch = new CountDownLatch(1); - private final TimeSource source; - - private TestTaskHandler(final TimeSource source) { - this.source = source; - } - - @TaskHandler(interval = 400L, unit = MindustryTimeUnit.MILLISECONDS) - public void someTask1() { - this.longs1.add(this.source.getCurrentTicks()); - if (this.longs1.size() == 3) { - this.latch.countDown(); - } - } - - @TaskHandler(interval = 400L, unit = MindustryTimeUnit.MILLISECONDS) - public void someTask2(final Cancellable cancellable) { - this.longs2.add(this.source.getCurrentTicks()); - cancellable.cancel(); - } - - @TaskHandler - public void someTask3() { - this.longs3.add(this.source.getCurrentTicks()); - } - } -} diff --git a/distributor-core/src/test/java/fr/xpdustry/distributor/core/security/SQLPlayerValidatorTest.java b/distributor-core/src/test/java/fr/xpdustry/distributor/core/security/SQLPlayerValidatorTest.java deleted file mode 100644 index 20c9a744..00000000 --- a/distributor-core/src/test/java/fr/xpdustry/distributor/core/security/SQLPlayerValidatorTest.java +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.security; - -import arc.Events; -import fr.xpdustry.distributor.api.Distributor; -import fr.xpdustry.distributor.api.DistributorProvider; -import fr.xpdustry.distributor.api.plugin.MindustryPlugin; -import fr.xpdustry.distributor.api.security.PlayerValidatorEvent; -import fr.xpdustry.distributor.api.security.PlayerValidatorEvent.Type; -import fr.xpdustry.distributor.api.util.MUUID; -import fr.xpdustry.distributor.core.database.SQLiteConnectionFactory; -import fr.xpdustry.distributor.core.event.SimpleEventBus; -import java.nio.file.Path; -import java.util.HashMap; -import java.util.Map; -import mindustry.gen.Groups; -import mindustry.gen.Player; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; -import org.mockito.Mockito; - -import static org.assertj.core.api.Assertions.assertThat; - -public final class SQLPlayerValidatorTest { - - private static final MUUID PLAYER_1A = MUUID.of("1AAAAAAAAAAAAAAAAAAAAA==", "AAAAAAAAAAA="); - private static final MUUID PLAYER_1B = MUUID.of("1AAAAAAAAAAAAAAAAAAAAA==", "BAAAAAAAAAA="); - private static final MUUID PLAYER_2A = MUUID.of("2AAAAAAAAAAAAAAAAAAAAA==", "AAAAAAAAAAA="); - private static final MUUID PLAYER_2B = MUUID.of("2AAAAAAAAAAAAAAAAAAAAA==", "BAAAAAAAAAA="); - - private Player player1; - private Player player2; - private final Map statuses = new HashMap<>(); - - private SQLiteConnectionFactory factory; - private @TempDir Path dbDir; - private SQLPlayerValidator validator; - - @BeforeEach - void setup() { - this.factory = new SQLiteConnectionFactory( - "test_", this.dbDir.resolve("test.db"), this.getClass().getClassLoader()); - this.factory.start(); - this.validator = new SQLPlayerValidator(this.factory); - - Groups.init(); - Groups.player.add(this.player1 = this.mock(PLAYER_1A)); - Groups.player.add(this.player2 = this.mock(PLAYER_2A)); - - final var distributor = Mockito.mock(Distributor.class); - final var eventBus = new SimpleEventBus(); - Mockito.when(distributor.getEventBus()).thenReturn(eventBus); - DistributorProvider.set(distributor); - eventBus.subscribe( - PlayerValidatorEvent.class, - Mockito.mock(MindustryPlugin.class), - event -> this.statuses.put(event.player(), event.type())); - } - - @AfterEach - void tearDown() throws Exception { - this.factory.close(); - Groups.clear(); - Events.clear(); - DistributorProvider.clear(); - } - - @Test - void test_muuid_hash_length() { - assertThat(SQLPlayerValidator.hash(PLAYER_1A)).hasSize(32); - } - - @Test - void test_validate() { - assertThat(this.validator.isValid(PLAYER_1A)).isFalse(); - assertThat(this.validator.contains(PLAYER_1A.getUuid())).isFalse(); - - this.validator.validate(PLAYER_1A); - - assertThat(this.validator.isValid(PLAYER_1A)).isTrue(); - assertThat(this.validator.contains(PLAYER_1A.getUuid())).isTrue(); - - assertThat(this.statuses).containsExactlyInAnyOrderEntriesOf(Map.of(this.player1, Type.VALIDATED)); - } - - @Test - void test_invalidate() { - assertThat(this.validator.isValid(PLAYER_1A)).isFalse(); - assertThat(this.validator.contains(PLAYER_1A.getUuid())).isFalse(); - - this.validator.invalidate(PLAYER_1A); - - assertThat(this.validator.isValid(PLAYER_1A)).isFalse(); - assertThat(this.validator.contains(PLAYER_1A.getUuid())).isTrue(); - - assertThat(this.statuses).containsExactlyInAnyOrderEntriesOf(Map.of(this.player1, Type.INVALIDATED)); - } - - @Test - void test_invalidate_muuid() { - this.validator.validate(PLAYER_1A); - this.validator.validate(PLAYER_1B); - this.validator.validate(PLAYER_2A); - this.validator.validate(PLAYER_2B); - - assertThat(this.validator.isValid(PLAYER_1A)).isTrue(); - assertThat(this.validator.isValid(PLAYER_1B)).isTrue(); - assertThat(this.validator.isValid(PLAYER_2A)).isTrue(); - assertThat(this.validator.isValid(PLAYER_2B)).isTrue(); - - this.validator.invalidate(PLAYER_1A); - - assertThat(this.validator.isValid(PLAYER_1A)).isFalse(); - assertThat(this.validator.isValid(PLAYER_1B)).isTrue(); - assertThat(this.validator.isValid(PLAYER_2A)).isTrue(); - assertThat(this.validator.isValid(PLAYER_2B)).isTrue(); - - assertThat(this.statuses) - .containsExactlyInAnyOrderEntriesOf(Map.of( - this.player2, Type.VALIDATED, - this.player1, Type.INVALIDATED)); - } - - @Test - void test_invalidate_uuid() { - this.validator.validate(PLAYER_1A); - this.validator.validate(PLAYER_1B); - this.validator.validate(PLAYER_2A); - this.validator.validate(PLAYER_2B); - - assertThat(this.validator.isValid(PLAYER_1A)).isTrue(); - assertThat(this.validator.isValid(PLAYER_1B)).isTrue(); - assertThat(this.validator.isValid(PLAYER_2A)).isTrue(); - assertThat(this.validator.isValid(PLAYER_2B)).isTrue(); - - this.validator.invalidate(PLAYER_1A.getUuid()); - - assertThat(this.validator.isValid(PLAYER_1A)).isFalse(); - assertThat(this.validator.isValid(PLAYER_1B)).isFalse(); - assertThat(this.validator.isValid(PLAYER_2A)).isTrue(); - assertThat(this.validator.isValid(PLAYER_2B)).isTrue(); - - assertThat(this.statuses) - .containsExactlyInAnyOrderEntriesOf(Map.of( - this.player2, Type.VALIDATED, - this.player1, Type.INVALIDATED)); - } - - @Test - void test_invalidate_all() { - this.validator.validate(PLAYER_1A); - this.validator.validate(PLAYER_1B); - this.validator.validate(PLAYER_2A); - this.validator.validate(PLAYER_2B); - - assertThat(this.validator.isValid(PLAYER_1A)).isTrue(); - assertThat(this.validator.isValid(PLAYER_1B)).isTrue(); - assertThat(this.validator.isValid(PLAYER_2A)).isTrue(); - assertThat(this.validator.isValid(PLAYER_2B)).isTrue(); - - this.validator.invalidateAll(); - - assertThat(this.validator.isValid(PLAYER_1A)).isFalse(); - assertThat(this.validator.isValid(PLAYER_1B)).isFalse(); - assertThat(this.validator.isValid(PLAYER_2A)).isFalse(); - assertThat(this.validator.isValid(PLAYER_2B)).isFalse(); - - assertThat(this.statuses) - .containsExactlyInAnyOrderEntriesOf( - Map.of(this.player1, Type.INVALIDATED, this.player2, Type.INVALIDATED)); - } - - @Test - void test_remove_muuid() { - this.validator.validate(PLAYER_1A); - this.validator.validate(PLAYER_1B); - this.validator.validate(PLAYER_2A); - this.validator.validate(PLAYER_2B); - - assertThat(this.validator.contains(PLAYER_1A)).isTrue(); - assertThat(this.validator.contains(PLAYER_1B)).isTrue(); - assertThat(this.validator.contains(PLAYER_2A)).isTrue(); - assertThat(this.validator.contains(PLAYER_2B)).isTrue(); - - this.validator.remove(PLAYER_1A); - - assertThat(this.validator.contains(PLAYER_1A)).isFalse(); - assertThat(this.validator.contains(PLAYER_1B)).isTrue(); - assertThat(this.validator.contains(PLAYER_2A)).isTrue(); - assertThat(this.validator.contains(PLAYER_2B)).isTrue(); - - assertThat(this.statuses) - .containsExactlyInAnyOrderEntriesOf(Map.of( - this.player2, Type.VALIDATED, - this.player1, Type.REMOVED)); - } - - @Test - void test_remove_uuid() { - this.validator.validate(PLAYER_1A); - this.validator.validate(PLAYER_1B); - this.validator.validate(PLAYER_2A); - this.validator.validate(PLAYER_2B); - - assertThat(this.validator.contains(PLAYER_1A)).isTrue(); - assertThat(this.validator.contains(PLAYER_1B)).isTrue(); - assertThat(this.validator.contains(PLAYER_2A)).isTrue(); - assertThat(this.validator.contains(PLAYER_2B)).isTrue(); - - this.validator.remove(PLAYER_1A.getUuid()); - - assertThat(this.validator.contains(PLAYER_1A)).isFalse(); - assertThat(this.validator.contains(PLAYER_1B)).isFalse(); - assertThat(this.validator.contains(PLAYER_2A)).isTrue(); - assertThat(this.validator.contains(PLAYER_2B)).isTrue(); - - assertThat(this.statuses) - .containsExactlyInAnyOrderEntriesOf(Map.of( - this.player2, Type.VALIDATED, - this.player1, Type.REMOVED)); - } - - @Test - void test_remove_all() { - this.validator.validate(PLAYER_1A); - this.validator.validate(PLAYER_1B); - this.validator.validate(PLAYER_2A); - this.validator.validate(PLAYER_2B); - - assertThat(this.validator.contains(PLAYER_1A)).isTrue(); - assertThat(this.validator.contains(PLAYER_1B)).isTrue(); - assertThat(this.validator.contains(PLAYER_2A)).isTrue(); - assertThat(this.validator.contains(PLAYER_2B)).isTrue(); - - this.validator.removeAll(); - - assertThat(this.validator.contains(PLAYER_1A)).isFalse(); - assertThat(this.validator.contains(PLAYER_1B)).isFalse(); - assertThat(this.validator.contains(PLAYER_2A)).isFalse(); - assertThat(this.validator.contains(PLAYER_2B)).isFalse(); - - assertThat(this.statuses) - .containsExactlyInAnyOrderEntriesOf(Map.of(this.player1, Type.REMOVED, this.player2, Type.REMOVED)); - } - - private Player mock(final MUUID muuid) { - final var player = Mockito.mock(Player.class); - Mockito.when(player.uuid()).thenReturn(muuid.getUuid()); - Mockito.when(player.usid()).thenReturn(muuid.getUsid()); - return player; - } -} diff --git a/distributor-core/src/test/java/fr/xpdustry/distributor/core/security/permission/AbstractSQLPermissibleManagerTest.java b/distributor-core/src/test/java/fr/xpdustry/distributor/core/security/permission/AbstractSQLPermissibleManagerTest.java deleted file mode 100644 index f9e68dcc..00000000 --- a/distributor-core/src/test/java/fr/xpdustry/distributor/core/security/permission/AbstractSQLPermissibleManagerTest.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.security.permission; - -import fr.xpdustry.distributor.api.security.permission.Permissible; -import fr.xpdustry.distributor.api.security.permission.PermissibleManager; -import fr.xpdustry.distributor.core.database.ConnectionFactory; -import fr.xpdustry.distributor.core.database.SQLiteConnectionFactory; -import java.nio.file.Path; -import java.util.List; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -import static org.assertj.core.api.Assertions.assertThat; - -public abstract class AbstractSQLPermissibleManagerTest

{ - - private ConnectionFactory factory; - private @TempDir Path tempDir; - - @BeforeEach - void createFactory() { - this.factory = new SQLiteConnectionFactory( - "", this.tempDir.resolve("test.db"), this.getClass().getClassLoader()); - this.factory.start(); - Permissibles.createDatabase(this.factory); - } - - @AfterEach - void closeFactory() throws Exception { - this.factory.close(); - } - - @Test - void test_save() { - final var manager = this.createManager(this.factory); - final var permissible = this.createRandomPermissible(); - - assertThat(manager.count()).isEqualTo(0); - assertThat(manager.findAll()).isEmpty(); - - manager.save(permissible); - - assertThat(manager.count()).isEqualTo(1); - assertThat(manager.findAll()).singleElement().isNotSameAs(permissible).isEqualTo(permissible); - } - - @Test - void test_save_all() { - final var manager = this.createManager(this.factory); - final var permissible1 = this.createRandomPermissible(); - final var permissible2 = this.createRandomPermissible(); - - manager.saveAll(List.of(permissible1, permissible2)); - - assertThat(manager.count()).isEqualTo(2); - assertThat(manager.findAll()).containsExactlyInAnyOrder(permissible1, permissible2); - } - - @Test - void test_modify() { - final var manager = this.createManager(this.factory); - final var permissible = this.createRandomPermissible(); - - permissible.setPermission("test", true); - manager.save(permissible); - assertThat(manager.findAll()).singleElement().isEqualTo(permissible); - - permissible.setPermission("test", false); - manager.save(permissible); - assertThat(manager.findAll()).singleElement().isEqualTo(permissible); - } - - @Test - void test_find() { - final var manager = this.createManager(this.factory); - final var permissible = this.createRandomPermissible(); - - assertThat(manager.findById(this.extractIdentifier(permissible))).isEmpty(); - manager.save(permissible); - assertThat(manager.findById(this.extractIdentifier(permissible))) - .isPresent() - .get() - .isNotSameAs(permissible) - .isEqualTo(permissible); - } - - @Test - void test_exists() { - final var manager = this.createManager(this.factory); - final var permissible = this.createRandomPermissible(); - - assertThat(manager.existsById(this.extractIdentifier(permissible))).isFalse(); - assertThat(manager.exists(permissible)).isFalse(); - - manager.save(permissible); - - assertThat(manager.existsById(this.extractIdentifier(permissible))).isTrue(); - assertThat(manager.exists(permissible)).isTrue(); - } - - @Test - void test_delete() { - final var manager = this.createManager(this.factory); - final var permissible1 = this.createRandomPermissible(); - final var permissible2 = this.createRandomPermissible(); - - manager.save(permissible1); - manager.save(permissible2); - - assertThat(manager.count()).isEqualTo(2); - assertThat(manager.exists(permissible1)).isTrue(); - assertThat(manager.exists(permissible2)).isTrue(); - - manager.delete(permissible1); - assertThat(manager.count()).isEqualTo(1); - assertThat(manager.exists(permissible1)).isFalse(); - assertThat(manager.exists(permissible2)).isTrue(); - - manager.deleteById(this.extractIdentifier(permissible2)); - assertThat(manager.count()).isEqualTo(0); - assertThat(manager.exists(permissible1)).isFalse(); - assertThat(manager.exists(permissible2)).isFalse(); - } - - @Test - void test_delete_all() { - final var manager = this.createManager(this.factory); - final var permissible1 = this.createRandomPermissible(); - final var permissible2 = this.createRandomPermissible(); - - manager.saveAll(List.of(permissible1, permissible2)); - assertThat(manager.count()).isEqualTo(2); - - manager.deleteAll(List.of(permissible1, permissible2)); - assertThat(manager.count()).isEqualTo(0); - } - - protected abstract PermissibleManager

createManager(final ConnectionFactory factory); - - protected abstract P createRandomPermissible(); - - protected abstract String extractIdentifier(final P permissible); -} diff --git a/distributor-core/src/test/java/fr/xpdustry/distributor/core/security/permission/SQLGroupPermissibleManagerTest.java b/distributor-core/src/test/java/fr/xpdustry/distributor/core/security/permission/SQLGroupPermissibleManagerTest.java deleted file mode 100644 index 36f44159..00000000 --- a/distributor-core/src/test/java/fr/xpdustry/distributor/core/security/permission/SQLGroupPermissibleManagerTest.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.security.permission; - -import fr.xpdustry.distributor.api.security.permission.GroupPermissible; -import fr.xpdustry.distributor.api.security.permission.PermissibleManager; -import fr.xpdustry.distributor.core.database.ConnectionFactory; -import java.util.UUID; - -public final class SQLGroupPermissibleManagerTest extends AbstractSQLPermissibleManagerTest { - - @Override - protected PermissibleManager createManager(final ConnectionFactory factory) { - return new SQLGroupPermissibleManager(factory); - } - - @Override - protected GroupPermissible createRandomPermissible() { - return new SimpleGroupPermissible(UUID.randomUUID().toString()); - } - - @Override - protected String extractIdentifier(final GroupPermissible permissible) { - return permissible.getName(); - } -} diff --git a/distributor-core/src/test/java/fr/xpdustry/distributor/core/security/permission/SQLPermissionServiceTest.java b/distributor-core/src/test/java/fr/xpdustry/distributor/core/security/permission/SQLPermissionServiceTest.java deleted file mode 100644 index e336df80..00000000 --- a/distributor-core/src/test/java/fr/xpdustry/distributor/core/security/permission/SQLPermissionServiceTest.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.security.permission; - -import fr.xpdustry.distributor.api.security.PlayerValidator; -import fr.xpdustry.distributor.api.security.permission.GroupPermissible; -import fr.xpdustry.distributor.api.security.permission.PlayerPermissible; -import fr.xpdustry.distributor.api.util.MUUID; -import fr.xpdustry.distributor.api.util.Tristate; -import fr.xpdustry.distributor.core.DistributorConfiguration; -import fr.xpdustry.distributor.core.database.SQLiteConnectionFactory; -import java.nio.file.Path; -import java.util.function.Consumer; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; -import org.mockito.Mockito; - -import static org.assertj.core.api.Assertions.assertThat; - -public final class SQLPermissionServiceTest { - - private static final MUUID PLAYER = MUUID.of("AAAAAAAAAAAAAAAAAAAAAA==", "AAAAAAAAAAA="); - - private static final String DEFAULT_GROUP = "default"; - private static final String GROUP1 = "group1"; - private static final String GROUP2 = "group2"; - - private static final String PERMISSION1 = "fr.xpdustry.test.a"; - private static final String PERMISSION2 = "fr.xpdustry.test.b"; - - private SQLiteConnectionFactory factory; - private @TempDir Path dbDir; - private SQLPermissionService service; - - @BeforeEach - void setup() { - final var config = Mockito.mock(DistributorConfiguration.class); - Mockito.when(config.getPermissionPrimaryGroup()).thenReturn(DEFAULT_GROUP); - Mockito.when(config.isAdminIgnored()).thenReturn(true); - - final var validator = Mockito.mock(PlayerValidator.class); - Mockito.when(validator.isValid(PLAYER)).thenReturn(true); - - this.factory = new SQLiteConnectionFactory( - "test_", this.dbDir.resolve("test.db"), this.getClass().getClassLoader()); - this.factory.start(); - - this.service = new SQLPermissionService(config, this.factory, validator); - } - - @AfterEach - void tearDown() throws Exception { - this.factory.close(); - } - - @Test - void test_player_permission_calculation_player() { - this.createPlayer(player -> player.setPermission(PERMISSION1, Tristate.TRUE)); - assertThat(this.service.getPlayerPermission(PLAYER, PERMISSION1).asBoolean()) - .isTrue(); - } - - @Test - void test_player_permission_calculation_group() { - this.createPlayer(player -> player.addParentGroup(GROUP1)); - this.createGroup(GROUP1, group -> group.setPermission(PERMISSION1, Tristate.FALSE)); - assertThat(this.service.getPlayerPermission(PLAYER, PERMISSION1).asBoolean()) - .isFalse(); - } - - @Test - void test_player_permission_calculation_group_with_weight() { - this.createPlayer(player -> { - player.addParentGroup(GROUP1); - player.addParentGroup(GROUP2); - }); - - this.createGroup(GROUP1, group -> { - group.setPermission(PERMISSION1, Tristate.FALSE); - group.setWeight(10); - }); - - this.createGroup(GROUP2, group -> { - group.setPermission(PERMISSION1, Tristate.TRUE); - group.setWeight(20); - }); - - assertThat(this.service.getPlayerPermission(PLAYER, PERMISSION1).asBoolean()) - .isTrue(); - } - - @Test - void test_player_default_group() { - this.createGroup(DEFAULT_GROUP, group -> group.setPermission(PERMISSION2, Tristate.TRUE)); - assertThat(this.service.getPlayerPermission(PLAYER, PERMISSION2).asBoolean()) - .isTrue(); - } - - @Test - void test_group_permission_calculation() { - this.createGroup(GROUP1, group -> group.setPermission(PERMISSION1, Tristate.TRUE)); - assertThat(this.service.getGroupPermission(GROUP1, PERMISSION1).asBoolean()) - .isTrue(); - } - - @Test - void test_group_permission_calculation_parent() { - this.createGroup(GROUP1, group -> group.addParentGroup(GROUP2)); - this.createGroup(GROUP2, group -> group.setPermission(PERMISSION1, Tristate.TRUE)); - assertThat(this.service.getGroupPermission(GROUP1, PERMISSION1).asBoolean()) - .isTrue(); - } - - private void createPlayer(final Consumer setup) { - final var players = this.service.getPlayerPermissionManager(); - final var player = players.findOrCreateById(SQLPermissionServiceTest.PLAYER.getUuid()); - setup.accept(player); - players.save(player); - } - - private void createGroup(final String name, final Consumer setup) { - final var groups = this.service.getGroupPermissionManager(); - final var group = groups.findOrCreateById(name); - setup.accept(group); - groups.save(group); - } -} diff --git a/distributor-core/src/test/java/fr/xpdustry/distributor/core/security/permission/SQLPlayerPermissibleManagerTest.java b/distributor-core/src/test/java/fr/xpdustry/distributor/core/security/permission/SQLPlayerPermissibleManagerTest.java deleted file mode 100644 index 4bd6d412..00000000 --- a/distributor-core/src/test/java/fr/xpdustry/distributor/core/security/permission/SQLPlayerPermissibleManagerTest.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.core.security.permission; - -import fr.xpdustry.distributor.api.security.permission.PermissibleManager; -import fr.xpdustry.distributor.api.security.permission.PlayerPermissible; -import fr.xpdustry.distributor.core.database.ConnectionFactory; -import java.util.Base64; -import java.util.Random; - -public final class SQLPlayerPermissibleManagerTest extends AbstractSQLPermissibleManagerTest { - - private final Random random = new Random(); - - @Override - protected PermissibleManager createManager(final ConnectionFactory factory) { - return new SQLPlayerPermissibleManager(factory); - } - - @Override - protected PlayerPermissible createRandomPermissible() { - final var bytes = new byte[16]; - this.random.nextBytes(bytes); - return new SimplePlayerPermissible(Base64.getEncoder().encodeToString(bytes)); - } - - @Override - protected String extractIdentifier(final PlayerPermissible permissible) { - return permissible.getUuid(); - } -} diff --git a/distributor-kotlin/build.gradle.kts b/distributor-kotlin/build.gradle.kts deleted file mode 100644 index 83ba0015..00000000 --- a/distributor-kotlin/build.gradle.kts +++ /dev/null @@ -1,83 +0,0 @@ -import fr.xpdustry.toxopid.task.GithubArtifactDownload - -plugins { - kotlin("jvm") version "1.9.10" - id("distributor.base-conventions") - id("distributor.publishing-conventions") - id("distributor.mindustry-conventions") -} - -repositories { - maven("https://maven.xpdustry.com/releases") { - name = "xpdustry-releases" - mavenContent { releasesOnly() } - } -} - -dependencies { - compileOnly("com.xpdustry:kotlin-runtime:3.1.0-k.1.9.10") - compileOnly(project(":distributor-core")) - api(cloudCommandFramework("kotlin-extensions")) - api(cloudCommandFramework("kotlin-coroutines")) - api(cloudCommandFramework("kotlin-coroutines-annotations")) - testImplementation(kotlin("stdlib")) -} - -configurations.runtimeClasspath { - exclude(group = "cloud.commandframework", module = "cloud-core") - exclude(group = "cloud.commandframework", module = "cloud-annotations") - exclude(group = "org.jetbrains.kotlin") - exclude(group = "org.jetbrains.kotlinx") -} - -val metadata = fr.xpdustry.toxopid.spec.ModMetadata.fromJson(rootProject.file("plugin.json")) -metadata.version = rootProject.version.toString() -metadata.description = rootProject.description.toString() -metadata.name = "distributor-kotlin" -metadata.displayName = "DistributorKotlin" -metadata.main = "fr.xpdustry.distributor.kotlin.DistributorKotlinPlugin" -metadata.dependencies += listOf("distributor-core", "kotlin-runtime") - -kotlin { - coreLibrariesVersion = "1.9.10" - explicitApi() - jvmToolchain { - languageVersion.set(JavaLanguageVersion.of(17)) - } -} - -spotless { - kotlin { - ktlint() - } -} - -tasks.shadowJar { - archiveFileName.set("distributor-kotlin.jar") - - doFirst { - val temp = temporaryDir.resolve("plugin.json") - temp.writeText(metadata.toJson(true)) - from(temp) - } -} - -val kotlinRuntime = - tasks.register("downloadKotlinRuntime") { - user.set("xpdustry") - repo.set("kotlin-runtime") - name.set("kotlin-runtime.jar") - version.set("v3.1.0-k.1.9.10") - } - -tasks.runMindustryServer { - mods.setFrom(project(":distributor-core").tasks.shadowJar, tasks.shadowJar, kotlinRuntime) -} - -// Indra adds the javadoc task, we don't want that so disable it -components.named("java") { - val component = this as AdhocComponentWithVariants - component.withVariantsFromConfiguration(configurations.javadocElements.get()) { - skip() - } -} diff --git a/distributor-kotlin/gradle.properties b/distributor-kotlin/gradle.properties deleted file mode 100644 index 0480fdcf..00000000 --- a/distributor-kotlin/gradle.properties +++ /dev/null @@ -1 +0,0 @@ -kotlin.stdlib.default.dependency=false \ No newline at end of file diff --git a/distributor-kotlin/src/main/kotlin/fr/xpdustry/distributor/kotlin/DistributorKotlinPlugin.kt b/distributor-kotlin/src/main/kotlin/fr/xpdustry/distributor/kotlin/DistributorKotlinPlugin.kt deleted file mode 100644 index d2d103a9..00000000 --- a/distributor-kotlin/src/main/kotlin/fr/xpdustry/distributor/kotlin/DistributorKotlinPlugin.kt +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Distributor, a feature-rich framework for Mindustry plugins. - * - * Copyright (C) 2023 Xpdustry - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.xpdustry.distributor.kotlin - -import fr.xpdustry.distributor.api.plugin.AbstractMindustryPlugin - -public class DistributorKotlinPlugin : AbstractMindustryPlugin() { - override fun onInit() { - logger.info("Distributor kotlin extensions loaded") - } -} diff --git a/distributor-logging-simple/build.gradle.kts b/distributor-logging-simple/build.gradle.kts new file mode 100644 index 00000000..9a91e089 --- /dev/null +++ b/distributor-logging-simple/build.gradle.kts @@ -0,0 +1,16 @@ +plugins { + id("distributor4.base-conventions") + id("distributor4.mindustry-conventions") +} + +module { + identifier = "distributor-logger" + display = "DistributorLoggerSimple" + main = "com.xpdustry.distributor.logger.simple.DistributorLoggerPlugin" + description = "Simple slf4j logger implementation for plugins." +} + +dependencies { + implementation(libs.slf4j.api) + implementation(libs.slf4j.from.jul) +} diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/logging/ArcLogger.java b/distributor-logging-simple/src/main/java/com/xpdustry/distributor/logger/simple/DistributorLogger.java similarity index 70% rename from distributor-core/src/main/java/fr/xpdustry/distributor/core/logging/ArcLogger.java rename to distributor-logging-simple/src/main/java/com/xpdustry/distributor/logger/simple/DistributorLogger.java index 2d97eef3..9ffbc4db 100644 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/logging/ArcLogger.java +++ b/distributor-logging-simple/src/main/java/com/xpdustry/distributor/logger/simple/DistributorLogger.java @@ -1,7 +1,7 @@ /* * Distributor, a feature-rich framework for Mindustry plugins. * - * Copyright (C) 2023 Xpdustry + * Copyright (C) 2024 Xpdustry * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,21 +16,23 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package fr.xpdustry.distributor.core.logging; +package com.xpdustry.distributor.logger.simple; import arc.util.ColorCodes; import arc.util.Log; import arc.util.Log.LogLevel; +import java.io.PrintWriter; import java.io.Serial; +import java.io.StringWriter; import java.util.Arrays; import mindustry.net.Administration; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; import org.slf4j.Marker; import org.slf4j.event.Level; import org.slf4j.helpers.AbstractLogger; import org.slf4j.helpers.MessageFormatter; -public final class ArcLogger extends AbstractLogger { +public final class DistributorLogger extends AbstractLogger { @Serial private static final long serialVersionUID = 3476499937056865545L; @@ -41,59 +43,59 @@ public final class ArcLogger extends AbstractLogger { private final @Nullable String plugin; - public ArcLogger(final String name, final @Nullable String plugin) { + public DistributorLogger(final String name, final @Nullable String plugin) { this.name = name; this.plugin = plugin; } @Override public boolean isTraceEnabled() { - return this.isArcLogLevelAtLeast(Log.LogLevel.debug) && TRACE.bool(); + return this.isArcLogLevelAtLeast(LogLevel.debug) && TRACE.bool(); } @Override public boolean isTraceEnabled(final Marker marker) { - return this.isArcLogLevelAtLeast(Log.LogLevel.debug) && TRACE.bool(); + return this.isArcLogLevelAtLeast(LogLevel.debug) && TRACE.bool(); } @Override public boolean isDebugEnabled() { - return this.isArcLogLevelAtLeast(Log.LogLevel.debug); + return this.isArcLogLevelAtLeast(LogLevel.debug); } @Override public boolean isDebugEnabled(final Marker marker) { - return this.isArcLogLevelAtLeast(Log.LogLevel.debug); + return this.isArcLogLevelAtLeast(LogLevel.debug); } @Override public boolean isInfoEnabled() { - return this.isArcLogLevelAtLeast(Log.LogLevel.info); + return this.isArcLogLevelAtLeast(LogLevel.info); } @Override public boolean isInfoEnabled(final Marker marker) { - return this.isArcLogLevelAtLeast(Log.LogLevel.info); + return this.isArcLogLevelAtLeast(LogLevel.info); } @Override public boolean isWarnEnabled() { - return this.isArcLogLevelAtLeast(Log.LogLevel.warn); + return this.isArcLogLevelAtLeast(LogLevel.warn); } @Override public boolean isWarnEnabled(final Marker marker) { - return this.isArcLogLevelAtLeast(Log.LogLevel.warn); + return this.isArcLogLevelAtLeast(LogLevel.warn); } @Override public boolean isErrorEnabled() { - return this.isArcLogLevelAtLeast(Log.LogLevel.err); + return this.isArcLogLevelAtLeast(LogLevel.err); } @Override public boolean isErrorEnabled(final Marker marker) { - return this.isArcLogLevelAtLeast(Log.LogLevel.err); + return this.isArcLogLevelAtLeast(LogLevel.err); } @Override @@ -139,33 +141,30 @@ protected void handleNormalizedLoggingCall( arguments = arguments.length == 1 ? null : Arrays.copyOf(arguments, arguments.length - 1); } - final var string = builder.append( - MessageFormatter.basicArrayFormat(messagePattern.replace("{}", "&fb&lb{}&fr"), arguments)) - .toString(); + builder.append(MessageFormatter.basicArrayFormat(messagePattern.replace("{}", "&fb&lb{}&fr"), arguments)); + + if (throwable != null) { + final var sw = new StringWriter(); + final var pw = new PrintWriter(sw); + throwable.printStackTrace(pw); + builder.append(": ").append(sw); + } synchronized (WRITE_LOCK) { - if (throwable != null && (arguments == null || arguments.length == 0)) { - Log.err(string); - Log.err(throwable); - } else { - Log.log(this.getArcLogLevel(level), string); - if (throwable != null) { - Log.err(throwable); - } - } + Log.log(this.getArcLogLevel(level), builder.toString()); } } - private boolean isArcLogLevelAtLeast(final Log.LogLevel level) { + private boolean isArcLogLevelAtLeast(final LogLevel level) { return level != LogLevel.none && Log.level.ordinal() <= level.ordinal(); } - private Log.LogLevel getArcLogLevel(final Level level) { + private LogLevel getArcLogLevel(final Level level) { return switch (level) { - case TRACE, DEBUG -> Log.LogLevel.debug; - case INFO -> Log.LogLevel.info; - case WARN -> Log.LogLevel.warn; - case ERROR -> Log.LogLevel.err; + case TRACE, DEBUG -> LogLevel.debug; + case INFO -> LogLevel.info; + case WARN -> LogLevel.warn; + case ERROR -> LogLevel.err; }; } diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/logging/ArcLoggerFactory.java b/distributor-logging-simple/src/main/java/com/xpdustry/distributor/logger/simple/DistributorLoggerFactory.java similarity index 52% rename from distributor-core/src/main/java/fr/xpdustry/distributor/core/logging/ArcLoggerFactory.java rename to distributor-logging-simple/src/main/java/com/xpdustry/distributor/logger/simple/DistributorLoggerFactory.java index 78356d7d..3a22a0cc 100644 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/logging/ArcLoggerFactory.java +++ b/distributor-logging-simple/src/main/java/com/xpdustry/distributor/logger/simple/DistributorLoggerFactory.java @@ -1,7 +1,7 @@ /* * Distributor, a feature-rich framework for Mindustry plugins. * - * Copyright (C) 2023 Xpdustry + * Copyright (C) 2024 Xpdustry * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,25 +16,28 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package fr.xpdustry.distributor.core.logging; +package com.xpdustry.distributor.logger.simple; -import fr.xpdustry.distributor.api.plugin.PluginDescriptor; -import java.io.IOException; +import arc.util.serialization.Json; import java.util.Arrays; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import mindustry.mod.ModClassLoader; +import mindustry.mod.Mods; import mindustry.mod.Plugin; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; import org.slf4j.ILoggerFactory; import org.slf4j.Logger; -public final class ArcLoggerFactory implements ILoggerFactory { +public final class DistributorLoggerFactory implements ILoggerFactory { - private final Map loggers = new ConcurrentHashMap<>(); + private static final List LOGGING_PACKAGES = List.of("org.slf4j", "java.util.logging", "sun.util.logging"); + + private final Map loggers = new ConcurrentHashMap<>(); { - this.loggers.put(Logger.ROOT_LOGGER_NAME, new ArcLogger(Logger.ROOT_LOGGER_NAME, null)); + this.loggers.put(Logger.ROOT_LOGGER_NAME, new DistributorLogger(Logger.ROOT_LOGGER_NAME, null)); } @Override @@ -51,49 +54,41 @@ public Logger getLogger(final String name) { } catch (final ClassNotFoundException ignored1) { final var candidate = tryFindCaller(Thread.currentThread().getStackTrace()); if (candidate == null) { - return new ArcLogger(name, null); + return new DistributorLogger(name, null); } try { caller = Class.forName(candidate); cache = false; } catch (final ClassNotFoundException ignored2) { - return new ArcLogger(name, null); + return new DistributorLogger(name, null); } } if (Plugin.class.isAssignableFrom(caller)) { - @SuppressWarnings("unchecked") - final var descriptor = PluginDescriptor.from((Class) caller); + final var display = getPluginDisplayName(caller.getClassLoader()); + if (display == null) { + return new DistributorLogger(name, null); + } // Plugin loggers are found on the first lookup, thus if the cache flag is false, // it means a custom logger has been created inside the plugin class - final var logger = cache - ? new ArcLogger(descriptor.getDisplayName(), null) - : new ArcLogger(name, descriptor.getDisplayName()); + final var logger = cache ? new DistributorLogger(display, null) : new DistributorLogger(name, display); if (cache) { this.loggers.put(name, logger); } return logger; } - ClassLoader classLoader = caller.getClassLoader(); - while (classLoader != null) { - if (classLoader.getParent() instanceof ModClassLoader) { - final PluginDescriptor descriptor; - try { - descriptor = PluginDescriptor.from(classLoader); - } catch (final IOException ignored) { - return this.createLogger(name, null, cache); - } - return this.createLogger(name, descriptor.getDisplayName(), cache); + ClassLoader loader = caller.getClassLoader(); + String display = null; + while (loader != null) { + if (loader.getParent() instanceof ModClassLoader) { + display = getPluginDisplayName(loader); + break; } - classLoader = classLoader.getParent(); + loader = loader.getParent(); } - return this.createLogger(name, null, cache); - } - - private ArcLogger createLogger(final String name, final @Nullable String plugin, final boolean cache) { - final var logger = new ArcLogger(name, plugin); + final var logger = new DistributorLogger(name, display); if (cache) { this.loggers.put(name, logger); } @@ -102,13 +97,28 @@ private ArcLogger createLogger(final String name, final @Nullable String plugin, private @Nullable String tryFindCaller(final StackTraceElement[] stacktrace) { return Arrays.stream(stacktrace) - .skip(3) // 0: stacktrace call, 1: ArcLoggerFactory#getLogger, 2: LoggerFactory#getLogger + .skip(3) // 0: stacktrace call, 1: DistributorLoggerFactory#getLogger, 2: LoggerFactory#getLogger .map(StackTraceElement::getClassName) // Skips the logger wrappers - .dropWhile(clazz -> clazz.startsWith("org.slf4j") - || clazz.startsWith("java.util.logging") - || clazz.startsWith("sun.util.logging")) + .dropWhile(clazz -> LOGGING_PACKAGES.stream().anyMatch(clazz::startsWith)) .findFirst() .orElse(null); } + + private @Nullable String getPluginDisplayName(final ClassLoader loader) { + var resource = loader.getResourceAsStream("plugin.json"); + if (resource == null) { + resource = loader.getResourceAsStream("plugin.hjson"); + if (resource == null) { + return null; + } + } + try (final var input = resource) { + final var meta = new Json().fromJson(Mods.ModMeta.class, input); + meta.cleanup(); + return meta.displayName(); + } catch (final Exception e) { + return null; + } + } } diff --git a/distributor-logging-simple/src/main/java/com/xpdustry/distributor/logger/simple/DistributorLoggerPlugin.java b/distributor-logging-simple/src/main/java/com/xpdustry/distributor/logger/simple/DistributorLoggerPlugin.java new file mode 100644 index 00000000..d8d142ee --- /dev/null +++ b/distributor-logging-simple/src/main/java/com/xpdustry/distributor/logger/simple/DistributorLoggerPlugin.java @@ -0,0 +1,57 @@ +/* + * Distributor, a feature-rich framework for Mindustry plugins. + * + * Copyright (C) 2024 Xpdustry + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.xpdustry.distributor.logger.simple; + +import arc.util.Log; +import mindustry.mod.Plugin; +import org.slf4j.LoggerFactory; +import org.slf4j.bridge.SLF4JBridgeHandler; + +public final class DistributorLoggerPlugin extends Plugin { + + static { + initialize(); + } + + private static void initialize() { + // Class loader trickery to use the ModClassLoader instead of the root + final var temp = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(DistributorLoggerPlugin.class.getClassLoader()); + try { + if (!(LoggerFactory.getILoggerFactory() instanceof DistributorLoggerFactory)) { + Log.err( + """ + The slf4j Logger factory isn't provided by Distributor (got @ instead of DistributorLoggerFactory). + Make sure another plugin doesn't set it's own logging implementation or that it's logging implementation is relocated correctly. + """, + LoggerFactory.getILoggerFactory().getClass().getName()); + return; + } + + // Redirect JUL to SLF4J + SLF4JBridgeHandler.removeHandlersForRootLogger(); + SLF4JBridgeHandler.install(); + + LoggerFactory.getLogger(DistributorLoggerPlugin.class).info("Initialized"); + } finally { + // Restore the class loader + Thread.currentThread().setContextClassLoader(temp); + } + } +} diff --git a/distributor-core/src/main/java/fr/xpdustry/distributor/core/logging/ArcServiceProvider.java b/distributor-logging-simple/src/main/java/com/xpdustry/distributor/logger/simple/DistributorLoggerProvider.java similarity index 68% rename from distributor-core/src/main/java/fr/xpdustry/distributor/core/logging/ArcServiceProvider.java rename to distributor-logging-simple/src/main/java/com/xpdustry/distributor/logger/simple/DistributorLoggerProvider.java index 3656f5df..9b72395e 100644 --- a/distributor-core/src/main/java/fr/xpdustry/distributor/core/logging/ArcServiceProvider.java +++ b/distributor-logging-simple/src/main/java/com/xpdustry/distributor/logger/simple/DistributorLoggerProvider.java @@ -1,7 +1,7 @@ /* * Distributor, a feature-rich framework for Mindustry plugins. * - * Copyright (C) 2023 Xpdustry + * Copyright (C) 2024 Xpdustry * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,9 +16,10 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package fr.xpdustry.distributor.core.logging; +package com.xpdustry.distributor.logger.simple; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import java.util.Objects; +import org.jspecify.annotations.Nullable; import org.slf4j.ILoggerFactory; import org.slf4j.IMarkerFactory; import org.slf4j.helpers.BasicMarkerFactory; @@ -26,25 +27,25 @@ import org.slf4j.spi.MDCAdapter; import org.slf4j.spi.SLF4JServiceProvider; -public final class ArcServiceProvider implements SLF4JServiceProvider { +public final class DistributorLoggerProvider implements SLF4JServiceProvider { - private @MonotonicNonNull ILoggerFactory loggerFactory = null; - private @MonotonicNonNull IMarkerFactory markerFactory = null; - private @MonotonicNonNull MDCAdapter mdcAdapter = null; + private @Nullable ILoggerFactory loggerFactory = null; + private @Nullable IMarkerFactory markerFactory = null; + private @Nullable MDCAdapter mdcAdapter = null; @Override public ILoggerFactory getLoggerFactory() { - return this.loggerFactory; + return Objects.requireNonNull(this.loggerFactory); } @Override public IMarkerFactory getMarkerFactory() { - return this.markerFactory; + return Objects.requireNonNull(this.markerFactory); } @Override public MDCAdapter getMDCAdapter() { - return this.mdcAdapter; + return Objects.requireNonNull(this.mdcAdapter); } @Override @@ -54,7 +55,7 @@ public String getRequestedApiVersion() { @Override public void initialize() { - this.loggerFactory = new ArcLoggerFactory(); + this.loggerFactory = new DistributorLoggerFactory(); this.markerFactory = new BasicMarkerFactory(); this.mdcAdapter = new NOPMDCAdapter(); } diff --git a/distributor-logging-simple/src/main/java/com/xpdustry/distributor/logger/simple/package-info.java b/distributor-logging-simple/src/main/java/com/xpdustry/distributor/logger/simple/package-info.java new file mode 100644 index 00000000..957fd3c5 --- /dev/null +++ b/distributor-logging-simple/src/main/java/com/xpdustry/distributor/logger/simple/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package com.xpdustry.distributor.logger.simple; + +import org.jspecify.annotations.NullMarked; diff --git a/distributor-logging-simple/src/main/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider b/distributor-logging-simple/src/main/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider new file mode 100644 index 00000000..92704302 --- /dev/null +++ b/distributor-logging-simple/src/main/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider @@ -0,0 +1 @@ +com.xpdustry.distributor.logger.simple.DistributorLoggerProvider \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 00000000..e3b07ea5 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,82 @@ +[metadata] +version = "1.0" + +[versions] + +# platforms +java = "17" +mindustry = "v146" + +# logging +slf4j = "2.0.11" + +# command +cloud = "2.0.0-beta.2" + +# utilities +immutables = "2.9.2" +geantyref = "1.3.15" + +# testing +junit = "5.10.1" +assert4j = "3.25.1" + +# static analysis +jspecify = "0.3.0" +errorprone-gradle = "3.1.0" +errorprone-core = "2.24.1" +nullaway = "0.10.21" + +# linting +palantir = "2.39.0" +ktlint = "0.49.1" + +# gradle +toxopid = "3.2.0" +indra = "3.1.3" +spotless = "6.24.0" +shadow = "8.1.1" +mammoth = "1.3.1" + +[libraries] + +# mindustry +mindustry-core = { module = "com.github.Anuken.Mindustry:core", version.ref = "mindustry" } +mindustry-server = { module = "com.github.Anuken.Mindustry:server", version.ref = "mindustry" } +arc-core = { module = "com.github.Anuken.Arc:arc-core", version.ref = "mindustry" } +arc-backend-headless = { module = "com.github.Anuken.Arc:backend-headless", version.ref = "mindustry" } + +# logging +slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } +slf4j-from-jul = { module = "org.slf4j:jul-to-slf4j", version.ref = "slf4j" } + +# command +cloud-core = { module = "org.incendo:cloud-core", version.ref = "cloud" } + +# utilities +immutables = { module = "org.immutables:value", version.ref = "immutables" } +geantyref = { module = "io.leangen.geantyref:geantyref", version.ref = "geantyref" } + +# testing +junit-api = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junit" } +junit-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit" } +assert4j-core = { module = "org.assertj:assertj-core", version.ref = "assert4j" } + +# static analysis +jspecify = { module = "org.jspecify:jspecify", version.ref = "jspecify" } +errorprone-gradle = { module = "net.ltgt.gradle:gradle-errorprone-plugin", version.ref = "errorprone-gradle" } +errorprone-core = { module = "com.google.errorprone:error_prone_core", version.ref = "errorprone-core" } +nullaway = { module = "com.uber.nullaway:nullaway", version.ref = "nullaway" } + +# gradle +toxopid = { module = "fr.xpdustry:toxopid", version.ref = "toxopid" } +indra-common = { module = "net.kyori:indra-common", version.ref = "indra" } +indra-licenser-spotless = { module = "net.kyori:indra-licenser-spotless", version.ref = "indra" } +spotless = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" } +shadow = { module = "com.github.johnrengelman:shadow", version.ref = "shadow" } +mammoth = { module = "net.kyori:mammoth", version.ref = "mammoth" } + +[bundles] +indra = [ "indra-common", "indra-licenser-spotless" ] +tests = [ "junit-api", "junit-engine", "assert4j-core" ] +mindustry = [ "mindustry-core", "mindustry-server", "arc-core", "arc-backend-headless" ] \ No newline at end of file diff --git a/plugin.json b/plugin.json index a68a8854..df824230 100644 --- a/plugin.json +++ b/plugin.json @@ -1,4 +1,6 @@ { + "displayName": "Distributor", + "name": "distributor", "author": "xpdustry", "minGameVersion": "146", "hidden": true, diff --git a/settings.gradle.kts b/settings.gradle.kts index 1566033c..eceb13f3 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,6 +1,6 @@ rootProject.name = "distributor-parent" -include(":distributor-bom") -include(":distributor-api") -include(":distributor-core") -include(":distributor-kotlin") +includeBuild("distributor-build-logic") +include(":distributor-logging-simple") +include(":distributor-common") +include(":distributor-command-cloud")