summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDico Karssiens <dico.karssiens@gmail.com>2018-11-11 14:06:45 +0000
committerDico Karssiens <dico.karssiens@gmail.com>2018-11-11 14:06:45 +0000
commit0f196f59c6a4cb76ab8409da62ff1f35505f94a8 (patch)
tree4b612eb874fc3b7aebe88430a28f1bfc7272c9a6
parente55c595e54116961798cec03f6404d0c8d986e6d (diff)
Changes I made before breaking my local repository. Hoping this works.dev
-rw-r--r--.editorconfig38
-rw-r--r--.gitignore3
-rw-r--r--README.md32
-rw-r--r--build.gradle.kts22
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/ChildCommandAddress.java240
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/Command.java316
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/CommandBuilder.java864
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/CommandException.java56
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/EMessageType.java38
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/EOverridePolicy.java24
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/ExecutionContext.java770
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/ExtendedCommand.java128
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/ICommandAddress.java410
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/ICommandDispatcher.java292
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/IContextFilter.java402
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/InheritingContextFilter.java128
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/LambdaCommand.java70
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/ModifiableCommandAddress.java624
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/PermissionContextFilter.java272
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/RootCommandAddress.java550
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/Validate.java104
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/annotation/BigRange.java104
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/annotation/Cmd.java32
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/annotation/CmdParamType.java54
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/annotation/CommandAnnotationUtils.java70
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/annotation/Desc.java54
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/annotation/Flag.java32
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/annotation/GenerateCommands.java28
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/annotation/GroupMatchedCommands.java136
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/annotation/NamedArg.java28
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/annotation/PreprocessArgs.java34
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/annotation/Range.java134
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequireConsole.java22
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequireParameters.java28
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequirePermissions.java66
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequirePlayer.java22
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/chat/AbstractChatHandler.java188
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/chat/ChatHandlers.java40
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/chat/IChatHandler.java120
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/chat/help/HelpPages.java184
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/chat/help/HelpTopicModifier.java48
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IHelpComponent.java18
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IHelpTopic.java46
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IPageBorder.java14
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IPageBuilder.java26
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IPageLayout.java40
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/chat/help/PageBorders.java152
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/chat/help/SimpleHelpComponent.java54
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/DefaultPageBuilder.java230
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/DefaultPageLayout.java80
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/DescriptionHelpTopic.java90
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/SubcommandsHelpTopic.java118
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/SyntaxHelpTopic.java184
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/EInsertionStage.java58
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/HelpComponentInserter.java86
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/IInsertion.java14
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/IInsertionFunction.java28
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/Insertions.java62
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/parameter/ArgumentBuffer.java590
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/parameter/ArgumentMergingPreProcessor.java354
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/parameter/ContextParser.java552
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/parameter/IArgumentPreProcessor.java82
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/parameter/ParameterList.java296
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/EnumParameterType.java92
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/IParameterTypeSelector.java88
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/MapBasedParameterTypeSelector.java228
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/ParameterConfig.java160
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/ParameterType.java298
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/SimpleParameterType.java62
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/predef/DefaultGroupCommand.java112
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/predef/HelpCommand.java152
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/predef/PredefinedCommand.java100
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/predef/SyntaxCommand.java72
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/registration/BukkitCommand.java244
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/registration/CommandMap.java118
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/CommandParseException.java54
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ICommandInterceptor.java72
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ICommandReceiver.java10
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveCommand.java374
-rw-r--r--dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveRegistration.java830
-rw-r--r--dicore3/command/src/main/kotlin/io/dico/dicore/command/registration/reflect/KotlinReflectiveRegistration.kt132
-rw-r--r--dicore3/command/src/test/java/io/dico/dicore/command/example/ParameterInfoObjectExample.java146
-rw-r--r--dicore3/core/src/main/java/io/dico/dicore/BitModifier.java364
-rw-r--r--dicore3/core/src/main/java/io/dico/dicore/Formatting.java588
-rw-r--r--dicore3/core/src/main/java/io/dico/dicore/InterfaceChain.java346
-rw-r--r--dicore3/core/src/main/java/io/dico/dicore/InventoryEventUtil.java240
-rw-r--r--dicore3/core/src/main/java/io/dico/dicore/Logging.java232
-rw-r--r--dicore3/core/src/main/java/io/dico/dicore/Reflection.java1560
-rw-r--r--dicore3/core/src/main/java/io/dico/dicore/SetBasedWhitelist.java118
-rw-r--r--dicore3/core/src/main/java/io/dico/dicore/SpigotUtil.java816
-rw-r--r--dicore3/core/src/main/java/io/dico/dicore/StringUtil.java946
-rw-r--r--dicore3/core/src/main/java/io/dico/dicore/Whitelist.java182
-rw-r--r--dicore3/core/src/main/java/io/dico/dicore/event/ChainedListener.java92
-rw-r--r--dicore3/core/src/main/java/io/dico/dicore/event/ChainedListenerHandle.java110
-rw-r--r--dicore3/core/src/main/java/io/dico/dicore/event/ChainedListenerHandles.java126
-rw-r--r--dicore3/core/src/main/java/io/dico/dicore/event/ChainedListeners.java112
-rw-r--r--dicore3/core/src/main/java/io/dico/dicore/event/HandlerList.java182
-rw-r--r--dicore3/core/src/main/java/io/dico/dicore/event/Listener.java34
-rw-r--r--dicore3/core/src/main/java/io/dico/dicore/event/ListenerHandle.java18
-rw-r--r--dicore3/core/src/main/java/io/dico/dicore/event/SimpleListener.java14
-rw-r--r--dicore3/core/src/main/java/io/dico/dicore/exceptions/ExceptionHandler.java406
-rw-r--r--dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedBiConsumer.java138
-rw-r--r--dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedBiFunction.java152
-rw-r--r--dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedConsumer.java134
-rw-r--r--dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedFunction.java148
-rw-r--r--dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedFunctionalObject.java170
-rw-r--r--dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedRunnable.java124
-rw-r--r--dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedSupplier.java140
-rw-r--r--dicore3/core/src/main/java/io/dico/dicore/task/BaseTask.java216
-rw-r--r--dicore3/core/src/main/java/io/dico/dicore/task/IteratorTask.java240
-rw-r--r--dicore3/core/src/main/java/io/dico/dicore/task/VoidTask.java38
-rw-r--r--gradle/wrapper/gradle-wrapper.properties10
-rw-r--r--gradlew344
-rw-r--r--gradlew.bat168
-rw-r--r--javaStyle.xml76
-rw-r--r--settings.gradle.kts3
-rw-r--r--src/main/kotlin/io/dico/parcels2/Interactable.kt346
-rw-r--r--src/main/kotlin/io/dico/parcels2/JobDispatcher.kt674
-rw-r--r--src/main/kotlin/io/dico/parcels2/Parcel.kt132
-rw-r--r--src/main/kotlin/io/dico/parcels2/ParcelGenerator.kt206
-rw-r--r--src/main/kotlin/io/dico/parcels2/ParcelId.kt112
-rw-r--r--src/main/kotlin/io/dico/parcels2/ParcelWorld.kt206
-rw-r--r--src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt304
-rw-r--r--src/main/kotlin/io/dico/parcels2/PlayerProfile.kt366
-rw-r--r--src/main/kotlin/io/dico/parcels2/Privilege.kt250
-rw-r--r--src/main/kotlin/io/dico/parcels2/Privileges.kt128
-rw-r--r--src/main/kotlin/io/dico/parcels2/blockvisitor/Attachables.kt122
-rw-r--r--src/main/kotlin/io/dico/parcels2/blockvisitor/ExtraBlockChange.kt76
-rw-r--r--src/main/kotlin/io/dico/parcels2/blockvisitor/RegionTraverser.kt646
-rw-r--r--src/main/kotlin/io/dico/parcels2/blockvisitor/Schematic.kt242
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/AbstractParcelCommands.kt126
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/CommandsAdmin.kt188
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/CommandsAdminPrivilegesGlobal.kt262
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/CommandsDebug.kt322
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt282
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/CommandsPrivilegesGlobal.kt152
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/CommandsPrivilegesLocal.kt286
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/ParcelCommandBuilder.kt274
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/ParcelCommandReceivers.kt136
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/ParcelOptionsInteractCommand.kt112
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/ParcelParameterTypes.kt166
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt382
-rw-r--r--src/main/kotlin/io/dico/parcels2/command/ParcelsChatHandler.kt46
-rw-r--r--src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelContainer.kt144
-rw-r--r--src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt754
-rw-r--r--src/main/kotlin/io/dico/parcels2/defaultimpl/GlobalPrivilegesManagerImpl.kt54
-rw-r--r--src/main/kotlin/io/dico/parcels2/defaultimpl/InfoBuilder.kt174
-rw-r--r--src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt444
-rw-r--r--src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt150
-rw-r--r--src/main/kotlin/io/dico/parcels2/listener/ParcelEntityTracker.kt120
-rw-r--r--src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt1320
-rw-r--r--src/main/kotlin/io/dico/parcels2/listener/WorldEditListener.kt156
-rw-r--r--src/main/kotlin/io/dico/parcels2/options/GeneratorOptions.kt70
-rw-r--r--src/main/kotlin/io/dico/parcels2/options/MigrationOptions.kt42
-rw-r--r--src/main/kotlin/io/dico/parcels2/options/Options.kt114
-rw-r--r--src/main/kotlin/io/dico/parcels2/options/OptionsMapper.kt132
-rw-r--r--src/main/kotlin/io/dico/parcels2/options/PolymorphicOptions.kt176
-rw-r--r--src/main/kotlin/io/dico/parcels2/options/StorageOptions.kt116
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/Backing.kt138
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/DataConverters.kt74
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/Hikari.kt146
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/Storage.kt228
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt564
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedExtensions.kt148
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/exposed/IdTables.kt328
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/exposed/ListTables.kt222
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/migration/Migration.kt18
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeMigration.kt234
-rw-r--r--src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeTables.kt62
-rw-r--r--src/main/kotlin/io/dico/parcels2/util/BukkitUtil.kt28
-rw-r--r--src/main/kotlin/io/dico/parcels2/util/MainThreadDispatcher.kt84
-rw-r--r--src/main/kotlin/io/dico/parcels2/util/PluginScheduler.kt40
-rw-r--r--src/main/kotlin/io/dico/parcels2/util/ext/Material.kt214
-rw-r--r--src/main/kotlin/io/dico/parcels2/util/ext/Misc.kt162
-rw-r--r--src/main/kotlin/io/dico/parcels2/util/ext/Player.kt114
-rw-r--r--src/main/kotlin/io/dico/parcels2/util/math/Dimension.kt36
-rw-r--r--src/main/kotlin/io/dico/parcels2/util/math/Math.kt82
-rw-r--r--src/main/kotlin/io/dico/parcels2/util/math/Region.kt72
-rw-r--r--src/main/kotlin/io/dico/parcels2/util/math/Vec2i.kt18
-rw-r--r--src/main/kotlin/io/dico/parcels2/util/math/Vec3d.kt104
-rw-r--r--src/main/kotlin/io/dico/parcels2/util/math/Vec3i.kt210
-rw-r--r--src/main/resources/logback.xml32
-rw-r--r--src/main/resources/plugin.yml10
-rw-r--r--todo.md206
184 files changed, 17998 insertions, 17998 deletions
diff --git a/.editorconfig b/.editorconfig
index 32f5e31..169357e 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,19 +1,19 @@
-[*]
-charset=utf-8
-end_of_line=lf
-insert_final_newline=false
-indent_style=space
-indent_size=4
-
-[{.babelrc,.stylelintrc,jest.config,.eslintrc,*.bowerrc,*.jsb3,*.jsb2,*.json}]
-indent_style=space
-indent_size=2
-
-[{*.ddl,*.sql}]
-indent_style=space
-indent_size=2
-
-[{*.yml,*.yaml}]
-indent_style=space
-indent_size=2
-
+[*]
+charset=utf-8
+end_of_line=lf
+insert_final_newline=false
+indent_style=space
+indent_size=4
+
+[{.babelrc,.stylelintrc,jest.config,.eslintrc,*.bowerrc,*.jsb3,*.jsb2,*.json}]
+indent_style=space
+indent_size=2
+
+[{*.ddl,*.sql}]
+indent_style=space
+indent_size=2
+
+[{*.yml,*.yaml}]
+indent_style=space
+indent_size=2
+
diff --git a/.gitignore b/.gitignore
index 601cc7d..60f7508 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,4 +6,5 @@ build/
/debug/
target/
/gradle-output.txt
-/*.java \ No newline at end of file
+/*.java
+*.dump \ No newline at end of file
diff --git a/README.md b/README.md
index e5aa074..dfebe9c 100644
--- a/README.md
+++ b/README.md
@@ -1,16 +1,16 @@
-# Parcels2
-
-Plot management and world generator plugin inspired by [PlotMe](https://github.com/WorldCretornica/PlotMe-Core).
-
-Newer version of discontinued [Parcels](https://github.com/RedstonerServer/Parcels).
-
-Written in Kotlin.
-This project is WIP.
-
-![world screenshot](https://i.imgur.com/tpbKrQI.png)
-
-## Build
-
-1. Add `worldedit-bukkit-7.0.0-beta-01.jar` to `/debug/plugins` directory
-2. Run `gradle releaseJar`
-3. Kotlin stdlib classpath is placed in `/debug/lib` directory and artifact can be found in `/debug/plugins` directory
+# Parcels2
+
+Plot management and world generator plugin inspired by [PlotMe](https://github.com/WorldCretornica/PlotMe-Core).
+
+Newer version of discontinued [Parcels](https://github.com/RedstonerServer/Parcels).
+
+Written in Kotlin.
+This project is WIP.
+
+![world screenshot](https://i.imgur.com/tpbKrQI.png)
+
+## Build
+
+1. Add `worldedit-bukkit-7.0.0-beta-01.jar` to `/debug/plugins` directory
+2. Run `gradle releaseJar`
+3. Kotlin stdlib classpath is placed in `/debug/lib` directory and artifact can be found in `/debug/plugins` directory
diff --git a/build.gradle.kts b/build.gradle.kts
index 12c2d97..211a978 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -13,22 +13,22 @@ version = "0.2"
plugins {
java
- kotlin("jvm") version "1.3.0-rc-57"
+ kotlin("jvm") version "1.3.0-rc-146"
id("com.github.johnrengelman.plugin-shadow") version "2.0.3"
}
-
allprojects {
apply<JavaPlugin>()
apply(plugin = "idea")
repositories {
mavenCentral()
- maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots")
- maven("https://hub.spigotmc.org/nexus/content/repositories/sonatype-nexus-snapshots")
- maven("https://dl.bintray.com/kotlin/exposed")
- maven("https://dl.bintray.com/kotlin/kotlin-eap")
+ maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/")
+ maven("https://hub.spigotmc.org/nexus/content/repositories/sonatype-nexus-snapshots/")
+ maven("https://dl.bintray.com/kotlin/exposed/")
+ maven("https://dl.bintray.com/kotlin/kotlin-dev/")
+ maven("https://dl.bintray.com/kotlin/kotlin-eap/")
maven("https://dl.bintray.com/kotlin/kotlinx/")
}
@@ -146,11 +146,6 @@ tasks {
val jarUrl = URL("https://yivesmirror.com/files/spigot/spigot-latest.jar")
val serverJarFile = file("$serverDir/lib/spigot.jar")
-
-
- doFirst {
-
- }
}
}
@@ -171,5 +166,8 @@ val ConfigurationContainer.`provided`: Configuration
val ConfigurationContainer.`kotlinStd`: Configuration
get() = findByName("kotlinStd") ?: create("kotlinStd").let { compileClasspath.extendsFrom(it) }
-fun Jar.fromFiles(files: Iterable<File>) =
+fun Jar.fromFiles(files: Iterable<File>) {
+ return
afterEvaluate { from(*files.map { if (it.isDirectory) it else zipTree(it) }.toTypedArray()) }
+}
+
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/ChildCommandAddress.java b/dicore3/command/src/main/java/io/dico/dicore/command/ChildCommandAddress.java
index 9a26f61..72bc303 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/ChildCommandAddress.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/ChildCommandAddress.java
@@ -1,120 +1,120 @@
-package io.dico.dicore.command;
-
-import io.dico.dicore.command.predef.DefaultGroupCommand;
-import io.dico.dicore.command.predef.HelpCommand;
-
-import java.util.*;
-
-public class ChildCommandAddress extends ModifiableCommandAddress {
- ModifiableCommandAddress parent;
- final List<String> namesModifiable = new ArrayList<>(4);
- List<String> names = namesModifiable;
- Command command;
- boolean isCommandTrailing;
-
- public ChildCommandAddress() {
- }
-
- public ChildCommandAddress(Command command) {
- this.command = command;
- }
-
- public ChildCommandAddress(Command command, String name, String... aliases) {
- this(command);
- addNameAndAliases(name, aliases);
- }
-
- public static ChildCommandAddress newPlaceHolderCommand(String name, String... aliases) {
- ChildCommandAddress rv = new ChildCommandAddress();
- rv.setupAsPlaceholder(name, aliases);
- return rv;
- }
-
- public void setupAsPlaceholder(String name, String... aliases) {
- if (!hasCommand()) {
- setCommand(DefaultGroupCommand.getInstance());
- }
-
- addNameAndAliases(name, aliases);
- HelpCommand.registerAsChild(this);
- }
-
- @Override
- public boolean isRoot() {
- return false;
- }
-
- @Override
- public ModifiableCommandAddress getParent() {
- return parent;
- }
-
- @Override
- public Command getCommand() {
- return command;
- }
-
- @Override
- public void setCommand(Command command) {
- if (hasUserDeclaredCommand()) {
- throw new IllegalStateException("Command is already set at address \"" + getAddress() + "\"");
- }
- this.command = command;
- }
-
- @Override
- public List<String> getNames() {
- return names;
- }
-
- public void addNameAndAliases(String name, String... aliases) {
- names.add(name);
- names.addAll(Arrays.asList(aliases));
- }
-
- @Override
- public String getMainKey() {
- return namesModifiable.isEmpty() ? null : namesModifiable.get(0);
- }
-
- @Override
- public String getAddress() {
- ICommandAddress address = this;
- int depth = getDepth();
- String[] keys = new String[depth];
- for (int i = depth - 1; i >= 0; i--) {
- keys[i] = address.getMainKey();
- address = address.getParent();
- }
- return String.join(" ", keys);
- }
-
- public void finalizeNames() {
- if (names == namesModifiable) {
- names = Collections.unmodifiableList(namesModifiable);
- }
- }
-
- Iterator<String> modifiableNamesIterator() {
- return namesModifiable.iterator();
- }
-
- void setParent(ModifiableCommandAddress parent) {
- finalizeNames();
- this.parent = parent;
- }
-
- @Override
- public boolean isCommandTrailing() {
- return isCommandTrailing;
- }
-
- @Override
- public void setCommandTrailing(boolean trailing) {
- if (hasChildren()) {
- throw new IllegalStateException("Address already has children, this property can't be modified");
- }
- isCommandTrailing = trailing;
- }
-
-}
+package io.dico.dicore.command;
+
+import io.dico.dicore.command.predef.DefaultGroupCommand;
+import io.dico.dicore.command.predef.HelpCommand;
+
+import java.util.*;
+
+public class ChildCommandAddress extends ModifiableCommandAddress {
+ ModifiableCommandAddress parent;
+ final List<String> namesModifiable = new ArrayList<>(4);
+ List<String> names = namesModifiable;
+ Command command;
+ boolean isCommandTrailing;
+
+ public ChildCommandAddress() {
+ }
+
+ public ChildCommandAddress(Command command) {
+ this.command = command;
+ }
+
+ public ChildCommandAddress(Command command, String name, String... aliases) {
+ this(command);
+ addNameAndAliases(name, aliases);
+ }
+
+ public static ChildCommandAddress newPlaceHolderCommand(String name, String... aliases) {
+ ChildCommandAddress rv = new ChildCommandAddress();
+ rv.setupAsPlaceholder(name, aliases);
+ return rv;
+ }
+
+ public void setupAsPlaceholder(String name, String... aliases) {
+ if (!hasCommand()) {
+ setCommand(DefaultGroupCommand.getInstance());
+ }
+
+ addNameAndAliases(name, aliases);
+ HelpCommand.registerAsChild(this);
+ }
+
+ @Override
+ public boolean isRoot() {
+ return false;
+ }
+
+ @Override
+ public ModifiableCommandAddress getParent() {
+ return parent;
+ }
+
+ @Override
+ public Command getCommand() {
+ return command;
+ }
+
+ @Override
+ public void setCommand(Command command) {
+ if (hasUserDeclaredCommand()) {
+ throw new IllegalStateException("Command is already set at address \"" + getAddress() + "\"");
+ }
+ this.command = command;
+ }
+
+ @Override
+ public List<String> getNames() {
+ return names;
+ }
+
+ public void addNameAndAliases(String name, String... aliases) {
+ names.add(name);
+ names.addAll(Arrays.asList(aliases));
+ }
+
+ @Override
+ public String getMainKey() {
+ return namesModifiable.isEmpty() ? null : namesModifiable.get(0);
+ }
+
+ @Override
+ public String getAddress() {
+ ICommandAddress address = this;
+ int depth = getDepth();
+ String[] keys = new String[depth];
+ for (int i = depth - 1; i >= 0; i--) {
+ keys[i] = address.getMainKey();
+ address = address.getParent();
+ }
+ return String.join(" ", keys);
+ }
+
+ public void finalizeNames() {
+ if (names == namesModifiable) {
+ names = Collections.unmodifiableList(namesModifiable);
+ }
+ }
+
+ Iterator<String> modifiableNamesIterator() {
+ return namesModifiable.iterator();
+ }
+
+ void setParent(ModifiableCommandAddress parent) {
+ finalizeNames();
+ this.parent = parent;
+ }
+
+ @Override
+ public boolean isCommandTrailing() {
+ return isCommandTrailing;
+ }
+
+ @Override
+ public void setCommandTrailing(boolean trailing) {
+ if (hasChildren()) {
+ throw new IllegalStateException("Address already has children, this property can't be modified");
+ }
+ isCommandTrailing = trailing;
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/Command.java b/dicore3/command/src/main/java/io/dico/dicore/command/Command.java
index 894e74e..97f4b78 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/Command.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/Command.java
@@ -1,158 +1,158 @@
-package io.dico.dicore.command;
-
-import io.dico.dicore.command.IContextFilter.Priority;
-import io.dico.dicore.command.parameter.ArgumentBuffer;
-import io.dico.dicore.command.parameter.IArgumentPreProcessor;
-import io.dico.dicore.command.parameter.Parameter;
-import io.dico.dicore.command.parameter.ParameterList;
-import io.dico.dicore.command.parameter.type.ParameterType;
-import org.bukkit.Location;
-import org.bukkit.command.CommandSender;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-
-public abstract class Command {
- private static final String[] EMPTY_DESCRIPTION = new String[0];
- private final ParameterList parameterList = new ParameterList();
- private final List<IContextFilter> contextFilters = new ArrayList<>(3);
- private String[] description = EMPTY_DESCRIPTION;
- private String shortDescription;
-
- public Command addParameter(Parameter<?, ?> parameter) {
- parameterList.addParameter(parameter);
- return this;
- }
-
- public <TType> Command addParameter(String name, String description, ParameterType<TType, Void> type) {
- return addParameter(new Parameter<>(name, description, type, null, false, null));
- }
-
- public <TType, TParamInfo> Command addParameter(String name, String description, ParameterType<TType, TParamInfo> type, TParamInfo paramInfo) {
- return addParameter(new Parameter<>(name, description, type, paramInfo, false, null));
- }
-
- public <TType> Command addFlag(String name, String description, ParameterType<TType, Void> type) {
- return addParameter(new Parameter<>('-' + name, description, type, null, true, null));
- }
-
- public <TType, TParamInfo> Command addFlag(String name, String description, ParameterType<TType, TParamInfo> type, TParamInfo paramInfo) {
- return addParameter(new Parameter<>('-' + name, description, type, paramInfo, true, null));
- }
-
- public <TType> Command addAuthorizedFlag(String name, String description, ParameterType<TType, Void> type, String permission) {
- return addParameter(new Parameter<>('-' + name, description, type, null, true, permission));
- }
-
- public <TType, TParamInfo> Command addAuthorizedFlag(String name, String description, ParameterType<TType, TParamInfo> type, TParamInfo paramInfo, String permission) {
- return addParameter(new Parameter<>('-' + name, description, type, paramInfo, true, permission));
- }
-
- public Command requiredParameters(int requiredParameters) {
- parameterList.setRequiredCount(requiredParameters);
- return this;
- }
-
- public Command repeatFinalParameter() {
- parameterList.setRepeatFinalParameter(true);
- return this;
- }
-
- public Command setDescription(String... description) {
- this.description = Objects.requireNonNull(description);
- return this;
- }
-
- public Command setShortDescription(String shortDescription) {
- this.shortDescription = shortDescription;
- return this;
- }
-
- /*
- public Command preprocessArguments(IArgumentPreProcessor processor) {
- parameterList.setArgumentPreProcessor(processor);
- return this;
- }*/
-
- public final ParameterList getParameterList() {
- return parameterList;
- }
-
- public final String[] getDescription() {
- return description.length == 0 ? description : description.clone();
- }
-
- public String getShortDescription() {
- return shortDescription;
- }
-
- /**
- * ---- CONTEXT FILTERS ----
- * Filter the contexts. For example, if the sender must be a player but it's the console,
- * throw a CommandException describing the problem.
- */
- private transient int postParameterFilterCount = 0;
-
- public Command addContextFilter(IContextFilter contextFilter) {
- Objects.requireNonNull(contextFilter);
- if (!contextFilters.contains(contextFilter)) {
- contextFilters.add(contextFilter);
- contextFilters.sort(null);
-
- if (contextFilter.getPriority().compareTo(Priority.POST_PARAMETERS) >= 0) {
- postParameterFilterCount++;
- }
- }
- return this;
- }
-
- public List<IContextFilter> getContextFilters() {
- return Collections.unmodifiableList(contextFilters);
- }
-
- public Command removeContextFilter(IContextFilter contextFilter) {
- boolean ret = contextFilters.remove(contextFilter);
- if (ret) {
- if (contextFilter.getPriority().compareTo(Priority.POST_PARAMETERS) >= 0) {
- postParameterFilterCount--;
- }
- }
- return this;
- }
-
- // ---- CONTROL FLOW IN COMMAND TREES ----
-
- public boolean isVisibleTo(CommandSender sender) {
- return true;
- }
-
- public boolean takePrecedenceOverSubcommand(String subCommand, ArgumentBuffer buffer) {
- return false;
- }
-
- // ---- EXECUTION ----
-
- public void initializeAndFilterContext(ExecutionContext context) throws CommandException {
- int i, n;
- for (i = 0, n = contextFilters.size() - postParameterFilterCount; i < n; i++) {
- contextFilters.get(i).filterContext(context);
- }
-
- context.parse(parameterList);
-
- if (!context.isTabComplete()) {
- for (n = contextFilters.size(); i < n; i++) {
- contextFilters.get(i).filterContext(context);
- }
- }
- }
-
- public abstract String execute(CommandSender sender, ExecutionContext context) throws CommandException;
-
- public List<String> tabComplete(CommandSender sender, ExecutionContext context, Location location) {
- return context.getSuggestedCompletions(location);
- }
-
-}
+package io.dico.dicore.command;
+
+import io.dico.dicore.command.IContextFilter.Priority;
+import io.dico.dicore.command.parameter.ArgumentBuffer;
+import io.dico.dicore.command.parameter.IArgumentPreProcessor;
+import io.dico.dicore.command.parameter.Parameter;
+import io.dico.dicore.command.parameter.ParameterList;
+import io.dico.dicore.command.parameter.type.ParameterType;
+import org.bukkit.Location;
+import org.bukkit.command.CommandSender;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+public abstract class Command {
+ private static final String[] EMPTY_DESCRIPTION = new String[0];
+ private final ParameterList parameterList = new ParameterList();
+ private final List<IContextFilter> contextFilters = new ArrayList<>(3);
+ private String[] description = EMPTY_DESCRIPTION;
+ private String shortDescription;
+
+ public Command addParameter(Parameter<?, ?> parameter) {
+ parameterList.addParameter(parameter);
+ return this;
+ }
+
+ public <TType> Command addParameter(String name, String description, ParameterType<TType, Void> type) {
+ return addParameter(new Parameter<>(name, description, type, null, false, null));
+ }
+
+ public <TType, TParamInfo> Command addParameter(String name, String description, ParameterType<TType, TParamInfo> type, TParamInfo paramInfo) {
+ return addParameter(new Parameter<>(name, description, type, paramInfo, false, null));
+ }
+
+ public <TType> Command addFlag(String name, String description, ParameterType<TType, Void> type) {
+ return addParameter(new Parameter<>('-' + name, description, type, null, true, null));
+ }
+
+ public <TType, TParamInfo> Command addFlag(String name, String description, ParameterType<TType, TParamInfo> type, TParamInfo paramInfo) {
+ return addParameter(new Parameter<>('-' + name, description, type, paramInfo, true, null));
+ }
+
+ public <TType> Command addAuthorizedFlag(String name, String description, ParameterType<TType, Void> type, String permission) {
+ return addParameter(new Parameter<>('-' + name, description, type, null, true, permission));
+ }
+
+ public <TType, TParamInfo> Command addAuthorizedFlag(String name, String description, ParameterType<TType, TParamInfo> type, TParamInfo paramInfo, String permission) {
+ return addParameter(new Parameter<>('-' + name, description, type, paramInfo, true, permission));
+ }
+
+ public Command requiredParameters(int requiredParameters) {
+ parameterList.setRequiredCount(requiredParameters);
+ return this;
+ }
+
+ public Command repeatFinalParameter() {
+ parameterList.setRepeatFinalParameter(true);
+ return this;
+ }
+
+ public Command setDescription(String... description) {
+ this.description = Objects.requireNonNull(description);
+ return this;
+ }
+
+ public Command setShortDescription(String shortDescription) {
+ this.shortDescription = shortDescription;
+ return this;
+ }
+
+ /*
+ public Command preprocessArguments(IArgumentPreProcessor processor) {
+ parameterList.setArgumentPreProcessor(processor);
+ return this;
+ }*/
+
+ public final ParameterList getParameterList() {
+ return parameterList;
+ }
+
+ public final String[] getDescription() {
+ return description.length == 0 ? description : description.clone();
+ }
+
+ public String getShortDescription() {
+ return shortDescription;
+ }
+
+ /**
+ * ---- CONTEXT FILTERS ----
+ * Filter the contexts. For example, if the sender must be a player but it's the console,
+ * throw a CommandException describing the problem.
+ */
+ private transient int postParameterFilterCount = 0;
+
+ public Command addContextFilter(IContextFilter contextFilter) {
+ Objects.requireNonNull(contextFilter);
+ if (!contextFilters.contains(contextFilter)) {
+ contextFilters.add(contextFilter);
+ contextFilters.sort(null);
+
+ if (contextFilter.getPriority().compareTo(Priority.POST_PARAMETERS) >= 0) {
+ postParameterFilterCount++;
+ }
+ }
+ return this;
+ }
+
+ public List<IContextFilter> getContextFilters() {
+ return Collections.unmodifiableList(contextFilters);
+ }
+
+ public Command removeContextFilter(IContextFilter contextFilter) {
+ boolean ret = contextFilters.remove(contextFilter);
+ if (ret) {
+ if (contextFilter.getPriority().compareTo(Priority.POST_PARAMETERS) >= 0) {
+ postParameterFilterCount--;
+ }
+ }
+ return this;
+ }
+
+ // ---- CONTROL FLOW IN COMMAND TREES ----
+
+ public boolean isVisibleTo(CommandSender sender) {
+ return true;
+ }
+
+ public boolean takePrecedenceOverSubcommand(String subCommand, ArgumentBuffer buffer) {
+ return false;
+ }
+
+ // ---- EXECUTION ----
+
+ public void initializeAndFilterContext(ExecutionContext context) throws CommandException {
+ int i, n;
+ for (i = 0, n = contextFilters.size() - postParameterFilterCount; i < n; i++) {
+ contextFilters.get(i).filterContext(context);
+ }
+
+ context.parse(parameterList);
+
+ if (!context.isTabComplete()) {
+ for (n = contextFilters.size(); i < n; i++) {
+ contextFilters.get(i).filterContext(context);
+ }
+ }
+ }
+
+ public abstract String execute(CommandSender sender, ExecutionContext context) throws CommandException;
+
+ public List<String> tabComplete(CommandSender sender, ExecutionContext context, Location location) {
+ return context.getSuggestedCompletions(location);
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/CommandBuilder.java b/dicore3/command/src/main/java/io/dico/dicore/command/CommandBuilder.java
index e527f27..118dd0f 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/CommandBuilder.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/CommandBuilder.java
@@ -1,432 +1,432 @@
-package io.dico.dicore.command;
-
-import io.dico.dicore.command.chat.IChatHandler;
-import io.dico.dicore.command.parameter.type.IParameterTypeSelector;
-import io.dico.dicore.command.parameter.type.MapBasedParameterTypeSelector;
-import io.dico.dicore.command.parameter.type.ParameterType;
-import io.dico.dicore.command.predef.HelpCommand;
-import io.dico.dicore.command.predef.PredefinedCommand;
-import io.dico.dicore.command.predef.SyntaxCommand;
-import io.dico.dicore.command.registration.reflect.ReflectiveRegistration;
-
-import java.util.HashSet;
-import java.util.Objects;
-import java.util.function.Consumer;
-
-/**
- * Mimic of WorldEdit's CommandGraph
- */
-public final class CommandBuilder {
- private final RootCommandAddress root;
- private ModifiableCommandAddress cur;
- private IParameterTypeSelector selector;
-
- /**
- * Instantiate a new CommandBuilder with a new command root system
- * Commands registered to this command builder might interfere with
- * commands registered to other commands builders or by other plugins.
- */
- public CommandBuilder() {
- this(new RootCommandAddress());
- }
-
- /**
- * Instantiate a new CommandBuilder with a specified root address.
- * If the root address is identical to that of another command builder,
- * they will modify the same tree.
- *
- * @param root the root address
- */
- public CommandBuilder(RootCommandAddress root) {
- this.root = Objects.requireNonNull(root);
- this.cur = root;
- this.selector = new MapBasedParameterTypeSelector(true);
- }
-
- /**
- * Add a sub command at the current address
- * The current address can be inspected using {@link #getAddress()}
- *
- * @param name the name of the command
- * @param command the command executor
- * @param aliases any aliases
- * @return this
- */
- public CommandBuilder addSubCommand(String name, Command command, String... aliases) {
- ChildCommandAddress address = new ChildCommandAddress(command);
- address.addNameAndAliases(name, aliases);
- return addSubCommand(address);
- }
-
- /**
- * Add a subcommand as an address at the current address
- * The result of this call is the same as
- * {@code addSubCommand(address.getMainKey(), address.getCommand(), address.getNames().sublist(1).toArray(new String[0]))}
- *
- * @param address the address
- * @return this
- * @throws IllegalArgumentException if {@code address.isRoot()}
- */
- public CommandBuilder addSubCommand(ICommandAddress address) {
- cur.addChild(address);
- return this;
- }
-
- /**
- * Search the given class for any (static) methods using command annotations
- * The class gets a localized parameter type selector if it defines parameter types.
- * Any commands found are registered as sub commands to the current address.
- *
- * @param clazz the clazz
- * @return this
- * @throws IllegalArgumentException if an exception occurs while parsing the methods of this class
- * @see #registerCommands(Class, Object)
- */
- public CommandBuilder registerCommands(Class<?> clazz) {
- return registerCommands(clazz, null);
- }
-
- /**
- * Search the given object's class for methods using command annotations.
- * If the object is null, only static methods are checked. Otherwise, instance methods are also checked.
- * The class gets a localized parameter type selector if it defines parameter types.
- * Any commands found are registered as sub commands to the current address.
- *
- * @param object the object
- * @return this
- * @throws IllegalArgumentException if an exception occurs while parsing the methods of this class
- * @see #registerCommands(Class, Object)
- */
- public CommandBuilder registerCommands(Object object) {
- return registerCommands(object.getClass(), object);
- }
-
- /**
- * Search the given class for methods using command annotations.
- * The class gets a localized parameter type selector if it defines parameter types.
- * Any commands found are registered as sub commands to the current address.
- * The instance is used to invoke non-static methods.
- *
- * @param clazz the class
- * @param instance the instance, null if only static methods
- * @return this
- * @throws IllegalArgumentException if instance is not null and it's not an instance of the class
- * @throws IllegalArgumentException if another exception occurs while parsing the methods of this class
- */
- public CommandBuilder registerCommands(Class<?> clazz, Object instance) {
- try {
- ReflectiveRegistration.parseCommandGroup(cur, selector, clazz, instance);
- return this;
- } catch (Exception ex) {
- throw new IllegalArgumentException(ex);
- }
- }
-
- /**
- * register the {@link HelpCommand} as a sub command at the current address
- *
- * @return this
- */
- public CommandBuilder registerHelpCommand() {
- HelpCommand.registerAsChild(cur);
- return this;
- }
-
- /**
- * register the {@link SyntaxCommand} as a sub command a the current address
- *
- * @return this
- */
- public CommandBuilder registerSyntaxCommand() {
- SyntaxCommand.registerAsChild(cur);
- return this;
- }
-
- /**
- * Generate the predefined commands.
- * These are presets.
- * Examples include {@code help} and {@code syntax}.
- * <p>
- * Predefined commands can be registered through {@link PredefinedCommand#registerPredefinedCommandGenerator(String, Consumer)}
- *
- * @param commands the commands
- * @return this
- */
- public CommandBuilder generatePredefinedCommands(String... commands) {
- ReflectiveRegistration.generateCommands(cur, commands);
- return this;
- }
-
- /**
- * Unregister any childs present at the given keys.
- * <p>
- * This method can be used to remove unwanted keys, that might have been added
- * outside of your control. For example, because you didn't want all commands
- * registered by {@link #registerCommands(Class, Object)}, or because you didn't
- * want the help command registered by {@link #group(String, String...)}
- *
- * @param removeAliases true if any aliases of the children present at the keys should be removed
- * @param keys a varargs array containing the keys
- * @return this
- * @throws IllegalArgumentException if keys array is empty
- */
- public CommandBuilder unregisterCommands(boolean removeAliases, String... keys) {
- cur.removeChildren(removeAliases, keys);
- return this;
- }
-
- /**
- * Jump to the sub-address with the given name as main key.
- * If an address with the exact name as main key exists,
- * that address becomes the current address.
- * <p>
- * Otherwise, a new addresses is registered with the name and aliases.
- * New addresses registered by this command have a HelpCommand added by default.
- * <p>
- * After this call, any registered commands are registered as a sub command
- * to the new address. To restore the previous state, a call to {@link #parent()}
- * should be made.
- * <p>
- * If the address is the target of a command, it will provide information about its sub commands
- * using the HelpCommand.
- *
- * @param name the main key
- * @param aliases the aliases
- * @return this
- */
- public CommandBuilder group(String name, String... aliases) {
- ChildCommandAddress address = cur.getChild(name);
- if (address == null || !name.equals(address.getMainKey())) {
- address = new ChildCommandAddress();
- address.setupAsPlaceholder(name, aliases);
- cur.addChild(address);
- }
- cur = address;
- return this;
- }
-
- /**
- * Similar to {@link #group(String, String[])} but this will force overwrite any present group,
- * using the address passed. The address MUST be an instance of {@link ChildCommandAddress}.
- *
- * <p>The address must not have a parent or any keys</p>
- *
- * @param address the address object to use
- * @param name the main key
- * @param aliases any aliases
- * @return this
- * @throws IllegalArgumentException if any of the requirements set out above aren't met
- */
- public CommandBuilder group(ICommandAddress address, String name, String... aliases) {
- if (address.hasParent() || address.getMainKey() != null || !(address instanceof ChildCommandAddress)) {
- throw new IllegalArgumentException();
- }
-
- ChildCommandAddress asChild = (ChildCommandAddress) address;
- asChild.setupAsPlaceholder(name, aliases);
- cur.addChild(address);
- cur = asChild;
- return this;
- }
-
- /**
- * Sets the description of a group created by {@link #group(String, String...)}
- * Can be called subsequently to making a call to {@link #group(String, String...)}
- *
- * @param shortDescription a short description
- * @param description the lines of a full description.
- * @return this
- * @throws IllegalStateException if the current group has no command
- */
- public CommandBuilder setGroupDescription(String shortDescription, String... description) {
- Command command = cur.getCommand();
- if (command == null) throw new IllegalStateException();
- cur.setCommand(command
- .setShortDescription(shortDescription)
- .setDescription(description));
- return this;
- }
-
- /**
- * Add a context filter to the command of the current group
- * @return this
- * @throws IllegalStateException if the current group has no command
- */
- public CommandBuilder addContextFilter(IContextFilter contextFilter) {
- Command command = cur.getCommand();
- if (command == null) throw new IllegalStateException();
- cur.setCommand(command
- .addContextFilter(contextFilter));
- return this;
- }
-
- /**
- * Add a required permission to the command of the current group
- * @return this
- * @throws IllegalStateException if the current group has no command
- */
- public CommandBuilder addPermission(String permission) {
- return addContextFilter(IContextFilter.permission(permission));
- }
-
- /**
- * Add a required permission to the command of the current group, which can be inherited
- * @return this
- * @throws IllegalStateException if the current group has no command
- */
- public CommandBuilder addInheritablePermission(String permission) {
- return addContextFilter(IContextFilter.inheritablePermission(permission));
- }
-
- /**
- * Jump up a level in the address
- *
- * @return this
- * @throws IllegalStateException if the address is empty
- * // has a depth of 0 // is at level 0
- */
- public CommandBuilder parent() {
- if (cur.hasParent()) {
- cur = cur.getParent();
- return this;
- }
- throw new IllegalStateException("No parent exists at this address");
- }
-
- /**
- * Jump to the root (empty) address,
- * such that a subsequent call to {@link #parent()}
- * will throw a {@link IllegalStateException}
- *
- * @return this
- */
- public CommandBuilder root() {
- cur = root;
- return this;
- }
-
- /**
- * Get the current address, as a space-separated string
- *
- * @return the current address
- */
- public String getAddress() {
- return cur.getAddress();
- }
-
- /**
- * Get the depth of the current address.
- * This is equivalent to {@code getAddress().split(" ").length}.
- * If the address is empty, the depth is 0.
- *
- * @return the depth
- */
- public int getDepth() {
- return cur.getDepth();
- }
-
- /**
- * Set the command at the current group. The command is set
- * a level higher than it would be if this were a call to {@link #addSubCommand(String, Command, String...)}
- * <p>
- * If a call to {@link #setGroupDescription(String, String...)} was made at the same address before,
- * the description is copied to the given executor.
- *
- * @param command the executor
- * @return this
- * @throws IllegalArgumentException if the command at the address is present and declared by the user,
- * in other words, it's not a {@link PredefinedCommand}
- */
- public CommandBuilder setCommand(Command command) {
- Command current = cur.getCommand();
- if (current instanceof HelpCommand && current != HelpCommand.INSTANCE) {
- command.setShortDescription(current.getShortDescription());
- command.setDescription(current.getDescription());
- }
-
- cur.setCommand(command);
- return this;
- }
-
- /**
- * Configure the chat handler at this address. The chat handler
- * is used for all children down the tree if they don't explicitly have
- * their own chat handler configured. If this isn't configured,
- * {@code ChatHandlers.defaultChat()} is used.
- *
- * @param chatHandler the chat handler
- * @return this
- */
- public CommandBuilder setChatHandler(IChatHandler chatHandler) {
- cur.setChatHandler(chatHandler);
- return this;
- }
-
- /**
- * Add the parameter type to this builder's selector.
- *
- * @param type the type
- * @param <T> the return type of the parameter type
- * @return this
- */
- public <T> CommandBuilder addParameterType(ParameterType<T, Void> type) {
- selector.addType(false, type);
- return this;
- }
-
- /**
- * Add the parameter type to this builder's selector.
- *
- * @param infolessAlias whether to also register the type with an infoless alias.
- * this increases the priority assigned to the type if no info object is present.
- * @param type the type
- * @param <T> the return type of the parameter type
- * @param <C> the parameter config type (info object)
- * @return this
- */
-
- public <T, C> CommandBuilder addParameterType(boolean infolessAlias, ParameterType<T, C> type) {
- selector.addType(infolessAlias, type);
- return this;
- }
-
- /**
- * Get the dispatcher for the root address.
- * The dispatcher should be used to finally register all commands,
- * after they are all declared.
- *
- * @return the dispatcher
- */
- public ICommandDispatcher getDispatcher() {
- return root;
- }
-
- /**
- * Print debugging information about the current addresses and commands in this builder
- * A StackTraceElement indicating where this was called from is also included
- *
- * @return this
- */
- public CommandBuilder printDebugInformation() {
- String address = cur == root ? "<root>" : cur.getAddress();
- StackTraceElement caller = getCallsite();
-
- StringBuilder message = new StringBuilder("### CommandBuilder dump ###");
- message.append("\nCalled from ").append(caller);
- message.append("\nPosition: ").append(address);
- cur.appendDebugInformation(message, "", new HashSet<>());
-
- System.out.println(message);
- return this;
- }
-
- private static StackTraceElement getCallsite() {
- // [0] Thread.currentThread()
- // [1] CommandBuilder.getCallsite()
- // [2] Calling method
- // [3] Method calling the calling method
- StackTraceElement[] trace = Thread.currentThread().getStackTrace();
- return trace.length > 3 ? trace[3] : null;
- }
-
-}
+package io.dico.dicore.command;
+
+import io.dico.dicore.command.chat.IChatHandler;
+import io.dico.dicore.command.parameter.type.IParameterTypeSelector;
+import io.dico.dicore.command.parameter.type.MapBasedParameterTypeSelector;
+import io.dico.dicore.command.parameter.type.ParameterType;
+import io.dico.dicore.command.predef.HelpCommand;
+import io.dico.dicore.command.predef.PredefinedCommand;
+import io.dico.dicore.command.predef.SyntaxCommand;
+import io.dico.dicore.command.registration.reflect.ReflectiveRegistration;
+
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.function.Consumer;
+
+/**
+ * Mimic of WorldEdit's CommandGraph
+ */
+public final class CommandBuilder {
+ private final RootCommandAddress root;
+ private ModifiableCommandAddress cur;
+ private IParameterTypeSelector selector;
+
+ /**
+ * Instantiate a new CommandBuilder with a new command root system
+ * Commands registered to this command builder might interfere with
+ * commands registered to other commands builders or by other plugins.
+ */
+ public CommandBuilder() {
+ this(new RootCommandAddress());
+ }
+
+ /**
+ * Instantiate a new CommandBuilder with a specified root address.
+ * If the root address is identical to that of another command builder,
+ * they will modify the same tree.
+ *
+ * @param root the root address
+ */
+ public CommandBuilder(RootCommandAddress root) {
+ this.root = Objects.requireNonNull(root);
+ this.cur = root;
+ this.selector = new MapBasedParameterTypeSelector(true);
+ }
+
+ /**
+ * Add a sub command at the current address
+ * The current address can be inspected using {@link #getAddress()}
+ *
+ * @param name the name of the command
+ * @param command the command executor
+ * @param aliases any aliases
+ * @return this
+ */
+ public CommandBuilder addSubCommand(String name, Command command, String... aliases) {
+ ChildCommandAddress address = new ChildCommandAddress(command);
+ address.addNameAndAliases(name, aliases);
+ return addSubCommand(address);
+ }
+
+ /**
+ * Add a subcommand as an address at the current address
+ * The result of this call is the same as
+ * {@code addSubCommand(address.getMainKey(), address.getCommand(), address.getNames().sublist(1).toArray(new String[0]))}
+ *
+ * @param address the address
+ * @return this
+ * @throws IllegalArgumentException if {@code address.isRoot()}
+ */
+ public CommandBuilder addSubCommand(ICommandAddress address) {
+ cur.addChild(address);
+ return this;
+ }
+
+ /**
+ * Search the given class for any (static) methods using command annotations
+ * The class gets a localized parameter type selector if it defines parameter types.
+ * Any commands found are registered as sub commands to the current address.
+ *
+ * @param clazz the clazz
+ * @return this
+ * @throws IllegalArgumentException if an exception occurs while parsing the methods of this class
+ * @see #registerCommands(Class, Object)
+ */
+ public CommandBuilder registerCommands(Class<?> clazz) {
+ return registerCommands(clazz, null);
+ }
+
+ /**
+ * Search the given object's class for methods using command annotations.
+ * If the object is null, only static methods are checked. Otherwise, instance methods are also checked.
+ * The class gets a localized parameter type selector if it defines parameter types.
+ * Any commands found are registered as sub commands to the current address.
+ *
+ * @param object the object
+ * @return this
+ * @throws IllegalArgumentException if an exception occurs while parsing the methods of this class
+ * @see #registerCommands(Class, Object)
+ */
+ public CommandBuilder registerCommands(Object object) {
+ return registerCommands(object.getClass(), object);
+ }
+
+ /**
+ * Search the given class for methods using command annotations.
+ * The class gets a localized parameter type selector if it defines parameter types.
+ * Any commands found are registered as sub commands to the current address.
+ * The instance is used to invoke non-static methods.
+ *
+ * @param clazz the class
+ * @param instance the instance, null if only static methods
+ * @return this
+ * @throws IllegalArgumentException if instance is not null and it's not an instance of the class
+ * @throws IllegalArgumentException if another exception occurs while parsing the methods of this class
+ */
+ public CommandBuilder registerCommands(Class<?> clazz, Object instance) {
+ try {
+ ReflectiveRegistration.parseCommandGroup(cur, selector, clazz, instance);
+ return this;
+ } catch (Exception ex) {
+ throw new IllegalArgumentException(ex);
+ }
+ }
+
+ /**
+ * register the {@link HelpCommand} as a sub command at the current address
+ *
+ * @return this
+ */
+ public CommandBuilder registerHelpCommand() {
+ HelpCommand.registerAsChild(cur);
+ return this;
+ }
+
+ /**
+ * register the {@link SyntaxCommand} as a sub command a the current address
+ *
+ * @return this
+ */
+ public CommandBuilder registerSyntaxCommand() {
+ SyntaxCommand.registerAsChild(cur);
+ return this;
+ }
+
+ /**
+ * Generate the predefined commands.
+ * These are presets.
+ * Examples include {@code help} and {@code syntax}.
+ * <p>
+ * Predefined commands can be registered through {@link PredefinedCommand#registerPredefinedCommandGenerator(String, Consumer)}
+ *
+ * @param commands the commands
+ * @return this
+ */
+ public CommandBuilder generatePredefinedCommands(String... commands) {
+ ReflectiveRegistration.generateCommands(cur, commands);
+ return this;
+ }
+
+ /**
+ * Unregister any childs present at the given keys.
+ * <p>
+ * This method can be used to remove unwanted keys, that might have been added
+ * outside of your control. For example, because you didn't want all commands
+ * registered by {@link #registerCommands(Class, Object)}, or because you didn't
+ * want the help command registered by {@link #group(String, String...)}
+ *
+ * @param removeAliases true if any aliases of the children present at the keys should be removed
+ * @param keys a varargs array containing the keys
+ * @return this
+ * @throws IllegalArgumentException if keys array is empty
+ */
+ public CommandBuilder unregisterCommands(boolean removeAliases, String... keys) {
+ cur.removeChildren(removeAliases, keys);
+ return this;
+ }
+
+ /**
+ * Jump to the sub-address with the given name as main key.
+ * If an address with the exact name as main key exists,
+ * that address becomes the current address.
+ * <p>
+ * Otherwise, a new addresses is registered with the name and aliases.
+ * New addresses registered by this command have a HelpCommand added by default.
+ * <p>
+ * After this call, any registered commands are registered as a sub command
+ * to the new address. To restore the previous state, a call to {@link #parent()}
+ * should be made.
+ * <p>
+ * If the address is the target of a command, it will provide information about its sub commands
+ * using the HelpCommand.
+ *
+ * @param name the main key
+ * @param aliases the aliases
+ * @return this
+ */
+ public CommandBuilder group(String name, String... aliases) {
+ ChildCommandAddress address = cur.getChild(name);
+ if (address == null || !name.equals(address.getMainKey())) {
+ address = new ChildCommandAddress();
+ address.setupAsPlaceholder(name, aliases);
+ cur.addChild(address);
+ }
+ cur = address;
+ return this;
+ }
+
+ /**
+ * Similar to {@link #group(String, String[])} but this will force overwrite any present group,
+ * using the address passed. The address MUST be an instance of {@link ChildCommandAddress}.
+ *
+ * <p>The address must not have a parent or any keys</p>
+ *
+ * @param address the address object to use
+ * @param name the main key
+ * @param aliases any aliases
+ * @return this
+ * @throws IllegalArgumentException if any of the requirements set out above aren't met
+ */
+ public CommandBuilder group(ICommandAddress address, String name, String... aliases) {
+ if (address.hasParent() || address.getMainKey() != null || !(address instanceof ChildCommandAddress)) {
+ throw new IllegalArgumentException();
+ }
+
+ ChildCommandAddress asChild = (ChildCommandAddress) address;
+ asChild.setupAsPlaceholder(name, aliases);
+ cur.addChild(address);
+ cur = asChild;
+ return this;
+ }
+
+ /**
+ * Sets the description of a group created by {@link #group(String, String...)}
+ * Can be called subsequently to making a call to {@link #group(String, String...)}
+ *
+ * @param shortDescription a short description
+ * @param description the lines of a full description.
+ * @return this
+ * @throws IllegalStateException if the current group has no command
+ */
+ public CommandBuilder setGroupDescription(String shortDescription, String... description) {
+ Command command = cur.getCommand();
+ if (command == null) throw new IllegalStateException();
+ cur.setCommand(command
+ .setShortDescription(shortDescription)
+ .setDescription(description));
+ return this;
+ }
+
+ /**
+ * Add a context filter to the command of the current group
+ * @return this
+ * @throws IllegalStateException if the current group has no command
+ */
+ public CommandBuilder addContextFilter(IContextFilter contextFilter) {
+ Command command = cur.getCommand();
+ if (command == null) throw new IllegalStateException();
+ cur.setCommand(command
+ .addContextFilter(contextFilter));
+ return this;
+ }
+
+ /**
+ * Add a required permission to the command of the current group
+ * @return this
+ * @throws IllegalStateException if the current group has no command
+ */
+ public CommandBuilder addPermission(String permission) {
+ return addContextFilter(IContextFilter.permission(permission));
+ }
+
+ /**
+ * Add a required permission to the command of the current group, which can be inherited
+ * @return this
+ * @throws IllegalStateException if the current group has no command
+ */
+ public CommandBuilder addInheritablePermission(String permission) {
+ return addContextFilter(IContextFilter.inheritablePermission(permission));
+ }
+
+ /**
+ * Jump up a level in the address
+ *
+ * @return this
+ * @throws IllegalStateException if the address is empty
+ * // has a depth of 0 // is at level 0
+ */
+ public CommandBuilder parent() {
+ if (cur.hasParent()) {
+ cur = cur.getParent();
+ return this;
+ }
+ throw new IllegalStateException("No parent exists at this address");
+ }
+
+ /**
+ * Jump to the root (empty) address,
+ * such that a subsequent call to {@link #parent()}
+ * will throw a {@link IllegalStateException}
+ *
+ * @return this
+ */
+ public CommandBuilder root() {
+ cur = root;
+ return this;
+ }
+
+ /**
+ * Get the current address, as a space-separated string
+ *
+ * @return the current address
+ */
+ public String getAddress() {
+ return cur.getAddress();
+ }
+
+ /**
+ * Get the depth of the current address.
+ * This is equivalent to {@code getAddress().split(" ").length}.
+ * If the address is empty, the depth is 0.
+ *
+ * @return the depth
+ */
+ public int getDepth() {
+ return cur.getDepth();
+ }
+
+ /**
+ * Set the command at the current group. The command is set
+ * a level higher than it would be if this were a call to {@link #addSubCommand(String, Command, String...)}
+ * <p>
+ * If a call to {@link #setGroupDescription(String, String...)} was made at the same address before,
+ * the description is copied to the given executor.
+ *
+ * @param command the executor
+ * @return this
+ * @throws IllegalArgumentException if the command at the address is present and declared by the user,
+ * in other words, it's not a {@link PredefinedCommand}
+ */
+ public CommandBuilder setCommand(Command command) {
+ Command current = cur.getCommand();
+ if (current instanceof HelpCommand && current != HelpCommand.INSTANCE) {
+ command.setShortDescription(current.getShortDescription());
+ command.setDescription(current.getDescription());
+ }
+
+ cur.setCommand(command);
+ return this;
+ }
+
+ /**
+ * Configure the chat handler at this address. The chat handler
+ * is used for all children down the tree if they don't explicitly have
+ * their own chat handler configured. If this isn't configured,
+ * {@code ChatHandlers.defaultChat()} is used.
+ *
+ * @param chatHandler the chat handler
+ * @return this
+ */
+ public CommandBuilder setChatHandler(IChatHandler chatHandler) {
+ cur.setChatHandler(chatHandler);
+ return this;
+ }
+
+ /**
+ * Add the parameter type to this builder's selector.
+ *
+ * @param type the type
+ * @param <T> the return type of the parameter type
+ * @return this
+ */
+ public <T> CommandBuilder addParameterType(ParameterType<T, Void> type) {
+ selector.addType(false, type);
+ return this;
+ }
+
+ /**
+ * Add the parameter type to this builder's selector.
+ *
+ * @param infolessAlias whether to also register the type with an infoless alias.
+ * this increases the priority assigned to the type if no info object is present.
+ * @param type the type
+ * @param <T> the return type of the parameter type
+ * @param <C> the parameter config type (info object)
+ * @return this
+ */
+
+ public <T, C> CommandBuilder addParameterType(boolean infolessAlias, ParameterType<T, C> type) {
+ selector.addType(infolessAlias, type);
+ return this;
+ }
+
+ /**
+ * Get the dispatcher for the root address.
+ * The dispatcher should be used to finally register all commands,
+ * after they are all declared.
+ *
+ * @return the dispatcher
+ */
+ public ICommandDispatcher getDispatcher() {
+ return root;
+ }
+
+ /**
+ * Print debugging information about the current addresses and commands in this builder
+ * A StackTraceElement indicating where this was called from is also included
+ *
+ * @return this
+ */
+ public CommandBuilder printDebugInformation() {
+ String address = cur == root ? "<root>" : cur.getAddress();
+ StackTraceElement caller = getCallsite();
+
+ StringBuilder message = new StringBuilder("### CommandBuilder dump ###");
+ message.append("\nCalled from ").append(caller);
+ message.append("\nPosition: ").append(address);
+ cur.appendDebugInformation(message, "", new HashSet<>());
+
+ System.out.println(message);
+ return this;
+ }
+
+ private static StackTraceElement getCallsite() {
+ // [0] Thread.currentThread()
+ // [1] CommandBuilder.getCallsite()
+ // [2] Calling method
+ // [3] Method calling the calling method
+ StackTraceElement[] trace = Thread.currentThread().getStackTrace();
+ return trace.length > 3 ? trace[3] : null;
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/CommandException.java b/dicore3/command/src/main/java/io/dico/dicore/command/CommandException.java
index b859952..adf66de 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/CommandException.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/CommandException.java
@@ -1,28 +1,28 @@
-package io.dico.dicore.command;
-
-public class CommandException extends Exception {
-
- public CommandException() {
- }
-
- public CommandException(String message) {
- super(message);
- }
-
- public CommandException(String message, Throwable cause) {
- super(message, cause);
- }
-
- public CommandException(Throwable cause) {
- super(cause);
- }
-
- public static CommandException missingArgument(String parameterName) {
- return new CommandException("Missing argument for " + parameterName);
- }
-
- public static CommandException invalidArgument(String parameterName, String syntaxHelp) {
- return new CommandException("Invalid input for " + parameterName + ", should be " + syntaxHelp);
- }
-
-}
+package io.dico.dicore.command;
+
+public class CommandException extends Exception {
+
+ public CommandException() {
+ }
+
+ public CommandException(String message) {
+ super(message);
+ }
+
+ public CommandException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public CommandException(Throwable cause) {
+ super(cause);
+ }
+
+ public static CommandException missingArgument(String parameterName) {
+ return new CommandException("Missing argument for " + parameterName);
+ }
+
+ public static CommandException invalidArgument(String parameterName, String syntaxHelp) {
+ return new CommandException("Invalid input for " + parameterName + ", should be " + syntaxHelp);
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/EMessageType.java b/dicore3/command/src/main/java/io/dico/dicore/command/EMessageType.java
index fba8780..0b02459 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/EMessageType.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/EMessageType.java
@@ -1,19 +1,19 @@
-package io.dico.dicore.command;
-
-public enum EMessageType {
- GOOD_NEWS,
- BAD_NEWS,
- NEUTRAL,
- INFORMATIVE,
- WARNING,
- INSTRUCTION,
- EXCEPTION,
- RESULT,
- CUSTOM,
-
- DESCRIPTION,
- SYNTAX,
- HIGHLIGHT,
- SUBCOMMAND,
- NUMBER,
-}
+package io.dico.dicore.command;
+
+public enum EMessageType {
+ GOOD_NEWS,
+ BAD_NEWS,
+ NEUTRAL,
+ INFORMATIVE,
+ WARNING,
+ INSTRUCTION,
+ EXCEPTION,
+ RESULT,
+ CUSTOM,
+
+ DESCRIPTION,
+ SYNTAX,
+ HIGHLIGHT,
+ SUBCOMMAND,
+ NUMBER,
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/EOverridePolicy.java b/dicore3/command/src/main/java/io/dico/dicore/command/EOverridePolicy.java
index 83b0151..980477a 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/EOverridePolicy.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/EOverridePolicy.java
@@ -1,12 +1,12 @@
-package io.dico.dicore.command;
-
-/**
- * Override policies for registering to the command map
- */
-public enum EOverridePolicy {
- OVERRIDE_ALL,
- MAIN_KEY_ONLY,
- MAIN_AND_FALLBACK,
- FALLBACK_ONLY,
- OVERRIDE_NONE
-}
+package io.dico.dicore.command;
+
+/**
+ * Override policies for registering to the command map
+ */
+public enum EOverridePolicy {
+ OVERRIDE_ALL,
+ MAIN_KEY_ONLY,
+ MAIN_AND_FALLBACK,
+ FALLBACK_ONLY,
+ OVERRIDE_NONE
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/ExecutionContext.java b/dicore3/command/src/main/java/io/dico/dicore/command/ExecutionContext.java
index a329f40..af42650 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/ExecutionContext.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/ExecutionContext.java
@@ -1,385 +1,385 @@
-package io.dico.dicore.command;
-
-import io.dico.dicore.Formatting;
-import io.dico.dicore.command.parameter.ArgumentBuffer;
-import io.dico.dicore.command.parameter.ContextParser;
-import io.dico.dicore.command.parameter.Parameter;
-import io.dico.dicore.command.parameter.ParameterList;
-import org.bukkit.Location;
-import org.bukkit.command.CommandSender;
-
-import java.util.*;
-
-/**
- * The context of execution.
- * <p>
- * This class is responsible for the control flow of parameter parsing, as well as caching and providing the parsed parameter values.
- * It is also responsible for keeping track of the parameter to complete in the case of a tab completion.
- */
-public class ExecutionContext {
- // Sender of the command
- private final CommandSender sender;
- // Address while parsing parameters with ContextParser
- private ICommandAddress address;
- // Command to execute
- private Command command;
- // if this flag is set, this execution is only for completion purposes.
- private boolean tabComplete;
-
- private final ArgumentBuffer buffer;
- // private ArgumentBuffer processedBuffer;
-
- // caches the buffer's cursor before parsing. This is needed to provide the original input of the player.
- private int cursorStart;
-
- // when the context starts parsing parameters, this flag is set, and any subsequent calls to #parseParameters() throw an IllegalStateException.
- //private boolean attemptedToParse;
-
-
- // The parsed parameter values, mapped by parameter name.
- // This also includes default values. All parameters from the parameter list are present if parsing was successful.
- private Map<String, Object> parameterValueMap = new HashMap<>();
- // this set contains the names of the parameters that were present in the command, and not given a default value.
- private Set<String> parsedParameters = new HashSet<>();
-
-
- // these fields store information required to provide completions.
- // the parameter to complete is the parameter that threw an exception when it was parsing.
- // the exception's message was discarded because it is a completion.
- private Parameter<?, ?> parameterToComplete;
- // this is the cursor that the ArgumentBuffer is reset to when suggested completions are requested.
- private int parameterToCompleteCursor = -1;
-
- // if this flag is set, any messages sent through the sendMessage methods are discarded.
- private boolean muted;
-
- public ExecutionContext(CommandSender sender, ArgumentBuffer buffer, boolean tabComplete) {
- this.sender = Objects.requireNonNull(sender);
- this.buffer = Objects.requireNonNull(buffer);
- this.muted = tabComplete;
- this.tabComplete = tabComplete;
-
- // If its tab completing, keep the empty element that might be at the end of the buffer
- // due to a space at the end of the command.
- // This allows the parser to correctly identify the parameter to be completed in this case.
- if (!tabComplete) {
- buffer.dropTrailingEmptyElements();
- }
- }
-
- /**
- * Construct an execution context that is ready to parse the parameter values.
- *
- * @param sender the sender
- * @param address the address
- * @param command the command
- * @param buffer the arguments
- * @param tabComplete true if this execution is a tab-completion
- */
- public ExecutionContext(CommandSender sender, ICommandAddress address, Command command, ArgumentBuffer buffer, boolean tabComplete) {
- this(sender, buffer, tabComplete);
- setAddress(address);
- setCommand(command);
- }
-
- /**
- * Sender of the command
- *
- * @return the sender of the command
- */
- public CommandSender getSender() {
- return sender;
- }
-
- /**
- * @return the buffer of arguments
- */
- public ArgumentBuffer getBuffer() {
- return buffer;
- }
-
- /**
- * Command's address
- *
- * @return the command's address
- */
- public ICommandAddress getAddress() {
- return address;
- }
-
- /**
- * Set the address
- *
- * @param address the new address
- */
- public void setAddress(ICommandAddress address) {
- this.address = address;
- }
-
- /**
- * The command
- *
- * @return the command
- */
- public Command getCommand() {
- return command;
- }
-
- /**
- * Set the command
- *
- * @param command the new command
- */
- public void setCommand(Command command) {
- this.command = command;
- }
-
- /**
- * @return true if this context is for a tab completion.
- */
- public boolean isTabComplete() {
- return tabComplete;
- }
-
- /**
- * @return true if this context is muted.
- */
- public boolean isMuted() {
- return muted;
- }
-
- /**
- * Parse parameters from the given parameter list,
- * adding their values to the cache of this context.
- *
- * @param parameterList the parameterList
- * @throws CommandException if the arguments are not valid
- */
- public void parse(ParameterList parameterList) throws CommandException {
- cursorStart = buffer.getCursor();
-
- ContextParser parser = new ContextParser(this, parameterList, parameterValueMap, parsedParameters);
-
- try {
- parser.parse();
- } finally {
- if (tabComplete) {
- parameterToComplete = parser.getCompletionTarget();
- parameterToCompleteCursor = parser.getCompletionCursor();
- }
- }
-
- }
-
- /**
- * The command's parameter definition.
- *
- * @return the parameter list
- */
- @Deprecated
- public ParameterList getParameterList() {
- return null;//command.getParameterList();
- }
-
- /**
- * Get the buffer as it was before preprocessing the arguments.
- *
- * @return the original buffer
- */
- @Deprecated
- public ArgumentBuffer getOriginalBuffer() {
- return buffer;
- }
-
- /**
- * The arguments
- *
- * @return the argument buffer
- */
- @Deprecated
- public ArgumentBuffer getProcessedBuffer() {
- return buffer;
- }
-
- /**
- * The cursor start, in other words, the buffer's cursor before parameters were parsed.
- *
- * @return the cursor start
- */
- public int getCursorStart() {
- return cursorStart;
- }
-
- /**
- * The original arguments.
- *
- * @return original arguments.
- */
- public String[] getOriginal() {
- return buffer.getArrayFromIndex(cursorStart);
- }
-
- /**
- * The path used to access this address.
- *
- * @return the path used to access this address.
- */
- public String[] getRoute() {
- return Arrays.copyOf(buffer.toArray(), address.getDepth());
- }
-
- public Formatting getFormat(EMessageType type) {
- return address.getChatHandler().getChatFormatForType(type);
- }
-
- /**
- * The full command as cached by the buffer. Might be incomplete depending on how it was dispatched.
- *
- * @return the full command
- */
- public String getRawInput() {
- return buffer.getRawInput();
- }
-
- /**
- * Get the value of the parameter with the given name
- *
- * @param name the parameter's name
- * @param <T> expected type
- * @return the parsed value or the default value
- */
- @SuppressWarnings("unchecked")
- public <T> T get(String name) {
- if (!parameterValueMap.containsKey(name)) {
- throw new IllegalArgumentException();
- }
-
- try {
- return (T) parameterValueMap.get(name);
- } catch (ClassCastException ex) {
- throw new IllegalArgumentException("Invalid type parameter requested for parameter " + name, ex);
- }
- }
-
- /**
- * Get the value of the flag with the given name
- *
- * @param flag the flag's name, without preceding "-"
- * @param <T> expected type
- * @return the parsed value or the default value
- */
- public <T> T getFlag(String flag) {
- return get("-" + flag);
- }
-
- @SuppressWarnings("unchecked")
- @Deprecated
- public <T> T get(int index) {
- return null;//get(getParameterList().getIndexedParameterName(index));
- }
-
- /**
- * Checks if the parameter by the name was provided in the command's arguments.
- *
- * @param name the parameter name
- * @return true if it was provided
- */
- public boolean isProvided(String name) {
- return parsedParameters.contains(name);
- }
-
- /**
- * Checks if the parameter by the index was provided in the command's arguments.
- *
- * @param index the parameter index
- * @return true if it was provided
- */
- @Deprecated
- public boolean isProvided(int index) {
- return false;//isProvided(getParameterList().getIndexedParameterName(index));
- }
-
- /**
- * The parameter to complete.
- * This parameter is requested suggestions
- *
- * @return the parameter to complete.
- */
- public Parameter<?, ?> getParameterToComplete() {
- return parameterToComplete;
- }
-
- /**
- * Get suggested completions.
- *
- * @param location The location as passed to {link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}, or null if requested in another way.
- * @return completions.
- */
- public List<String> getSuggestedCompletions(Location location) {
- if (parameterToComplete != null) {
- return parameterToComplete.complete(this, location, buffer.getUnaffectingCopy().setCursor(parameterToCompleteCursor));
- }
-
- List<String> result = new ArrayList<>();
- for (String name : parameterValueMap.keySet()) {
- if (name.startsWith("-") && !parsedParameters.contains(name)) {
- result.add(name);
- }
- }
- return result;
- }
-
- /*
- Chat handling
- */
-
- public void sendMessage(String message) {
- sendMessage(true, message);
- }
-
- public void sendMessage(EMessageType messageType, String message) {
- sendMessage(messageType, true, message);
- }
-
- public void sendMessage(boolean translateColours, String message) {
- sendMessage(EMessageType.NEUTRAL, translateColours, message);
- }
-
- public void sendMessage(EMessageType messageType, boolean translateColours, String message) {
- if (!muted) {
- if (translateColours) {
- message = Formatting.translateChars('&', message);
- }
- address.getChatHandler().sendMessage(this, messageType, message);
- }
- }
-
- public void sendMessage(String messageFormat, Object... args) {
- sendMessage(true, messageFormat, args);
- }
-
- public void sendMessage(EMessageType messageType, String messageFormat, Object... args) {
- sendMessage(messageType, true, messageFormat, args);
- }
-
- public void sendMessage(boolean translateColours, String messageFormat, Object... args) {
- sendMessage(EMessageType.NEUTRAL, translateColours, messageFormat, args);
- }
-
- public void sendMessage(EMessageType messageType, boolean translateColours, String messageFormat, Object... args) {
- sendMessage(messageType, translateColours, String.format(messageFormat, args));
- }
-
- public void sendHelpMessage(int page) {
- if (!muted) {
- address.getChatHandler().sendHelpMessage(sender, this, address, page);
- }
- }
-
- public void sendSyntaxMessage() {
- if (!muted) {
- address.getChatHandler().sendSyntaxMessage(sender, this, address);
- }
- }
-
-}
+package io.dico.dicore.command;
+
+import io.dico.dicore.Formatting;
+import io.dico.dicore.command.parameter.ArgumentBuffer;
+import io.dico.dicore.command.parameter.ContextParser;
+import io.dico.dicore.command.parameter.Parameter;
+import io.dico.dicore.command.parameter.ParameterList;
+import org.bukkit.Location;
+import org.bukkit.command.CommandSender;
+
+import java.util.*;
+
+/**
+ * The context of execution.
+ * <p>
+ * This class is responsible for the control flow of parameter parsing, as well as caching and providing the parsed parameter values.
+ * It is also responsible for keeping track of the parameter to complete in the case of a tab completion.
+ */
+public class ExecutionContext {
+ // Sender of the command
+ private final CommandSender sender;
+ // Address while parsing parameters with ContextParser
+ private ICommandAddress address;
+ // Command to execute
+ private Command command;
+ // if this flag is set, this execution is only for completion purposes.
+ private boolean tabComplete;
+
+ private final ArgumentBuffer buffer;
+ // private ArgumentBuffer processedBuffer;
+
+ // caches the buffer's cursor before parsing. This is needed to provide the original input of the player.
+ private int cursorStart;
+
+ // when the context starts parsing parameters, this flag is set, and any subsequent calls to #parseParameters() throw an IllegalStateException.
+ //private boolean attemptedToParse;
+
+
+ // The parsed parameter values, mapped by parameter name.
+ // This also includes default values. All parameters from the parameter list are present if parsing was successful.
+ private Map<String, Object> parameterValueMap = new HashMap<>();
+ // this set contains the names of the parameters that were present in the command, and not given a default value.
+ private Set<String> parsedParameters = new HashSet<>();
+
+
+ // these fields store information required to provide completions.
+ // the parameter to complete is the parameter that threw an exception when it was parsing.
+ // the exception's message was discarded because it is a completion.
+ private Parameter<?, ?> parameterToComplete;
+ // this is the cursor that the ArgumentBuffer is reset to when suggested completions are requested.
+ private int parameterToCompleteCursor = -1;
+
+ // if this flag is set, any messages sent through the sendMessage methods are discarded.
+ private boolean muted;
+
+ public ExecutionContext(CommandSender sender, ArgumentBuffer buffer, boolean tabComplete) {
+ this.sender = Objects.requireNonNull(sender);
+ this.buffer = Objects.requireNonNull(buffer);
+ this.muted = tabComplete;
+ this.tabComplete = tabComplete;
+
+ // If its tab completing, keep the empty element that might be at the end of the buffer
+ // due to a space at the end of the command.
+ // This allows the parser to correctly identify the parameter to be completed in this case.
+ if (!tabComplete) {
+ buffer.dropTrailingEmptyElements();
+ }
+ }
+
+ /**
+ * Construct an execution context that is ready to parse the parameter values.
+ *
+ * @param sender the sender
+ * @param address the address
+ * @param command the command
+ * @param buffer the arguments
+ * @param tabComplete true if this execution is a tab-completion
+ */
+ public ExecutionContext(CommandSender sender, ICommandAddress address, Command command, ArgumentBuffer buffer, boolean tabComplete) {
+ this(sender, buffer, tabComplete);
+ setAddress(address);
+ setCommand(command);
+ }
+
+ /**
+ * Sender of the command
+ *
+ * @return the sender of the command
+ */
+ public CommandSender getSender() {
+ return sender;
+ }
+
+ /**
+ * @return the buffer of arguments
+ */
+ public ArgumentBuffer getBuffer() {
+ return buffer;
+ }
+
+ /**
+ * Command's address
+ *
+ * @return the command's address
+ */
+ public ICommandAddress getAddress() {
+ return address;
+ }
+
+ /**
+ * Set the address
+ *
+ * @param address the new address
+ */
+ public void setAddress(ICommandAddress address) {
+ this.address = address;
+ }
+
+ /**
+ * The command
+ *
+ * @return the command
+ */
+ public Command getCommand() {
+ return command;
+ }
+
+ /**
+ * Set the command
+ *
+ * @param command the new command
+ */
+ public void setCommand(Command command) {
+ this.command = command;
+ }
+
+ /**
+ * @return true if this context is for a tab completion.
+ */
+ public boolean isTabComplete() {
+ return tabComplete;
+ }
+
+ /**
+ * @return true if this context is muted.
+ */
+ public boolean isMuted() {
+ return muted;
+ }
+
+ /**
+ * Parse parameters from the given parameter list,
+ * adding their values to the cache of this context.
+ *
+ * @param parameterList the parameterList
+ * @throws CommandException if the arguments are not valid
+ */
+ public void parse(ParameterList parameterList) throws CommandException {
+ cursorStart = buffer.getCursor();
+
+ ContextParser parser = new ContextParser(this, parameterList, parameterValueMap, parsedParameters);
+
+ try {
+ parser.parse();
+ } finally {
+ if (tabComplete) {
+ parameterToComplete = parser.getCompletionTarget();
+ parameterToCompleteCursor = parser.getCompletionCursor();
+ }
+ }
+
+ }
+
+ /**
+ * The command's parameter definition.
+ *
+ * @return the parameter list
+ */
+ @Deprecated
+ public ParameterList getParameterList() {
+ return null;//command.getParameterList();
+ }
+
+ /**
+ * Get the buffer as it was before preprocessing the arguments.
+ *
+ * @return the original buffer
+ */
+ @Deprecated
+ public ArgumentBuffer getOriginalBuffer() {
+ return buffer;
+ }
+
+ /**
+ * The arguments
+ *
+ * @return the argument buffer
+ */
+ @Deprecated
+ public ArgumentBuffer getProcessedBuffer() {
+ return buffer;
+ }
+
+ /**
+ * The cursor start, in other words, the buffer's cursor before parameters were parsed.
+ *
+ * @return the cursor start
+ */
+ public int getCursorStart() {
+ return cursorStart;
+ }
+
+ /**
+ * The original arguments.
+ *
+ * @return original arguments.
+ */
+ public String[] getOriginal() {
+ return buffer.getArrayFromIndex(cursorStart);
+ }
+
+ /**
+ * The path used to access this address.
+ *
+ * @return the path used to access this address.
+ */
+ public String[] getRoute() {
+ return Arrays.copyOf(buffer.toArray(), address.getDepth());
+ }
+
+ public Formatting getFormat(EMessageType type) {
+ return address.getChatHandler().getChatFormatForType(type);
+ }
+
+ /**
+ * The full command as cached by the buffer. Might be incomplete depending on how it was dispatched.
+ *
+ * @return the full command
+ */
+ public String getRawInput() {
+ return buffer.getRawInput();
+ }
+
+ /**
+ * Get the value of the parameter with the given name
+ *
+ * @param name the parameter's name
+ * @param <T> expected type
+ * @return the parsed value or the default value
+ */
+ @SuppressWarnings("unchecked")
+ public <T> T get(String name) {
+ if (!parameterValueMap.containsKey(name)) {
+ throw new IllegalArgumentException();
+ }
+
+ try {
+ return (T) parameterValueMap.get(name);
+ } catch (ClassCastException ex) {
+ throw new IllegalArgumentException("Invalid type parameter requested for parameter " + name, ex);
+ }
+ }
+
+ /**
+ * Get the value of the flag with the given name
+ *
+ * @param flag the flag's name, without preceding "-"
+ * @param <T> expected type
+ * @return the parsed value or the default value
+ */
+ public <T> T getFlag(String flag) {
+ return get("-" + flag);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Deprecated
+ public <T> T get(int index) {
+ return null;//get(getParameterList().getIndexedParameterName(index));
+ }
+
+ /**
+ * Checks if the parameter by the name was provided in the command's arguments.
+ *
+ * @param name the parameter name
+ * @return true if it was provided
+ */
+ public boolean isProvided(String name) {
+ return parsedParameters.contains(name);
+ }
+
+ /**
+ * Checks if the parameter by the index was provided in the command's arguments.
+ *
+ * @param index the parameter index
+ * @return true if it was provided
+ */
+ @Deprecated
+ public boolean isProvided(int index) {
+ return false;//isProvided(getParameterList().getIndexedParameterName(index));
+ }
+
+ /**
+ * The parameter to complete.
+ * This parameter is requested suggestions
+ *
+ * @return the parameter to complete.
+ */
+ public Parameter<?, ?> getParameterToComplete() {
+ return parameterToComplete;
+ }
+
+ /**
+ * Get suggested completions.
+ *
+ * @param location The location as passed to {link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}, or null if requested in another way.
+ * @return completions.
+ */
+ public List<String> getSuggestedCompletions(Location location) {
+ if (parameterToComplete != null) {
+ return parameterToComplete.complete(this, location, buffer.getUnaffectingCopy().setCursor(parameterToCompleteCursor));
+ }
+
+ List<String> result = new ArrayList<>();
+ for (String name : parameterValueMap.keySet()) {
+ if (name.startsWith("-") && !parsedParameters.contains(name)) {
+ result.add(name);
+ }
+ }
+ return result;
+ }
+
+ /*
+ Chat handling
+ */
+
+ public void sendMessage(String message) {
+ sendMessage(true, message);
+ }
+
+ public void sendMessage(EMessageType messageType, String message) {
+ sendMessage(messageType, true, message);
+ }
+
+ public void sendMessage(boolean translateColours, String message) {
+ sendMessage(EMessageType.NEUTRAL, translateColours, message);
+ }
+
+ public void sendMessage(EMessageType messageType, boolean translateColours, String message) {
+ if (!muted) {
+ if (translateColours) {
+ message = Formatting.translateChars('&', message);
+ }
+ address.getChatHandler().sendMessage(this, messageType, message);
+ }
+ }
+
+ public void sendMessage(String messageFormat, Object... args) {
+ sendMessage(true, messageFormat, args);
+ }
+
+ public void sendMessage(EMessageType messageType, String messageFormat, Object... args) {
+ sendMessage(messageType, true, messageFormat, args);
+ }
+
+ public void sendMessage(boolean translateColours, String messageFormat, Object... args) {
+ sendMessage(EMessageType.NEUTRAL, translateColours, messageFormat, args);
+ }
+
+ public void sendMessage(EMessageType messageType, boolean translateColours, String messageFormat, Object... args) {
+ sendMessage(messageType, translateColours, String.format(messageFormat, args));
+ }
+
+ public void sendHelpMessage(int page) {
+ if (!muted) {
+ address.getChatHandler().sendHelpMessage(sender, this, address, page);
+ }
+ }
+
+ public void sendSyntaxMessage() {
+ if (!muted) {
+ address.getChatHandler().sendSyntaxMessage(sender, this, address);
+ }
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/ExtendedCommand.java b/dicore3/command/src/main/java/io/dico/dicore/command/ExtendedCommand.java
index 47c2aca..eb93b30 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/ExtendedCommand.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/ExtendedCommand.java
@@ -1,64 +1,64 @@
-package io.dico.dicore.command;
-
-import io.dico.dicore.command.parameter.IArgumentPreProcessor;
-import io.dico.dicore.command.parameter.Parameter;
-import io.dico.dicore.command.parameter.type.ParameterType;
-
-@SuppressWarnings("unchecked")
-public abstract class ExtendedCommand<T extends ExtendedCommand<T>> extends Command {
- protected boolean modifiable;
-
- public ExtendedCommand() {
- this(true);
- }
-
- public ExtendedCommand(boolean modifiable) {
- this.modifiable = modifiable;
- }
-
- protected T newModifiableInstance() {
- return (T) this;
- }
-
- @Override
- public T addParameter(Parameter<?, ?> parameter) {
- return modifiable ? (T) super.addParameter(parameter) : newModifiableInstance().addParameter(parameter);
- }
-
- @Override
- public T addContextFilter(IContextFilter contextFilter) {
- return modifiable ? (T) super.addContextFilter(contextFilter) : newModifiableInstance().addContextFilter(contextFilter);
- }
-
- @Override
- public T removeContextFilter(IContextFilter contextFilter) {
- return modifiable ? (T) super.removeContextFilter(contextFilter) : newModifiableInstance().removeContextFilter(contextFilter);
- }
-
- @Override
- public T requiredParameters(int requiredParameters) {
- return modifiable ? (T) super.requiredParameters(requiredParameters) : newModifiableInstance().requiredParameters(requiredParameters);
- }
-
- @Override
- public T repeatFinalParameter() {
- return modifiable ? (T) super.repeatFinalParameter() : newModifiableInstance().repeatFinalParameter();
- }
-
- @Override
- public T setDescription(String... description) {
- return modifiable ? (T) super.setDescription(description) : newModifiableInstance().setDescription(description);
- }
-
- @Override
- public T setShortDescription(String shortDescription) {
- return modifiable ? (T) super.setShortDescription(shortDescription) : newModifiableInstance().setShortDescription(shortDescription);
- }
-
- /*
- @Override
- public T preprocessArguments(IArgumentPreProcessor processor) {
- return modifiable ? (T) super.preprocessArguments(processor) : newModifiableInstance().preprocessArguments(processor);
- }*/
-
-}
+package io.dico.dicore.command;
+
+import io.dico.dicore.command.parameter.IArgumentPreProcessor;
+import io.dico.dicore.command.parameter.Parameter;
+import io.dico.dicore.command.parameter.type.ParameterType;
+
+@SuppressWarnings("unchecked")
+public abstract class ExtendedCommand<T extends ExtendedCommand<T>> extends Command {
+ protected boolean modifiable;
+
+ public ExtendedCommand() {
+ this(true);
+ }
+
+ public ExtendedCommand(boolean modifiable) {
+ this.modifiable = modifiable;
+ }
+
+ protected T newModifiableInstance() {
+ return (T) this;
+ }
+
+ @Override
+ public T addParameter(Parameter<?, ?> parameter) {
+ return modifiable ? (T) super.addParameter(parameter) : newModifiableInstance().addParameter(parameter);
+ }
+
+ @Override
+ public T addContextFilter(IContextFilter contextFilter) {
+ return modifiable ? (T) super.addContextFilter(contextFilter) : newModifiableInstance().addContextFilter(contextFilter);
+ }
+
+ @Override
+ public T removeContextFilter(IContextFilter contextFilter) {
+ return modifiable ? (T) super.removeContextFilter(contextFilter) : newModifiableInstance().removeContextFilter(contextFilter);
+ }
+
+ @Override
+ public T requiredParameters(int requiredParameters) {
+ return modifiable ? (T) super.requiredParameters(requiredParameters) : newModifiableInstance().requiredParameters(requiredParameters);
+ }
+
+ @Override
+ public T repeatFinalParameter() {
+ return modifiable ? (T) super.repeatFinalParameter() : newModifiableInstance().repeatFinalParameter();
+ }
+
+ @Override
+ public T setDescription(String... description) {
+ return modifiable ? (T) super.setDescription(description) : newModifiableInstance().setDescription(description);
+ }
+
+ @Override
+ public T setShortDescription(String shortDescription) {
+ return modifiable ? (T) super.setShortDescription(shortDescription) : newModifiableInstance().setShortDescription(shortDescription);
+ }
+
+ /*
+ @Override
+ public T preprocessArguments(IArgumentPreProcessor processor) {
+ return modifiable ? (T) super.preprocessArguments(processor) : newModifiableInstance().preprocessArguments(processor);
+ }*/
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/ICommandAddress.java b/dicore3/command/src/main/java/io/dico/dicore/command/ICommandAddress.java
index 158b1f0..f900139 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/ICommandAddress.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/ICommandAddress.java
@@ -1,205 +1,205 @@
-package io.dico.dicore.command;
-
-import io.dico.dicore.command.chat.IChatHandler;
-import io.dico.dicore.command.parameter.ArgumentBuffer;
-import io.dico.dicore.command.parameter.ParameterList;
-import io.dico.dicore.command.predef.PredefinedCommand;
-import org.bukkit.command.CommandSender;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Interface for an address of a command.
- * <p>
- * The address holds what the name and aliases of a command are.
- * The address also (optionally) holds a reference to a {@link Command}
- * <p>
- * One instance of {@link Command} can be held by multiple addresses,
- * because the address decides what the command's name and aliases are.
- * <p>
- * The address holds children by key in a map. This map's keys include aliases for its children.
- * This creates a tree of addresses. If a command is dispatches, the tree is traversed untill a command is found
- * and no children deeper down match the command (there are exceptions to the later as defined by
- * {@link Command#takePrecedenceOverSubcommand(String, ArgumentBuffer)}
- * and {@link Command#isVisibleTo(CommandSender)}
- */
-public interface ICommandAddress {
-
- /**
- * @return true if this address has a parent.
- */
- boolean hasParent();
-
- /**
- * Get the parent of this address
- *
- * @return the parent of this address, or null if none exists.
- */
- ICommandAddress getParent();
-
- /**
- * @return true if this address has a command.
- */
- boolean hasCommand();
-
- /**
- * @return true if this address has a command that is not an instance of {@link PredefinedCommand}
- */
- boolean hasUserDeclaredCommand();
-
- /**
- * @return Get the command of this address, or null if none exists.
- */
- Command getCommand();
-
- /**
- * @return true if this address is an instance of {@link RootCommandAddress}
- */
- boolean isRoot();
-
- /**
- * @return the root address of the tree which this address resides in.
- */
- ICommandAddress getRoot();
-
- /**
- * A list of the names of this address, at the current level.
- * The first entry is the main key, the subsequent ones are aliases.
- * <p>
- * Untill an address is assigned a parent, this list is mutable.
- * <p>
- * If {@link #isRoot()}, this returns an immutable, empty list.
- *
- * @return the list of names.
- */
- List<String> getNames();
-
- /**
- * A list of the aliases of this address. That is, {@link #getNames()}
- * without the first entry.
- *
- * @return a list of aliases
- */
- List<String> getAliases();
-
- /**
- * @return The first element of {@link #getNames()}
- */
- String getMainKey();
-
- /**
- * Get the address of this command.
- * That is, the main keys of all commands leading up to this address, and this address itself, separated by a space.
- * In other words, the command without the / that is required to target the command at this address.
- *
- * @return the address of this command.
- */
- String getAddress();
-
- /**
- * Get the amount of addresses that separate this address from the root of the tree, + 1.
- * The root of the tree has a depth of 0. Each subsequent child has its depth incremented by 1.
- *
- * @return The depth of this address
- */
- int getDepth();
-
- /**
- * @return true if the depth of this address is larger than the argument.
- */
- boolean isDepthLargerThan(int depth);
-
- /**
- * @return true if this address has any children.
- */
- boolean hasChildren();
-
- /**
- * @return total number of children, not considering any aliases
- */
- int getNumberOfRealChildren();
-
- /**
- * Get an unmodifiable view of all main keys of the children of this address.
- *
- * @return the main keys
- */
- Collection<String> getChildrenMainKeys();
-
- /**
- * Get an unmodifiable view of the children of this address.
- * Values might be duplicated for aliases.
- *
- * <p>
- * To iterate children without duplicates, you can do something like this:
- * <pre>{@code
- * for (String key : address.getChildrenMainKeys()) {
- * ICommandAddress child = address.getChild(key);
- * // do stuff with child
- * }
- * }</pre>
- * </p>
- *
- * @return the children of this address.
- */
- Map<String, ? extends ICommandAddress> getChildren();
-
- /**
- * Query for a child at the given key.
- *
- * @param key the key. The name or alias of a command.
- * @return the child, or null if it's not found
- */
- ICommandAddress getChild(String key);
-
- /**
- * Query for a child using the given buffer, with the given context for reference.
- * Can be used to override behaviour of the address tree.
- * <p>
- * The default implementation is as follows:
- * <pre>{@code
- * return buffer.hasNext() ? getChild(buffer.next()) : null;
- * }</pre>
- *
- * @param context context of a command being executed
- * @param buffer the buffer. The name or alias of a command.
- * @return the child, or null if it's not found, altered freely by the implementation
- */
- ICommandAddress getChild(ExecutionContext context, ArgumentBuffer buffer) throws CommandException;
-
- /**
- * Get the command dispatcher for this tree
- *
- * @return the command dispatcher
- */
- ICommandDispatcher getDispatcherForTree();
-
- /**
- * @return The desired chathandler for use by commands at this address and any sub-addresses, if they define no explicit chat handler.
- */
- IChatHandler getChatHandler();
-
- /**
- * Returns if the command attached to this address should be treated as trailing.
- * A trailing command is executed whenever the address is scanned for children.
- * Its parameters are parsed and added to the context.
- *
- * @return true if the command attached to this address should be treated as trailing.
- */
- boolean isCommandTrailing();
-
- static ICommandAddress newChild() {
- return new ChildCommandAddress();
- }
-
- static ICommandAddress newChild(Command command) {
- return new ChildCommandAddress(command);
- }
-
- static ICommandAddress newRoot() {
- return new RootCommandAddress();
- }
-
-}
+package io.dico.dicore.command;
+
+import io.dico.dicore.command.chat.IChatHandler;
+import io.dico.dicore.command.parameter.ArgumentBuffer;
+import io.dico.dicore.command.parameter.ParameterList;
+import io.dico.dicore.command.predef.PredefinedCommand;
+import org.bukkit.command.CommandSender;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Interface for an address of a command.
+ * <p>
+ * The address holds what the name and aliases of a command are.
+ * The address also (optionally) holds a reference to a {@link Command}
+ * <p>
+ * One instance of {@link Command} can be held by multiple addresses,
+ * because the address decides what the command's name and aliases are.
+ * <p>
+ * The address holds children by key in a map. This map's keys include aliases for its children.
+ * This creates a tree of addresses. If a command is dispatches, the tree is traversed untill a command is found
+ * and no children deeper down match the command (there are exceptions to the later as defined by
+ * {@link Command#takePrecedenceOverSubcommand(String, ArgumentBuffer)}
+ * and {@link Command#isVisibleTo(CommandSender)}
+ */
+public interface ICommandAddress {
+
+ /**
+ * @return true if this address has a parent.
+ */
+ boolean hasParent();
+
+ /**
+ * Get the parent of this address
+ *
+ * @return the parent of this address, or null if none exists.
+ */
+ ICommandAddress getParent();
+
+ /**
+ * @return true if this address has a command.
+ */
+ boolean hasCommand();
+
+ /**
+ * @return true if this address has a command that is not an instance of {@link PredefinedCommand}
+ */
+ boolean hasUserDeclaredCommand();
+
+ /**
+ * @return Get the command of this address, or null if none exists.
+ */
+ Command getCommand();
+
+ /**
+ * @return true if this address is an instance of {@link RootCommandAddress}
+ */
+ boolean isRoot();
+
+ /**
+ * @return the root address of the tree which this address resides in.
+ */
+ ICommandAddress getRoot();
+
+ /**
+ * A list of the names of this address, at the current level.
+ * The first entry is the main key, the subsequent ones are aliases.
+ * <p>
+ * Untill an address is assigned a parent, this list is mutable.
+ * <p>
+ * If {@link #isRoot()}, this returns an immutable, empty list.
+ *
+ * @return the list of names.
+ */
+ List<String> getNames();
+
+ /**
+ * A list of the aliases of this address. That is, {@link #getNames()}
+ * without the first entry.
+ *
+ * @return a list of aliases
+ */
+ List<String> getAliases();
+
+ /**
+ * @return The first element of {@link #getNames()}
+ */
+ String getMainKey();
+
+ /**
+ * Get the address of this command.
+ * That is, the main keys of all commands leading up to this address, and this address itself, separated by a space.
+ * In other words, the command without the / that is required to target the command at this address.
+ *
+ * @return the address of this command.
+ */
+ String getAddress();
+
+ /**
+ * Get the amount of addresses that separate this address from the root of the tree, + 1.
+ * The root of the tree has a depth of 0. Each subsequent child has its depth incremented by 1.
+ *
+ * @return The depth of this address
+ */
+ int getDepth();
+
+ /**
+ * @return true if the depth of this address is larger than the argument.
+ */
+ boolean isDepthLargerThan(int depth);
+
+ /**
+ * @return true if this address has any children.
+ */
+ boolean hasChildren();
+
+ /**
+ * @return total number of children, not considering any aliases
+ */
+ int getNumberOfRealChildren();
+
+ /**
+ * Get an unmodifiable view of all main keys of the children of this address.
+ *
+ * @return the main keys
+ */
+ Collection<String> getChildrenMainKeys();
+
+ /**
+ * Get an unmodifiable view of the children of this address.
+ * Values might be duplicated for aliases.
+ *
+ * <p>
+ * To iterate children without duplicates, you can do something like this:
+ * <pre>{@code
+ * for (String key : address.getChildrenMainKeys()) {
+ * ICommandAddress child = address.getChild(key);
+ * // do stuff with child
+ * }
+ * }</pre>
+ * </p>
+ *
+ * @return the children of this address.
+ */
+ Map<String, ? extends ICommandAddress> getChildren();
+
+ /**
+ * Query for a child at the given key.
+ *
+ * @param key the key. The name or alias of a command.
+ * @return the child, or null if it's not found
+ */
+ ICommandAddress getChild(String key);
+
+ /**
+ * Query for a child using the given buffer, with the given context for reference.
+ * Can be used to override behaviour of the address tree.
+ * <p>
+ * The default implementation is as follows:
+ * <pre>{@code
+ * return buffer.hasNext() ? getChild(buffer.next()) : null;
+ * }</pre>
+ *
+ * @param context context of a command being executed
+ * @param buffer the buffer. The name or alias of a command.
+ * @return the child, or null if it's not found, altered freely by the implementation
+ */
+ ICommandAddress getChild(ExecutionContext context, ArgumentBuffer buffer) throws CommandException;
+
+ /**
+ * Get the command dispatcher for this tree
+ *
+ * @return the command dispatcher
+ */
+ ICommandDispatcher getDispatcherForTree();
+
+ /**
+ * @return The desired chathandler for use by commands at this address and any sub-addresses, if they define no explicit chat handler.
+ */
+ IChatHandler getChatHandler();
+
+ /**
+ * Returns if the command attached to this address should be treated as trailing.
+ * A trailing command is executed whenever the address is scanned for children.
+ * Its parameters are parsed and added to the context.
+ *
+ * @return true if the command attached to this address should be treated as trailing.
+ */
+ boolean isCommandTrailing();
+
+ static ICommandAddress newChild() {
+ return new ChildCommandAddress();
+ }
+
+ static ICommandAddress newChild(Command command) {
+ return new ChildCommandAddress(command);
+ }
+
+ static ICommandAddress newRoot() {
+ return new RootCommandAddress();
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/ICommandDispatcher.java b/dicore3/command/src/main/java/io/dico/dicore/command/ICommandDispatcher.java
index 055171d..4f3ffc6 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/ICommandDispatcher.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/ICommandDispatcher.java
@@ -1,146 +1,146 @@
-package io.dico.dicore.command;
-
-import io.dico.dicore.command.parameter.ArgumentBuffer;
-import io.dico.dicore.command.registration.CommandMap;
-import org.bukkit.Location;
-import org.bukkit.command.CommandSender;
-
-import java.util.List;
-import java.util.Map;
-
-public interface ICommandDispatcher {
-
- /**
- * Get a potentially indirect child of the root of this dispatcher
- *
- * @param buffer the argument buffer with the subsequent keys to traverse. Any keys beyond the first that isn't found are ignored.
- * @return the child, or this same instance of no child is found.
- */
- ICommandAddress getDeepChild(ArgumentBuffer buffer);
-
- /**
- * Similar to {@link #getDeepChild(ArgumentBuffer)},
- * but this method incorporates checks on the command of traversed children:
- * {@link Command#isVisibleTo(CommandSender)}
- * and {@link Command#takePrecedenceOverSubcommand(String, ArgumentBuffer)}
- * <p>
- * The target of a command is never null, however, the same instance might be returned, and the returned address might not hold a command.
- *
- * @param sender the sender of the command
- * @param buffer the command itself as a buffer.
- * @return the address that is the target of the command.
- */
- @Deprecated
- ICommandAddress getCommandTarget(CommandSender sender, ArgumentBuffer buffer);
-
- /**
- * Similar to {@link #getDeepChild(ArgumentBuffer)},
- * but this method incorporates checks on the command of traversed children:
- * {@link Command#isVisibleTo(CommandSender)}
- * and {@link Command#takePrecedenceOverSubcommand(String, ArgumentBuffer)}
- * <p>
- * The target of a command is never null, however, the same instance might be returned, and the returned address might not hold a command.
- *
- * @param context the context of the command. The context must not have its address set.
- * @param buffer the command itself as a buffer.
- * @return the address that is the target of the command.
- */
- ICommandAddress getCommandTarget(ExecutionContext context, ArgumentBuffer buffer) throws CommandException;
-
- /**
- * dispatch the command
- *
- * @param sender the sender
- * @param command the command
- * @return true if a command has executed
- */
- boolean dispatchCommand(CommandSender sender, String[] command);
-
- /**
- * dispatch the command
- *
- * @param sender the sender
- * @param usedLabel the label (word after the /)
- * @param args the arguments
- * @return true if a command has executed
- */
- boolean dispatchCommand(CommandSender sender, String usedLabel, String[] args);
-
- /**
- * dispatch the command
- *
- * @param sender the sender
- * @param buffer the command
- * @return true if a command has executed
- */
- boolean dispatchCommand(CommandSender sender, ArgumentBuffer buffer);
-
- /**
- * suggest tab completions
- *
- * @param sender the sender as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}
- * @param location the location as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}
- * @param args the arguments as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}
- * args must be sanitized such that it contains no empty elements, particularly at the last index.
- * @return tab completions
- */
- List<String> getTabCompletions(CommandSender sender, Location location, String[] args);
-
- /**
- * suggest tab completions
- *
- * @param sender the sender as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}
- * @param usedLabel the label as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}
- * @param location the location as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}
- * @param args the arguments as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}
- * @return tab completions
- */
- List<String> getTabCompletions(CommandSender sender, String usedLabel, Location location, String[] args);
-
- /**
- * suggest tab completions
- *
- * @param sender the sender as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}
- * @param location the location as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}
- * @param buffer the arguments as a buffer
- * @return tab completions
- */
- List<String> getTabCompletions(CommandSender sender, Location location, ArgumentBuffer buffer);
-
- /**
- * Register this dispatcher's commands to the command map
- *
- * @throws UnsupportedOperationException if this dispatcher is not the root of the tree
- */
- default void registerToCommandMap() {
- registerToCommandMap(null, CommandMap.getCommandMap(), EOverridePolicy.OVERRIDE_ALL);
- }
-
- /**
- * Register this dispatcher's commands to the command map
- *
- * @param fallbackPrefix the fallback prefix to use, null if none
- * @param overridePolicy the override policy
- * @throws UnsupportedOperationException if this dispatcher is not the root of the tree
- */
- default void registerToCommandMap(String fallbackPrefix, EOverridePolicy overridePolicy) {
- registerToCommandMap(fallbackPrefix, CommandMap.getCommandMap(), overridePolicy);
- }
-
- /**
- * Register this dispatcher's commands to the command map
- *
- * @param fallbackPrefix the fallback prefix to use, null if none
- * @param map the command map
- * @param overridePolicy the override policy
- * @throws UnsupportedOperationException if this dispatcher is not the root of the tree
- */
- void registerToCommandMap(String fallbackPrefix, Map<String, org.bukkit.command.Command> map, EOverridePolicy overridePolicy);
-
- default void unregisterFromCommandMap() {
- unregisterFromCommandMap(CommandMap.getCommandMap());
- }
-
- void unregisterFromCommandMap(Map<String, org.bukkit.command.Command> map);
-
-}
+package io.dico.dicore.command;
+
+import io.dico.dicore.command.parameter.ArgumentBuffer;
+import io.dico.dicore.command.registration.CommandMap;
+import org.bukkit.Location;
+import org.bukkit.command.CommandSender;
+
+import java.util.List;
+import java.util.Map;
+
+public interface ICommandDispatcher {
+
+ /**
+ * Get a potentially indirect child of the root of this dispatcher
+ *
+ * @param buffer the argument buffer with the subsequent keys to traverse. Any keys beyond the first that isn't found are ignored.
+ * @return the child, or this same instance of no child is found.
+ */
+ ICommandAddress getDeepChild(ArgumentBuffer buffer);
+
+ /**
+ * Similar to {@link #getDeepChild(ArgumentBuffer)},
+ * but this method incorporates checks on the command of traversed children:
+ * {@link Command#isVisibleTo(CommandSender)}
+ * and {@link Command#takePrecedenceOverSubcommand(String, ArgumentBuffer)}
+ * <p>
+ * The target of a command is never null, however, the same instance might be returned, and the returned address might not hold a command.
+ *
+ * @param sender the sender of the command
+ * @param buffer the command itself as a buffer.
+ * @return the address that is the target of the command.
+ */
+ @Deprecated
+ ICommandAddress getCommandTarget(CommandSender sender, ArgumentBuffer buffer);
+
+ /**
+ * Similar to {@link #getDeepChild(ArgumentBuffer)},
+ * but this method incorporates checks on the command of traversed children:
+ * {@link Command#isVisibleTo(CommandSender)}
+ * and {@link Command#takePrecedenceOverSubcommand(String, ArgumentBuffer)}
+ * <p>
+ * The target of a command is never null, however, the same instance might be returned, and the returned address might not hold a command.
+ *
+ * @param context the context of the command. The context must not have its address set.
+ * @param buffer the command itself as a buffer.
+ * @return the address that is the target of the command.
+ */
+ ICommandAddress getCommandTarget(ExecutionContext context, ArgumentBuffer buffer) throws CommandException;
+
+ /**
+ * dispatch the command
+ *
+ * @param sender the sender
+ * @param command the command
+ * @return true if a command has executed
+ */
+ boolean dispatchCommand(CommandSender sender, String[] command);
+
+ /**
+ * dispatch the command
+ *
+ * @param sender the sender
+ * @param usedLabel the label (word after the /)
+ * @param args the arguments
+ * @return true if a command has executed
+ */
+ boolean dispatchCommand(CommandSender sender, String usedLabel, String[] args);
+
+ /**
+ * dispatch the command
+ *
+ * @param sender the sender
+ * @param buffer the command
+ * @return true if a command has executed
+ */
+ boolean dispatchCommand(CommandSender sender, ArgumentBuffer buffer);
+
+ /**
+ * suggest tab completions
+ *
+ * @param sender the sender as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}
+ * @param location the location as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}
+ * @param args the arguments as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}
+ * args must be sanitized such that it contains no empty elements, particularly at the last index.
+ * @return tab completions
+ */
+ List<String> getTabCompletions(CommandSender sender, Location location, String[] args);
+
+ /**
+ * suggest tab completions
+ *
+ * @param sender the sender as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}
+ * @param usedLabel the label as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}
+ * @param location the location as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}
+ * @param args the arguments as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}
+ * @return tab completions
+ */
+ List<String> getTabCompletions(CommandSender sender, String usedLabel, Location location, String[] args);
+
+ /**
+ * suggest tab completions
+ *
+ * @param sender the sender as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}
+ * @param location the location as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}
+ * @param buffer the arguments as a buffer
+ * @return tab completions
+ */
+ List<String> getTabCompletions(CommandSender sender, Location location, ArgumentBuffer buffer);
+
+ /**
+ * Register this dispatcher's commands to the command map
+ *
+ * @throws UnsupportedOperationException if this dispatcher is not the root of the tree
+ */
+ default void registerToCommandMap() {
+ registerToCommandMap(null, CommandMap.getCommandMap(), EOverridePolicy.OVERRIDE_ALL);
+ }
+
+ /**
+ * Register this dispatcher's commands to the command map
+ *
+ * @param fallbackPrefix the fallback prefix to use, null if none
+ * @param overridePolicy the override policy
+ * @throws UnsupportedOperationException if this dispatcher is not the root of the tree
+ */
+ default void registerToCommandMap(String fallbackPrefix, EOverridePolicy overridePolicy) {
+ registerToCommandMap(fallbackPrefix, CommandMap.getCommandMap(), overridePolicy);
+ }
+
+ /**
+ * Register this dispatcher's commands to the command map
+ *
+ * @param fallbackPrefix the fallback prefix to use, null if none
+ * @param map the command map
+ * @param overridePolicy the override policy
+ * @throws UnsupportedOperationException if this dispatcher is not the root of the tree
+ */
+ void registerToCommandMap(String fallbackPrefix, Map<String, org.bukkit.command.Command> map, EOverridePolicy overridePolicy);
+
+ default void unregisterFromCommandMap() {
+ unregisterFromCommandMap(CommandMap.getCommandMap());
+ }
+
+ void unregisterFromCommandMap(Map<String, org.bukkit.command.Command> map);
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/IContextFilter.java b/dicore3/command/src/main/java/io/dico/dicore/command/IContextFilter.java
index 5c05e27..bf34873 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/IContextFilter.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/IContextFilter.java
@@ -1,201 +1,201 @@
-package io.dico.dicore.command;
-
-import io.dico.dicore.exceptions.checkedfunctions.CheckedConsumer;
-import io.dico.dicore.exceptions.checkedfunctions.CheckedRunnable;
-import org.bukkit.command.CommandSender;
-import org.jetbrains.annotations.NotNull;
-
-public interface IContextFilter extends Comparable<IContextFilter> {
-
- /**
- * Filter the given context by this filter's criteria.
- * If the context does not match the criteria, an exception is thrown describing the problem.
- *
- * @param context the context to match
- * @throws CommandException if it doesn't match
- */
- void filterContext(ExecutionContext context) throws CommandException;
-
- /**
- * Filter an execution context for a direct or indirect sub command of the command that registered this filter.
- *
- * @param subContext the context for the execution
- * @param path the path traversed from the command that registered this filter to the executed command
- */
- default void filterSubContext(ExecutionContext subContext, String... path) throws CommandException {
- filterContext(subContext);
- }
-
- /**
- * Get the priority of this context filter.
- * The priorities determine the order in which a command's context filters are executed.
- *
- * @return the priority
- */
- Priority getPriority();
-
- default boolean allowsContext(ExecutionContext context) {
- try {
- filterContext(context);
- return true;
- } catch (CommandException ex) {
- return false;
- }
- }
-
- /**
- * Used to sort filters in execution order. That is, filters are ordered by {@link #getPriority()}
- *
- * @param o compared filter
- * @return comparison value
- */
- @Override
- default int compareTo(@NotNull IContextFilter o) {
- return getPriority().compareTo(o.getPriority());
- }
-
- /*
- default boolean isInheritable() {
- return false;
- }
-
- default IContextFilter inherit(String... components) {
- if (!isInheritable()) {
- throw new IllegalStateException("This IContextFilter cannot be inherited");
- }
-
- return this;
- }*/
-
- /**
- * IContextFilter priorities. Executes from top to bottom.
- */
- enum Priority {
- /**
- * This priority should have checks on the sender type.
- * Any filters on this priority are tested before permissions are.
- * This is the highest priority.
- */
- VERY_EARLY, // sender type check
-
- /**
- * This priority is specific to permissions.
- */
- PERMISSION,
-
- /**
- * Early priority. Post permissions, pre parameter-parsing.
- */
- EARLY,
-
- /**
- * Normal priority. Post permissions, pre parameter-parsing.
- */
- NORMAL,
-
- /**
- * Late priority. Post permissions, pre parameter-parsing.
- */
- LATE,
-
- /**
- * Very late priority. Post permissions, pre parameter-parsing.
- */
- VERY_LATE,
-
- /**
- * Post parameters priority. Post permissions, post parameter-parsing.
- * This is the lowest priority.
- */
- POST_PARAMETERS;
-
- private IContextFilter inheritor;
-
- /**
- * Get the context filter that inherits context filters from the parent of the same priority.
- * If this filter is also present at the parent, it will do the same for the parent's parent, and so on.
- *
- * @return the inheritor
- */
- public IContextFilter getInheritor() {
- if (inheritor == null) {
- inheritor = InheritingContextFilter.inheritingPriority(this);
- }
- return inheritor;
- }
-
- }
-
- /**
- * Ensures that only {@link org.bukkit.entity.Player} type senders can execute the command.
- */
- IContextFilter PLAYER_ONLY = filterSender(Priority.VERY_EARLY, Validate::isPlayer);
-
- /**
- * Ensures that only {@link org.bukkit.command.ConsoleCommandSender} type senders can execute the command.
- */
- IContextFilter CONSOLE_ONLY = filterSender(Priority.VERY_EARLY, Validate::isConsole);
-
- /**
- * This filter is not working as intended.
- * <p>
- * There is supposed to be a permission filter that takes a base, and appends the command's address to the base, and checks that permission.
- */
- IContextFilter INHERIT_PERMISSIONS = Priority.PERMISSION.getInheritor();
-
- static IContextFilter fromCheckedRunnable(Priority priority, CheckedRunnable<? extends CommandException> runnable) {
- return new IContextFilter() {
- @Override
- public void filterContext(ExecutionContext context) throws CommandException {
- runnable.checkedRun();
- }
-
- @Override
- public Priority getPriority() {
- return priority;
- }
- };
- }
-
- static IContextFilter filterSender(Priority priority, CheckedConsumer<? super CommandSender, ? extends CommandException> consumer) {
- return new IContextFilter() {
- @Override
- public void filterContext(ExecutionContext context) throws CommandException {
- consumer.checkedAccept(context.getSender());
- }
-
- @Override
- public Priority getPriority() {
- return priority;
- }
- };
- }
-
- static IContextFilter permission(String permission) {
- return new PermissionContextFilter(permission);
- }
-
- static IContextFilter permission(String permission, String failMessage) {
- return new PermissionContextFilter(permission, failMessage);
- }
-
- static IContextFilter inheritablePermission(String permission) {
- return new PermissionContextFilter(permission, true);
- }
-
- /**
- * Produce an inheritable permission context filter.
- * A permission component is an element in {@code permission.split("\\.")}
- *
- * @param permission The permission that is required for the command that this is directly assigned to
- * @param componentInsertionIndex the index where any sub-components are inserted. -1 for "at the end".
- * @param failMessage the message to send if the permission is not met
- * @return the context filter
- * @throws IllegalArgumentException if componentInsertionIndex is out of range
- */
- static IContextFilter inheritablePermission(String permission, int componentInsertionIndex, String failMessage) {
- return new PermissionContextFilter(permission, componentInsertionIndex, failMessage);
- }
-
-}
-
+package io.dico.dicore.command;
+
+import io.dico.dicore.exceptions.checkedfunctions.CheckedConsumer;
+import io.dico.dicore.exceptions.checkedfunctions.CheckedRunnable;
+import org.bukkit.command.CommandSender;
+import org.jetbrains.annotations.NotNull;
+
+public interface IContextFilter extends Comparable<IContextFilter> {
+
+ /**
+ * Filter the given context by this filter's criteria.
+ * If the context does not match the criteria, an exception is thrown describing the problem.
+ *
+ * @param context the context to match
+ * @throws CommandException if it doesn't match
+ */
+ void filterContext(ExecutionContext context) throws CommandException;
+
+ /**
+ * Filter an execution context for a direct or indirect sub command of the command that registered this filter.
+ *
+ * @param subContext the context for the execution
+ * @param path the path traversed from the command that registered this filter to the executed command
+ */
+ default void filterSubContext(ExecutionContext subContext, String... path) throws CommandException {
+ filterContext(subContext);
+ }
+
+ /**
+ * Get the priority of this context filter.
+ * The priorities determine the order in which a command's context filters are executed.
+ *
+ * @return the priority
+ */
+ Priority getPriority();
+
+ default boolean allowsContext(ExecutionContext context) {
+ try {
+ filterContext(context);
+ return true;
+ } catch (CommandException ex) {
+ return false;
+ }
+ }
+
+ /**
+ * Used to sort filters in execution order. That is, filters are ordered by {@link #getPriority()}
+ *
+ * @param o compared filter
+ * @return comparison value
+ */
+ @Override
+ default int compareTo(@NotNull IContextFilter o) {
+ return getPriority().compareTo(o.getPriority());
+ }
+
+ /*
+ default boolean isInheritable() {
+ return false;
+ }
+
+ default IContextFilter inherit(String... components) {
+ if (!isInheritable()) {
+ throw new IllegalStateException("This IContextFilter cannot be inherited");
+ }
+
+ return this;
+ }*/
+
+ /**
+ * IContextFilter priorities. Executes from top to bottom.
+ */
+ enum Priority {
+ /**
+ * This priority should have checks on the sender type.
+ * Any filters on this priority are tested before permissions are.
+ * This is the highest priority.
+ */
+ VERY_EARLY, // sender type check
+
+ /**
+ * This priority is specific to permissions.
+ */
+ PERMISSION,
+
+ /**
+ * Early priority. Post permissions, pre parameter-parsing.
+ */
+ EARLY,
+
+ /**
+ * Normal priority. Post permissions, pre parameter-parsing.
+ */
+ NORMAL,
+
+ /**
+ * Late priority. Post permissions, pre parameter-parsing.
+ */
+ LATE,
+
+ /**
+ * Very late priority. Post permissions, pre parameter-parsing.
+ */
+ VERY_LATE,
+
+ /**
+ * Post parameters priority. Post permissions, post parameter-parsing.
+ * This is the lowest priority.
+ */
+ POST_PARAMETERS;
+
+ private IContextFilter inheritor;
+
+ /**
+ * Get the context filter that inherits context filters from the parent of the same priority.
+ * If this filter is also present at the parent, it will do the same for the parent's parent, and so on.
+ *
+ * @return the inheritor
+ */
+ public IContextFilter getInheritor() {
+ if (inheritor == null) {
+ inheritor = InheritingContextFilter.inheritingPriority(this);
+ }
+ return inheritor;
+ }
+
+ }
+
+ /**
+ * Ensures that only {@link org.bukkit.entity.Player} type senders can execute the command.
+ */
+ IContextFilter PLAYER_ONLY = filterSender(Priority.VERY_EARLY, Validate::isPlayer);
+
+ /**
+ * Ensures that only {@link org.bukkit.command.ConsoleCommandSender} type senders can execute the command.
+ */
+ IContextFilter CONSOLE_ONLY = filterSender(Priority.VERY_EARLY, Validate::isConsole);
+
+ /**
+ * This filter is not working as intended.
+ * <p>
+ * There is supposed to be a permission filter that takes a base, and appends the command's address to the base, and checks that permission.
+ */
+ IContextFilter INHERIT_PERMISSIONS = Priority.PERMISSION.getInheritor();
+
+ static IContextFilter fromCheckedRunnable(Priority priority, CheckedRunnable<? extends CommandException> runnable) {
+ return new IContextFilter() {
+ @Override
+ public void filterContext(ExecutionContext context) throws CommandException {
+ runnable.checkedRun();
+ }
+
+ @Override
+ public Priority getPriority() {
+ return priority;
+ }
+ };
+ }
+
+ static IContextFilter filterSender(Priority priority, CheckedConsumer<? super CommandSender, ? extends CommandException> consumer) {
+ return new IContextFilter() {
+ @Override
+ public void filterContext(ExecutionContext context) throws CommandException {
+ consumer.checkedAccept(context.getSender());
+ }
+
+ @Override
+ public Priority getPriority() {
+ return priority;
+ }
+ };
+ }
+
+ static IContextFilter permission(String permission) {
+ return new PermissionContextFilter(permission);
+ }
+
+ static IContextFilter permission(String permission, String failMessage) {
+ return new PermissionContextFilter(permission, failMessage);
+ }
+
+ static IContextFilter inheritablePermission(String permission) {
+ return new PermissionContextFilter(permission, true);
+ }
+
+ /**
+ * Produce an inheritable permission context filter.
+ * A permission component is an element in {@code permission.split("\\.")}
+ *
+ * @param permission The permission that is required for the command that this is directly assigned to
+ * @param componentInsertionIndex the index where any sub-components are inserted. -1 for "at the end".
+ * @param failMessage the message to send if the permission is not met
+ * @return the context filter
+ * @throws IllegalArgumentException if componentInsertionIndex is out of range
+ */
+ static IContextFilter inheritablePermission(String permission, int componentInsertionIndex, String failMessage) {
+ return new PermissionContextFilter(permission, componentInsertionIndex, failMessage);
+ }
+
+}
+
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/InheritingContextFilter.java b/dicore3/command/src/main/java/io/dico/dicore/command/InheritingContextFilter.java
index 0b4875b..58cd7a6 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/InheritingContextFilter.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/InheritingContextFilter.java
@@ -1,64 +1,64 @@
-package io.dico.dicore.command;
-
-import java.util.List;
-
-public abstract class InheritingContextFilter implements IContextFilter {
- private static final String[] emptyStringArray = new String[0];
-
- private static String[] addParent(String[] path, String parent) {
- String[] out = new String[path.length + 1];
- System.arraycopy(path, 0, out, 1, path.length);
- out[0] = parent;
- return out;
- }
-
- protected abstract boolean isInherited(IContextFilter filter);
-
- @Override
- public void filterContext(ExecutionContext context) throws CommandException {
- ICommandAddress address = context.getAddress();
-
- String[] traversedPath = emptyStringArray;
- do {
- traversedPath = addParent(traversedPath, address.getMainKey());
- address = address.getParent();
-
- if (address != null && address.hasCommand()) {
- boolean doBreak = true;
-
- Command command = address.getCommand();
- List<IContextFilter> contextFilterList = command.getContextFilters();
- for (IContextFilter filter : contextFilterList) {
- if (isInherited(filter)) {
- if (filter == this) {
- // do the same for next parent
- // this method is necessary to keep traversedPath information
- doBreak = false;
- } else {
- filter.filterSubContext(context, traversedPath);
- }
- }
- }
-
- if (doBreak) {
- break;
- }
- }
- } while (address != null);
- }
-
- static InheritingContextFilter inheritingPriority(Priority priority) {
- return new InheritingContextFilter() {
- @Override
- protected boolean isInherited(IContextFilter filter) {
- return filter.getPriority() == priority;
- }
-
- @Override
- public Priority getPriority() {
- return priority;
- }
- };
- }
-
-}
+package io.dico.dicore.command;
+
+import java.util.List;
+
+public abstract class InheritingContextFilter implements IContextFilter {
+ private static final String[] emptyStringArray = new String[0];
+
+ private static String[] addParent(String[] path, String parent) {
+ String[] out = new String[path.length + 1];
+ System.arraycopy(path, 0, out, 1, path.length);
+ out[0] = parent;
+ return out;
+ }
+
+ protected abstract boolean isInherited(IContextFilter filter);
+
+ @Override
+ public void filterContext(ExecutionContext context) throws CommandException {
+ ICommandAddress address = context.getAddress();
+
+ String[] traversedPath = emptyStringArray;
+ do {
+ traversedPath = addParent(traversedPath, address.getMainKey());
+ address = address.getParent();
+
+ if (address != null && address.hasCommand()) {
+ boolean doBreak = true;
+
+ Command command = address.getCommand();
+ List<IContextFilter> contextFilterList = command.getContextFilters();
+ for (IContextFilter filter : contextFilterList) {
+ if (isInherited(filter)) {
+ if (filter == this) {
+ // do the same for next parent
+ // this method is necessary to keep traversedPath information
+ doBreak = false;
+ } else {
+ filter.filterSubContext(context, traversedPath);
+ }
+ }
+ }
+
+ if (doBreak) {
+ break;
+ }
+ }
+ } while (address != null);
+ }
+
+ static InheritingContextFilter inheritingPriority(Priority priority) {
+ return new InheritingContextFilter() {
+ @Override
+ protected boolean isInherited(IContextFilter filter) {
+ return filter.getPriority() == priority;
+ }
+
+ @Override
+ public Priority getPriority() {
+ return priority;
+ }
+ };
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/LambdaCommand.java b/dicore3/command/src/main/java/io/dico/dicore/command/LambdaCommand.java
index 71b5ca4..5f55122 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/LambdaCommand.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/LambdaCommand.java
@@ -1,35 +1,35 @@
-package io.dico.dicore.command;
-
-import io.dico.dicore.exceptions.checkedfunctions.CheckedBiFunction;
-import org.bukkit.Location;
-import org.bukkit.command.CommandSender;
-
-import java.util.List;
-import java.util.Objects;
-import java.util.function.BiFunction;
-
-public class LambdaCommand extends ExtendedCommand<LambdaCommand> {
- private CheckedBiFunction<CommandSender, ExecutionContext, String, CommandException> executor;
- private BiFunction<CommandSender, ExecutionContext, List<String>> completer;
-
- public LambdaCommand executor(CheckedBiFunction<CommandSender, ExecutionContext, String, CommandException> executor) {
- this.executor = Objects.requireNonNull(executor);
- return this;
- }
-
- public LambdaCommand completer(BiFunction<CommandSender, ExecutionContext, List<String>> completer) {
- this.completer = Objects.requireNonNull(completer);
- return this;
- }
-
- @Override
- public String execute(CommandSender sender, ExecutionContext context) throws CommandException {
- return executor.checkedApply(sender, context);
- }
-
- @Override
- public List<String> tabComplete(CommandSender sender, ExecutionContext context, Location location) {
- return completer == null ? super.tabComplete(sender, context, location) : completer.apply(sender, context);
- }
-
-}
+package io.dico.dicore.command;
+
+import io.dico.dicore.exceptions.checkedfunctions.CheckedBiFunction;
+import org.bukkit.Location;
+import org.bukkit.command.CommandSender;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.function.BiFunction;
+
+public class LambdaCommand extends ExtendedCommand<LambdaCommand> {
+ private CheckedBiFunction<CommandSender, ExecutionContext, String, CommandException> executor;
+ private BiFunction<CommandSender, ExecutionContext, List<String>> completer;
+
+ public LambdaCommand executor(CheckedBiFunction<CommandSender, ExecutionContext, String, CommandException> executor) {
+ this.executor = Objects.requireNonNull(executor);
+ return this;
+ }
+
+ public LambdaCommand completer(BiFunction<CommandSender, ExecutionContext, List<String>> completer) {
+ this.completer = Objects.requireNonNull(completer);
+ return this;
+ }
+
+ @Override
+ public String execute(CommandSender sender, ExecutionContext context) throws CommandException {
+ return executor.checkedApply(sender, context);
+ }
+
+ @Override
+ public List<String> tabComplete(CommandSender sender, ExecutionContext context, Location location) {
+ return completer == null ? super.tabComplete(sender, context, location) : completer.apply(sender, context);
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/ModifiableCommandAddress.java b/dicore3/command/src/main/java/io/dico/dicore/command/ModifiableCommandAddress.java
index 0cfd755..70e3e03 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/ModifiableCommandAddress.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/ModifiableCommandAddress.java
@@ -1,312 +1,312 @@
-package io.dico.dicore.command;
-
-import io.dico.dicore.command.chat.ChatHandlers;
-import io.dico.dicore.command.chat.IChatHandler;
-import io.dico.dicore.command.parameter.ArgumentBuffer;
-import io.dico.dicore.command.parameter.ParameterList;
-import io.dico.dicore.command.predef.DefaultGroupCommand;
-import io.dico.dicore.command.predef.HelpCommand;
-import io.dico.dicore.command.predef.PredefinedCommand;
-
-import java.util.*;
-
-public abstract class ModifiableCommandAddress implements ICommandAddress {
- Map<String, ChildCommandAddress> children;
- Collection<String> childrenMainKeys = Collections.emptyList();
-
- // the chat handler as configured by the programmer
- IChatHandler chatHandler;
- // cache for the algorithm that finds the first chat handler going up the tree
- transient IChatHandler cachedChatHandlerFallback;
-
- ModifiableCommandAddress helpChild;
-
- public ModifiableCommandAddress() {
- this.children = new LinkedHashMap<>(4);
- }
-
- @Override
- public boolean hasParent() {
- return getParent() != null;
- }
-
- @Override
- public boolean hasCommand() {
- return getCommand() != null;
- }
-
- @Override
- public boolean hasUserDeclaredCommand() {
- Command command = getCommand();
- return command != null && !(command instanceof PredefinedCommand);
- }
-
- @Override
- public Command getCommand() {
- return null;
- }
-
- @Override
- public boolean isRoot() {
- return false;
- }
-
- @Override
- public List<String> getNames() {
- return null;
- }
-
- @Override
- public List<String> getAliases() {
- List<String> names = getNames();
- if (names == null) {
- return null;
- }
- if (names.isEmpty()) {
- return Collections.emptyList();
- }
- return names.subList(1, names.size());
- }
-
- @Override
- public String getMainKey() {
- return null;
- }
-
- public void setCommand(Command command) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public abstract ModifiableCommandAddress getParent();
-
- @Override
- public RootCommandAddress getRoot() {
- ModifiableCommandAddress out = this;
- while (out.hasParent()) {
- out = out.getParent();
- }
- return out.isRoot() ? (RootCommandAddress) out : null;
- }
-
- @Override
- public int getDepth() {
- int depth = 0;
- ICommandAddress address = this;
- while (address.hasParent()) {
- address = address.getParent();
- depth++;
- }
- return depth;
- }
-
- @Override
- public boolean isDepthLargerThan(int value) {
- int depth = 0;
- ICommandAddress address = this;
- do {
- if (depth > value) {
- return true;
- }
-
- address = address.getParent();
- depth++;
- } while (address != null);
- return false;
- }
-
- @Override
- public boolean hasChildren() {
- return !children.isEmpty();
- }
-
- @Override
- public int getNumberOfRealChildren() {
- return childrenMainKeys.size();
- }
-
- @Override
- public Collection<String> getChildrenMainKeys() {
- return Collections.unmodifiableCollection(childrenMainKeys);
- }
-
- @Override
- public Map<String, ? extends ModifiableCommandAddress> getChildren() {
- return Collections.unmodifiableMap(children);
- }
-
- @Override
- public ChildCommandAddress getChild(String key) {
- return children.get(key);
- }
-
- @Override
- public ChildCommandAddress getChild(ExecutionContext context, ArgumentBuffer buffer) throws CommandException {
- return buffer.hasNext() ? getChild(buffer.next()) : null;
- }
-
- public void addChild(ICommandAddress child) {
- if (!(child instanceof ChildCommandAddress)) {
- throw new IllegalArgumentException("Argument must be a ChildCommandAddress");
- }
-
- ChildCommandAddress mChild = (ChildCommandAddress) child;
- if (mChild.parent != null) {
- throw new IllegalArgumentException("Argument already has a parent");
- }
-
- if (mChild.names.isEmpty()) {
- throw new IllegalArgumentException("Argument must have names");
- }
-
- Iterator<String> names = mChild.modifiableNamesIterator();
- String mainKey = names.next();
-
- if (!childrenMainKeys.contains(mainKey)) {
- if (!(childrenMainKeys instanceof ArrayList)) {
- childrenMainKeys = new ArrayList<>();
- }
- childrenMainKeys.add(mainKey);
- }
-
- children.put(mainKey, mChild);
-
- while (names.hasNext()) {
- String name = names.next();
- if (children.putIfAbsent(name, mChild) != null) {
- names.remove();
- }
- }
-
- mChild.setParent(this);
-
- if (mChild.hasCommand() && mChild.getCommand() instanceof HelpCommand) {
- helpChild = mChild;
- }
- }
-
- public void removeChildren(boolean removeAliases, String... keys) {
- if (keys.length == 0) {
- throw new IllegalArgumentException("keys is empty");
- }
-
- for (String key : keys) {
- ChildCommandAddress keyTarget = getChild(key);
- if (keyTarget == null) {
- continue;
- }
-
- if (removeAliases) {
- Iterator<String> iterator = keyTarget.namesModifiable.iterator();
- boolean first = true;
- while (iterator.hasNext()) {
- String alias = iterator.next();
- ChildCommandAddress aliasTarget = getChild(key);
- if (aliasTarget == keyTarget) {
- if (first) {
- childrenMainKeys.remove(alias);
- }
- children.remove(alias);
- }
- iterator.remove();
- first = false;
- }
-
- } else {
- if (key.equals(keyTarget.getMainKey())) {
- childrenMainKeys.remove(key);
- }
-
- children.remove(key);
- keyTarget.namesModifiable.remove(key);
- }
- }
- }
-
- public boolean hasHelpCommand() {
- return helpChild != null;
- }
-
- public ModifiableCommandAddress getHelpCommand() {
- return helpChild;
- }
-
- @Override
- public IChatHandler getChatHandler() {
- if (cachedChatHandlerFallback == null) {
- if (chatHandler != null) {
- cachedChatHandlerFallback = chatHandler;
- } else if (!hasParent()) {
- cachedChatHandlerFallback = ChatHandlers.defaultChat();
- } else {
- cachedChatHandlerFallback = getParent().getChatHandler();
- }
- }
- return cachedChatHandlerFallback;
- }
-
- public void setChatHandler(IChatHandler chatHandler) {
- this.chatHandler = chatHandler;
- resetChatHandlerCache(new HashSet<>());
- }
-
- void resetChatHandlerCache(Set<ModifiableCommandAddress> dejaVu) {
- if (dejaVu.add(this)) {
- cachedChatHandlerFallback = chatHandler;
- for (ChildCommandAddress address : children.values()) {
- if (address.chatHandler == null) {
- address.resetChatHandlerCache(dejaVu);
- }
- }
- }
- }
-
- @Override
- public ICommandDispatcher getDispatcherForTree() {
- return getRoot();
- }
-
- @Override
- public boolean isCommandTrailing() {
- return false;
- }
-
- public void setCommandTrailing(boolean trailing) {
- throw new UnsupportedOperationException();
- }
-
- void appendDebugInformation(StringBuilder target, String linePrefix, Set<ICommandAddress> seen) {
- target.append('\n').append(linePrefix);
- if (!seen.add(this)) {
- target.append("<duplicate of address '").append(getAddress()).append("'>");
- return;
- }
-
- if (this instanceof ChildCommandAddress) {
- List<String> namesModifiable = ((ChildCommandAddress) this).namesModifiable;
- if (namesModifiable.isEmpty()) {
- target.append("<no key>");
- } else {
- Iterator<String> keys = namesModifiable.iterator();
- target.append(keys.next()).append(' ');
- if (keys.hasNext()) {
- target.append('(').append(keys.next());
- while (keys.hasNext()) {
- target.append(" ,").append(keys.next());
- }
- target.append(") ");
- }
- }
- } else {
- target.append("<root> ");
- }
-
- String commandClass = hasCommand() ? getCommand().getClass().getCanonicalName() : "<no command>";
- target.append(commandClass);
-
- for (ChildCommandAddress child : new HashSet<>(children.values())) {
- child.appendDebugInformation(target, linePrefix + " ", seen);
- }
- }
-
-}
+package io.dico.dicore.command;
+
+import io.dico.dicore.command.chat.ChatHandlers;
+import io.dico.dicore.command.chat.IChatHandler;
+import io.dico.dicore.command.parameter.ArgumentBuffer;
+import io.dico.dicore.command.parameter.ParameterList;
+import io.dico.dicore.command.predef.DefaultGroupCommand;
+import io.dico.dicore.command.predef.HelpCommand;
+import io.dico.dicore.command.predef.PredefinedCommand;
+
+import java.util.*;
+
+public abstract class ModifiableCommandAddress implements ICommandAddress {
+ Map<String, ChildCommandAddress> children;
+ Collection<String> childrenMainKeys = Collections.emptyList();
+
+ // the chat handler as configured by the programmer
+ IChatHandler chatHandler;
+ // cache for the algorithm that finds the first chat handler going up the tree
+ transient IChatHandler cachedChatHandlerFallback;
+
+ ModifiableCommandAddress helpChild;
+
+ public ModifiableCommandAddress() {
+ this.children = new LinkedHashMap<>(4);
+ }
+
+ @Override
+ public boolean hasParent() {
+ return getParent() != null;
+ }
+
+ @Override
+ public boolean hasCommand() {
+ return getCommand() != null;
+ }
+
+ @Override
+ public boolean hasUserDeclaredCommand() {
+ Command command = getCommand();
+ return command != null && !(command instanceof PredefinedCommand);
+ }
+
+ @Override
+ public Command getCommand() {
+ return null;
+ }
+
+ @Override
+ public boolean isRoot() {
+ return false;
+ }
+
+ @Override
+ public List<String> getNames() {
+ return null;
+ }
+
+ @Override
+ public List<String> getAliases() {
+ List<String> names = getNames();
+ if (names == null) {
+ return null;
+ }
+ if (names.isEmpty()) {
+ return Collections.emptyList();
+ }
+ return names.subList(1, names.size());
+ }
+
+ @Override
+ public String getMainKey() {
+ return null;
+ }
+
+ public void setCommand(Command command) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public abstract ModifiableCommandAddress getParent();
+
+ @Override
+ public RootCommandAddress getRoot() {
+ ModifiableCommandAddress out = this;
+ while (out.hasParent()) {
+ out = out.getParent();
+ }
+ return out.isRoot() ? (RootCommandAddress) out : null;
+ }
+
+ @Override
+ public int getDepth() {
+ int depth = 0;
+ ICommandAddress address = this;
+ while (address.hasParent()) {
+ address = address.getParent();
+ depth++;
+ }
+ return depth;
+ }
+
+ @Override
+ public boolean isDepthLargerThan(int value) {
+ int depth = 0;
+ ICommandAddress address = this;
+ do {
+ if (depth > value) {
+ return true;
+ }
+
+ address = address.getParent();
+ depth++;
+ } while (address != null);
+ return false;
+ }
+
+ @Override
+ public boolean hasChildren() {
+ return !children.isEmpty();
+ }
+
+ @Override
+ public int getNumberOfRealChildren() {
+ return childrenMainKeys.size();
+ }
+
+ @Override
+ public Collection<String> getChildrenMainKeys() {
+ return Collections.unmodifiableCollection(childrenMainKeys);
+ }
+
+ @Override
+ public Map<String, ? extends ModifiableCommandAddress> getChildren() {
+ return Collections.unmodifiableMap(children);
+ }
+
+ @Override
+ public ChildCommandAddress getChild(String key) {
+ return children.get(key);
+ }
+
+ @Override
+ public ChildCommandAddress getChild(ExecutionContext context, ArgumentBuffer buffer) throws CommandException {
+ return buffer.hasNext() ? getChild(buffer.next()) : null;
+ }
+
+ public void addChild(ICommandAddress child) {
+ if (!(child instanceof ChildCommandAddress)) {
+ throw new IllegalArgumentException("Argument must be a ChildCommandAddress");
+ }
+
+ ChildCommandAddress mChild = (ChildCommandAddress) child;
+ if (mChild.parent != null) {
+ throw new IllegalArgumentException("Argument already has a parent");
+ }
+
+ if (mChild.names.isEmpty()) {
+ throw new IllegalArgumentException("Argument must have names");
+ }
+
+ Iterator<String> names = mChild.modifiableNamesIterator();
+ String mainKey = names.next();
+
+ if (!childrenMainKeys.contains(mainKey)) {
+ if (!(childrenMainKeys instanceof ArrayList)) {
+ childrenMainKeys = new ArrayList<>();
+ }
+ childrenMainKeys.add(mainKey);
+ }
+
+ children.put(mainKey, mChild);
+
+ while (names.hasNext()) {
+ String name = names.next();
+ if (children.putIfAbsent(name, mChild) != null) {
+ names.remove();
+ }
+ }
+
+ mChild.setParent(this);
+
+ if (mChild.hasCommand() && mChild.getCommand() instanceof HelpCommand) {
+ helpChild = mChild;
+ }
+ }
+
+ public void removeChildren(boolean removeAliases, String... keys) {
+ if (keys.length == 0) {
+ throw new IllegalArgumentException("keys is empty");
+ }
+
+ for (String key : keys) {
+ ChildCommandAddress keyTarget = getChild(key);
+ if (keyTarget == null) {
+ continue;
+ }
+
+ if (removeAliases) {
+ Iterator<String> iterator = keyTarget.namesModifiable.iterator();
+ boolean first = true;
+ while (iterator.hasNext()) {
+ String alias = iterator.next();
+ ChildCommandAddress aliasTarget = getChild(key);
+ if (aliasTarget == keyTarget) {
+ if (first) {
+ childrenMainKeys.remove(alias);
+ }
+ children.remove(alias);
+ }
+ iterator.remove();
+ first = false;
+ }
+
+ } else {
+ if (key.equals(keyTarget.getMainKey())) {
+ childrenMainKeys.remove(key);
+ }
+
+ children.remove(key);
+ keyTarget.namesModifiable.remove(key);
+ }
+ }
+ }
+
+ public boolean hasHelpCommand() {
+ return helpChild != null;
+ }
+
+ public ModifiableCommandAddress getHelpCommand() {
+ return helpChild;
+ }
+
+ @Override
+ public IChatHandler getChatHandler() {
+ if (cachedChatHandlerFallback == null) {
+ if (chatHandler != null) {
+ cachedChatHandlerFallback = chatHandler;
+ } else if (!hasParent()) {
+ cachedChatHandlerFallback = ChatHandlers.defaultChat();
+ } else {
+ cachedChatHandlerFallback = getParent().getChatHandler();
+ }
+ }
+ return cachedChatHandlerFallback;
+ }
+
+ public void setChatHandler(IChatHandler chatHandler) {
+ this.chatHandler = chatHandler;
+ resetChatHandlerCache(new HashSet<>());
+ }
+
+ void resetChatHandlerCache(Set<ModifiableCommandAddress> dejaVu) {
+ if (dejaVu.add(this)) {
+ cachedChatHandlerFallback = chatHandler;
+ for (ChildCommandAddress address : children.values()) {
+ if (address.chatHandler == null) {
+ address.resetChatHandlerCache(dejaVu);
+ }
+ }
+ }
+ }
+
+ @Override
+ public ICommandDispatcher getDispatcherForTree() {
+ return getRoot();
+ }
+
+ @Override
+ public boolean isCommandTrailing() {
+ return false;
+ }
+
+ public void setCommandTrailing(boolean trailing) {
+ throw new UnsupportedOperationException();
+ }
+
+ void appendDebugInformation(StringBuilder target, String linePrefix, Set<ICommandAddress> seen) {
+ target.append('\n').append(linePrefix);
+ if (!seen.add(this)) {
+ target.append("<duplicate of address '").append(getAddress()).append("'>");
+ return;
+ }
+
+ if (this instanceof ChildCommandAddress) {
+ List<String> namesModifiable = ((ChildCommandAddress) this).namesModifiable;
+ if (namesModifiable.isEmpty()) {
+ target.append("<no key>");
+ } else {
+ Iterator<String> keys = namesModifiable.iterator();
+ target.append(keys.next()).append(' ');
+ if (keys.hasNext()) {
+ target.append('(').append(keys.next());
+ while (keys.hasNext()) {
+ target.append(" ,").append(keys.next());
+ }
+ target.append(") ");
+ }
+ }
+ } else {
+ target.append("<root> ");
+ }
+
+ String commandClass = hasCommand() ? getCommand().getClass().getCanonicalName() : "<no command>";
+ target.append(commandClass);
+
+ for (ChildCommandAddress child : new HashSet<>(children.values())) {
+ child.appendDebugInformation(target, linePrefix + " ", seen);
+ }
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/PermissionContextFilter.java b/dicore3/command/src/main/java/io/dico/dicore/command/PermissionContextFilter.java
index 75b2035..c996356 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/PermissionContextFilter.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/PermissionContextFilter.java
@@ -1,136 +1,136 @@
-package io.dico.dicore.command;
-
-import java.util.List;
-import java.util.Objects;
-
-public class PermissionContextFilter implements IContextFilter {
- private String permission;
- private String[] permissionComponents;
- private int componentInsertionIndex;
- private String failMessage;
-
- public PermissionContextFilter(String permission) {
- this.permission = Objects.requireNonNull(permission);
- }
-
- public PermissionContextFilter(String permission, String failMessage) {
- this(permission);
- this.failMessage = failMessage;
- }
-
- public PermissionContextFilter(String permission, boolean inheritable) {
- this(permission, null, inheritable);
- }
-
- public PermissionContextFilter(String permission, String failMessage, boolean inheritable) {
- this(permission, failMessage);
- if (inheritable) {
- setupInheritability(-1);
- }
- }
-
- public PermissionContextFilter(String permission, int componentInsertionIndex, String failMessage) {
- this(permission, failMessage);
- setupInheritability(componentInsertionIndex);
- }
-
- private void setupInheritability(int componentInsertionIndex) {
- this.permissionComponents = permission.split("\\.");
- this.componentInsertionIndex = componentInsertionIndex < 0 ? permissionComponents.length : componentInsertionIndex;
- if (componentInsertionIndex > permissionComponents.length) throw new IllegalArgumentException();
- }
-
- private void doFilter(ExecutionContext context, String permission) throws CommandException {
- if (failMessage != null) {
- Validate.isAuthorized(context.getSender(), permission, failMessage);
- } else {
- Validate.isAuthorized(context.getSender(), permission);
- }
- }
-
- @Override
- public void filterContext(ExecutionContext context) throws CommandException {
- doFilter(context, permission);
- }
-
- public String getInheritedPermission(String[] components) {
- int insertedAmount = components.length;
- String[] currentComponents = permissionComponents;
- int currentAmount = currentComponents.length;
- String[] targetArray = new String[currentAmount + insertedAmount];
-
- int insertionIndex;
- //int newInsertionIndex;
- if (componentInsertionIndex == -1) {
- insertionIndex = currentAmount;
- //newInsertionIndex = -1;
- } else {
- insertionIndex = componentInsertionIndex;
- //newInsertionIndex = insertionIndex + insertedAmount;
- }
-
- // copy the current components up to insertionIndex
- System.arraycopy(currentComponents, 0, targetArray, 0, insertionIndex);
- // copy the new components into the array at insertionIndex
- System.arraycopy(components, 0, targetArray, insertionIndex, insertedAmount);
- // copy the current components from insertionIndex + inserted amount
- System.arraycopy(currentComponents, insertionIndex, targetArray, insertionIndex + insertedAmount, currentAmount - insertionIndex);
-
- return String.join(".", targetArray);
- }
-
- @Override
- public void filterSubContext(ExecutionContext subContext, String... path) throws CommandException {
- if (isInheritable()) {
- doFilter(subContext, getInheritedPermission(path));
- }
- }
-
- @Override
- public Priority getPriority() {
- return Priority.PERMISSION;
- }
-
- public boolean isInheritable() {
- return permissionComponents != null;
- }
-
- public String getPermission() {
- return permission;
- }
-
- public int getComponentInsertionIndex() {
- return componentInsertionIndex;
- }
-
- public String getFailMessage() {
- return failMessage;
- }
-
- /*
- private fun getPermissionsOf(address: ICommandAddress) = getPermissionsOf(address, emptyArray(), mutableListOf())
-
- private fun getPermissionsOf(address: ICommandAddress, path: Array<String>, result: MutableList<String>): List<String> {
- val command = address.command ?: return result
-
- var inherited = false
- for (filter in command.contextFilters) {
- when (filter) {
- is PermissionContextFilter -> {
- if (path.isEmpty()) result.add(filter.permission)
- else if (filter.isInheritable) result.add(filter.getInheritedPermission(path))
- }
- is InheritingContextFilter -> {
- if (filter.priority == PERMISSION && address.hasParent() && !inherited) {
- inherited = true
- getPermissionsOf(address.parent, arrayOf(address.mainKey, *path), result)
- }
- }
- }
- }
-
- return result
- }
- */
-
-}
+package io.dico.dicore.command;
+
+import java.util.List;
+import java.util.Objects;
+
+public class PermissionContextFilter implements IContextFilter {
+ private String permission;
+ private String[] permissionComponents;
+ private int componentInsertionIndex;
+ private String failMessage;
+
+ public PermissionContextFilter(String permission) {
+ this.permission = Objects.requireNonNull(permission);
+ }
+
+ public PermissionContextFilter(String permission, String failMessage) {
+ this(permission);
+ this.failMessage = failMessage;
+ }
+
+ public PermissionContextFilter(String permission, boolean inheritable) {
+ this(permission, null, inheritable);
+ }
+
+ public PermissionContextFilter(String permission, String failMessage, boolean inheritable) {
+ this(permission, failMessage);
+ if (inheritable) {
+ setupInheritability(-1);
+ }
+ }
+
+ public PermissionContextFilter(String permission, int componentInsertionIndex, String failMessage) {
+ this(permission, failMessage);
+ setupInheritability(componentInsertionIndex);
+ }
+
+ private void setupInheritability(int componentInsertionIndex) {
+ this.permissionComponents = permission.split("\\.");
+ this.componentInsertionIndex = componentInsertionIndex < 0 ? permissionComponents.length : componentInsertionIndex;
+ if (componentInsertionIndex > permissionComponents.length) throw new IllegalArgumentException();
+ }
+
+ private void doFilter(ExecutionContext context, String permission) throws CommandException {
+ if (failMessage != null) {
+ Validate.isAuthorized(context.getSender(), permission, failMessage);
+ } else {
+ Validate.isAuthorized(context.getSender(), permission);
+ }
+ }
+
+ @Override
+ public void filterContext(ExecutionContext context) throws CommandException {
+ doFilter(context, permission);
+ }
+
+ public String getInheritedPermission(String[] components) {
+ int insertedAmount = components.length;
+ String[] currentComponents = permissionComponents;
+ int currentAmount = currentComponents.length;
+ String[] targetArray = new String[currentAmount + insertedAmount];
+
+ int insertionIndex;
+ //int newInsertionIndex;
+ if (componentInsertionIndex == -1) {
+ insertionIndex = currentAmount;
+ //newInsertionIndex = -1;
+ } else {
+ insertionIndex = componentInsertionIndex;
+ //newInsertionIndex = insertionIndex + insertedAmount;
+ }
+
+ // copy the current components up to insertionIndex
+ System.arraycopy(currentComponents, 0, targetArray, 0, insertionIndex);
+ // copy the new components into the array at insertionIndex
+ System.arraycopy(components, 0, targetArray, insertionIndex, insertedAmount);
+ // copy the current components from insertionIndex + inserted amount
+ System.arraycopy(currentComponents, insertionIndex, targetArray, insertionIndex + insertedAmount, currentAmount - insertionIndex);
+
+ return String.join(".", targetArray);
+ }
+
+ @Override
+ public void filterSubContext(ExecutionContext subContext, String... path) throws CommandException {
+ if (isInheritable()) {
+ doFilter(subContext, getInheritedPermission(path));
+ }
+ }
+
+ @Override
+ public Priority getPriority() {
+ return Priority.PERMISSION;
+ }
+
+ public boolean isInheritable() {
+ return permissionComponents != null;
+ }
+
+ public String getPermission() {
+ return permission;
+ }
+
+ public int getComponentInsertionIndex() {
+ return componentInsertionIndex;
+ }
+
+ public String getFailMessage() {
+ return failMessage;
+ }
+
+ /*
+ private fun getPermissionsOf(address: ICommandAddress) = getPermissionsOf(address, emptyArray(), mutableListOf())
+
+ private fun getPermissionsOf(address: ICommandAddress, path: Array<String>, result: MutableList<String>): List<String> {
+ val command = address.command ?: return result
+
+ var inherited = false
+ for (filter in command.contextFilters) {
+ when (filter) {
+ is PermissionContextFilter -> {
+ if (path.isEmpty()) result.add(filter.permission)
+ else if (filter.isInheritable) result.add(filter.getInheritedPermission(path))
+ }
+ is InheritingContextFilter -> {
+ if (filter.priority == PERMISSION && address.hasParent() && !inherited) {
+ inherited = true
+ getPermissionsOf(address.parent, arrayOf(address.mainKey, *path), result)
+ }
+ }
+ }
+ }
+
+ return result
+ }
+ */
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/RootCommandAddress.java b/dicore3/command/src/main/java/io/dico/dicore/command/RootCommandAddress.java
index 44f0540..2b70eaf 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/RootCommandAddress.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/RootCommandAddress.java
@@ -1,275 +1,275 @@
-package io.dico.dicore.command;
-
-import io.dico.dicore.command.parameter.ArgumentBuffer;
-import io.dico.dicore.command.registration.BukkitCommand;
-import org.bukkit.Location;
-import org.bukkit.command.CommandSender;
-
-import java.util.*;
-
-public class RootCommandAddress extends ModifiableCommandAddress implements ICommandDispatcher {
- @Deprecated
- public static final RootCommandAddress INSTANCE = new RootCommandAddress();
-
- public RootCommandAddress() {
- }
-
- @Override
- public Command getCommand() {
- return null;
- }
-
- @Override
- public boolean isRoot() {
- return true;
- }
-
- @Override
- public List<String> getNames() {
- return Collections.emptyList();
- }
-
- @Override
- public ModifiableCommandAddress getParent() {
- return null;
- }
-
- @Override
- public String getMainKey() {
- return null;
- }
-
- @Override
- public String getAddress() {
- return "";
- }
-
- @Override
- public void registerToCommandMap(String fallbackPrefix, Map<String, org.bukkit.command.Command> map, EOverridePolicy overridePolicy) {
- Objects.requireNonNull(overridePolicy);
- //debugChildren(this);
- Map<String, ChildCommandAddress> children = this.children;
- Map<ChildCommandAddress, BukkitCommand> wrappers = new IdentityHashMap<>();
-
- for (ChildCommandAddress address : children.values()) {
- if (!wrappers.containsKey(address)) {
- wrappers.put(address, new BukkitCommand(address));
- }
- }
-
- for (Map.Entry<String, ChildCommandAddress> entry : children.entrySet()) {
- String key = entry.getKey();
- ChildCommandAddress address = entry.getValue();
- boolean override = overridePolicy == EOverridePolicy.OVERRIDE_ALL;
- if (!override && key.equals(address.getMainKey())) {
- override = overridePolicy == EOverridePolicy.MAIN_KEY_ONLY || overridePolicy == EOverridePolicy.MAIN_AND_FALLBACK;
- }
-
- registerMember(map, key, wrappers.get(address), override);
-
- if (fallbackPrefix != null) {
- key = fallbackPrefix + key;
- override = overridePolicy != EOverridePolicy.OVERRIDE_NONE && overridePolicy != EOverridePolicy.MAIN_KEY_ONLY;
- registerMember(map, key, wrappers.get(address), override);
- }
- }
-
- }
-
- private static void debugChildren(ModifiableCommandAddress address) {
- Collection<String> keys = address.getChildrenMainKeys();
- for (String key : keys) {
- ChildCommandAddress child = address.getChild(key);
- System.out.println(child.getAddress());
- debugChildren(child);
- }
- }
-
- private static void registerMember(Map<String, org.bukkit.command.Command> map,
- String key, org.bukkit.command.Command value, boolean override) {
- if (override) {
- map.put(key, value);
- } else {
- map.putIfAbsent(key, value);
- }
- }
-
- @Override
- public void unregisterFromCommandMap(Map<String, org.bukkit.command.Command> map) {
- Set<ICommandAddress> children = new HashSet<>(this.children.values());
- Iterator<Map.Entry<String, org.bukkit.command.Command>> iterator = map.entrySet().iterator();
- while (iterator.hasNext()) {
- Map.Entry<String, org.bukkit.command.Command> entry = iterator.next();
- org.bukkit.command.Command cmd = entry.getValue();
- if (cmd instanceof BukkitCommand && children.contains(((BukkitCommand) cmd).getOrigin())) {
- iterator.remove();
- }
- }
- }
-
- @Override
- public ModifiableCommandAddress getDeepChild(ArgumentBuffer buffer) {
- ModifiableCommandAddress cur = this;
- ChildCommandAddress child;
- while (buffer.hasNext()) {
- child = cur.getChild(buffer.next());
- if (child == null) {
- buffer.rewind();
- return cur;
- }
-
- cur = child;
- }
- return cur;
- }
-
- @Override
- public ModifiableCommandAddress getCommandTarget(CommandSender sender, ArgumentBuffer buffer) {
- ModifiableCommandAddress cur = this;
- ChildCommandAddress child;
- while (buffer.hasNext()) {
- child = cur.getChild(buffer.next());
- if (child == null
- || (child.hasCommand() && !child.getCommand().isVisibleTo(sender))
- || (cur.hasCommand() && cur.getCommand().takePrecedenceOverSubcommand(buffer.peekPrevious(), buffer.getUnaffectingCopy()))) {
- buffer.rewind();
- break;
- }
-
- cur = child;
- }
-
- return cur;
- }
-
- @Override
- public ModifiableCommandAddress getCommandTarget(ExecutionContext context, ArgumentBuffer buffer) throws CommandException {
- CommandSender sender = context.getSender();
- ModifiableCommandAddress cur = this;
- ChildCommandAddress child;
- while (buffer.hasNext()) {
- int cursor = buffer.getCursor();
-
- child = cur.getChild(context, buffer);
-
- if (child == null
- || (context.isTabComplete() && !buffer.hasNext())
- || (child.hasCommand() && !child.getCommand().isVisibleTo(sender))
- || (cur.hasCommand() && cur.getCommand().takePrecedenceOverSubcommand(buffer.peekPrevious(), buffer.getUnaffectingCopy()))) {
- buffer.setCursor(cursor);
- break;
- }
-
- cur = child;
-
- context.setAddress(child);
- if (child.hasCommand() && child.isCommandTrailing()) {
- child.getCommand().initializeAndFilterContext(context);
- child.getCommand().execute(context.getSender(), context);
- }
- }
-
- return cur;
- }
-
- @Override
- public boolean dispatchCommand(CommandSender sender, String[] command) {
- return dispatchCommand(sender, new ArgumentBuffer(command));
- }
-
- @Override
- public boolean dispatchCommand(CommandSender sender, String usedLabel, String[] args) {
- return dispatchCommand(sender, new ArgumentBuffer(usedLabel, args));
- }
-
- @Override
- public boolean dispatchCommand(CommandSender sender, ArgumentBuffer buffer) {
- ExecutionContext context = new ExecutionContext(sender, buffer, false);
-
- ModifiableCommandAddress targetAddress = null;
-
- try {
- targetAddress = getCommandTarget(context, buffer);
- Command target = targetAddress.getCommand();
-
- if (target == null) {
- if (targetAddress.hasHelpCommand()) {
- target = targetAddress.getHelpCommand().getCommand();
- } else {
- return false;
- }
- }
-
- context.setCommand(target);
-
- if (!targetAddress.isCommandTrailing()) {
- target.initializeAndFilterContext(context);
- String message = target.execute(sender, context);
- if (message != null && !message.isEmpty()) {
- context.sendMessage(EMessageType.RESULT, message);
- }
- }
-
- } catch (Throwable t) {
- if (targetAddress == null) {
- targetAddress = this;
- }
- targetAddress.getChatHandler().handleException(sender, context, t);
- }
-
- return true;
- }
-
- @Override
- public List<String> getTabCompletions(CommandSender sender, Location location, String[] args) {
- return getTabCompletions(sender, location, new ArgumentBuffer(args));
- }
-
- @Override
- public List<String> getTabCompletions(CommandSender sender, String usedLabel, Location location, String[] args) {
- return getTabCompletions(sender, location, new ArgumentBuffer(usedLabel, args));
- }
-
- @Override
- public List<String> getTabCompletions(CommandSender sender, Location location, ArgumentBuffer buffer) {
- ExecutionContext context = new ExecutionContext(sender, buffer, true);
-
- try {
- ICommandAddress target = getCommandTarget(context, buffer);
-
- List<String> out;
- if (target.hasCommand()) {
- context.setCommand(target.getCommand());
- target.getCommand().initializeAndFilterContext(context);
- out = target.getCommand().tabComplete(sender, context, location);
- } else {
- out = Collections.emptyList();
- }
-
- int cursor = buffer.getCursor();
- String input;
- if (cursor >= buffer.size()) {
- input = "";
- } else {
- input = buffer.get(cursor).toLowerCase();
- }
-
- boolean wrapped = false;
- for (String child : target.getChildrenMainKeys()) {
- if (child.toLowerCase().startsWith(input)) {
- if (!wrapped) {
- out = new ArrayList<>(out);
- wrapped = true;
- }
- out.add(child);
- }
- }
-
- return out;
-
- } catch (CommandException ex) {
- return Collections.emptyList();
- }
-
- }
-}
+package io.dico.dicore.command;
+
+import io.dico.dicore.command.parameter.ArgumentBuffer;
+import io.dico.dicore.command.registration.BukkitCommand;
+import org.bukkit.Location;
+import org.bukkit.command.CommandSender;
+
+import java.util.*;
+
+public class RootCommandAddress extends ModifiableCommandAddress implements ICommandDispatcher {
+ @Deprecated
+ public static final RootCommandAddress INSTANCE = new RootCommandAddress();
+
+ public RootCommandAddress() {
+ }
+
+ @Override
+ public Command getCommand() {
+ return null;
+ }
+
+ @Override
+ public boolean isRoot() {
+ return true;
+ }
+
+ @Override
+ public List<String> getNames() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public ModifiableCommandAddress getParent() {
+ return null;
+ }
+
+ @Override
+ public String getMainKey() {
+ return null;
+ }
+
+ @Override
+ public String getAddress() {
+ return "";
+ }
+
+ @Override
+ public void registerToCommandMap(String fallbackPrefix, Map<String, org.bukkit.command.Command> map, EOverridePolicy overridePolicy) {
+ Objects.requireNonNull(overridePolicy);
+ //debugChildren(this);
+ Map<String, ChildCommandAddress> children = this.children;
+ Map<ChildCommandAddress, BukkitCommand> wrappers = new IdentityHashMap<>();
+
+ for (ChildCommandAddress address : children.values()) {
+ if (!wrappers.containsKey(address)) {
+ wrappers.put(address, new BukkitCommand(address));
+ }
+ }
+
+ for (Map.Entry<String, ChildCommandAddress> entry : children.entrySet()) {
+ String key = entry.getKey();
+ ChildCommandAddress address = entry.getValue();
+ boolean override = overridePolicy == EOverridePolicy.OVERRIDE_ALL;
+ if (!override && key.equals(address.getMainKey())) {
+ override = overridePolicy == EOverridePolicy.MAIN_KEY_ONLY || overridePolicy == EOverridePolicy.MAIN_AND_FALLBACK;
+ }
+
+ registerMember(map, key, wrappers.get(address), override);
+
+ if (fallbackPrefix != null) {
+ key = fallbackPrefix + key;
+ override = overridePolicy != EOverridePolicy.OVERRIDE_NONE && overridePolicy != EOverridePolicy.MAIN_KEY_ONLY;
+ registerMember(map, key, wrappers.get(address), override);
+ }
+ }
+
+ }
+
+ private static void debugChildren(ModifiableCommandAddress address) {
+ Collection<String> keys = address.getChildrenMainKeys();
+ for (String key : keys) {
+ ChildCommandAddress child = address.getChild(key);
+ System.out.println(child.getAddress());
+ debugChildren(child);
+ }
+ }
+
+ private static void registerMember(Map<String, org.bukkit.command.Command> map,
+ String key, org.bukkit.command.Command value, boolean override) {
+ if (override) {
+ map.put(key, value);
+ } else {
+ map.putIfAbsent(key, value);
+ }
+ }
+
+ @Override
+ public void unregisterFromCommandMap(Map<String, org.bukkit.command.Command> map) {
+ Set<ICommandAddress> children = new HashSet<>(this.children.values());
+ Iterator<Map.Entry<String, org.bukkit.command.Command>> iterator = map.entrySet().iterator();
+ while (iterator.hasNext()) {
+ Map.Entry<String, org.bukkit.command.Command> entry = iterator.next();
+ org.bukkit.command.Command cmd = entry.getValue();
+ if (cmd instanceof BukkitCommand && children.contains(((BukkitCommand) cmd).getOrigin())) {
+ iterator.remove();
+ }
+ }
+ }
+
+ @Override
+ public ModifiableCommandAddress getDeepChild(ArgumentBuffer buffer) {
+ ModifiableCommandAddress cur = this;
+ ChildCommandAddress child;
+ while (buffer.hasNext()) {
+ child = cur.getChild(buffer.next());
+ if (child == null) {
+ buffer.rewind();
+ return cur;
+ }
+
+ cur = child;
+ }
+ return cur;
+ }
+
+ @Override
+ public ModifiableCommandAddress getCommandTarget(CommandSender sender, ArgumentBuffer buffer) {
+ ModifiableCommandAddress cur = this;
+ ChildCommandAddress child;
+ while (buffer.hasNext()) {
+ child = cur.getChild(buffer.next());
+ if (child == null
+ || (child.hasCommand() && !child.getCommand().isVisibleTo(sender))
+ || (cur.hasCommand() && cur.getCommand().takePrecedenceOverSubcommand(buffer.peekPrevious(), buffer.getUnaffectingCopy()))) {
+ buffer.rewind();
+ break;
+ }
+
+ cur = child;
+ }
+
+ return cur;
+ }
+
+ @Override
+ public ModifiableCommandAddress getCommandTarget(ExecutionContext context, ArgumentBuffer buffer) throws CommandException {
+ CommandSender sender = context.getSender();
+ ModifiableCommandAddress cur = this;
+ ChildCommandAddress child;
+ while (buffer.hasNext()) {
+ int cursor = buffer.getCursor();
+
+ child = cur.getChild(context, buffer);
+
+ if (child == null
+ || (context.isTabComplete() && !buffer.hasNext())
+ || (child.hasCommand() && !child.getCommand().isVisibleTo(sender))
+ || (cur.hasCommand() && cur.getCommand().takePrecedenceOverSubcommand(buffer.peekPrevious(), buffer.getUnaffectingCopy()))) {
+ buffer.setCursor(cursor);
+ break;
+ }
+
+ cur = child;
+
+ context.setAddress(child);
+ if (child.hasCommand() && child.isCommandTrailing()) {
+ child.getCommand().initializeAndFilterContext(context);
+ child.getCommand().execute(context.getSender(), context);
+ }
+ }
+
+ return cur;
+ }
+
+ @Override
+ public boolean dispatchCommand(CommandSender sender, String[] command) {
+ return dispatchCommand(sender, new ArgumentBuffer(command));
+ }
+
+ @Override
+ public boolean dispatchCommand(CommandSender sender, String usedLabel, String[] args) {
+ return dispatchCommand(sender, new ArgumentBuffer(usedLabel, args));
+ }
+
+ @Override
+ public boolean dispatchCommand(CommandSender sender, ArgumentBuffer buffer) {
+ ExecutionContext context = new ExecutionContext(sender, buffer, false);
+
+ ModifiableCommandAddress targetAddress = null;
+
+ try {
+ targetAddress = getCommandTarget(context, buffer);
+ Command target = targetAddress.getCommand();
+
+ if (target == null) {
+ if (targetAddress.hasHelpCommand()) {
+ target = targetAddress.getHelpCommand().getCommand();
+ } else {
+ return false;
+ }
+ }
+
+ context.setCommand(target);
+
+ if (!targetAddress.isCommandTrailing()) {
+ target.initializeAndFilterContext(context);
+ String message = target.execute(sender, context);
+ if (message != null && !message.isEmpty()) {
+ context.sendMessage(EMessageType.RESULT, message);
+ }
+ }
+
+ } catch (Throwable t) {
+ if (targetAddress == null) {
+ targetAddress = this;
+ }
+ targetAddress.getChatHandler().handleException(sender, context, t);
+ }
+
+ return true;
+ }
+
+ @Override
+ public List<String> getTabCompletions(CommandSender sender, Location location, String[] args) {
+ return getTabCompletions(sender, location, new ArgumentBuffer(args));
+ }
+
+ @Override
+ public List<String> getTabCompletions(CommandSender sender, String usedLabel, Location location, String[] args) {
+ return getTabCompletions(sender, location, new ArgumentBuffer(usedLabel, args));
+ }
+
+ @Override
+ public List<String> getTabCompletions(CommandSender sender, Location location, ArgumentBuffer buffer) {
+ ExecutionContext context = new ExecutionContext(sender, buffer, true);
+
+ try {
+ ICommandAddress target = getCommandTarget(context, buffer);
+
+ List<String> out;
+ if (target.hasCommand()) {
+ context.setCommand(target.getCommand());
+ target.getCommand().initializeAndFilterContext(context);
+ out = target.getCommand().tabComplete(sender, context, location);
+ } else {
+ out = Collections.emptyList();
+ }
+
+ int cursor = buffer.getCursor();
+ String input;
+ if (cursor >= buffer.size()) {
+ input = "";
+ } else {
+ input = buffer.get(cursor).toLowerCase();
+ }
+
+ boolean wrapped = false;
+ for (String child : target.getChildrenMainKeys()) {
+ if (child.toLowerCase().startsWith(input)) {
+ if (!wrapped) {
+ out = new ArrayList<>(out);
+ wrapped = true;
+ }
+ out.add(child);
+ }
+ }
+
+ return out;
+
+ } catch (CommandException ex) {
+ return Collections.emptyList();
+ }
+
+ }
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/Validate.java b/dicore3/command/src/main/java/io/dico/dicore/command/Validate.java
index 596ad08..3b1f7d4 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/Validate.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/Validate.java
@@ -1,52 +1,52 @@
-package io.dico.dicore.command;
-
-import org.bukkit.command.CommandSender;
-import org.bukkit.command.ConsoleCommandSender;
-import org.bukkit.entity.Player;
-
-import java.util.Optional;
-
-public class Validate {
-
- private Validate() {
-
- }
-
- //@Contract("false, _ -> fail")
- public static void isTrue(boolean expression, String failMessage) throws CommandException {
- if (!expression) {
- throw new CommandException(failMessage);
- }
- }
-
- //@Contract("null, _ -> fail")
- public static void notNull(Object obj, String failMessage) throws CommandException {
- Validate.isTrue(obj != null, failMessage);
- }
-
- public static void isAuthorized(CommandSender sender, String permission, String failMessage) throws CommandException {
- Validate.isTrue(sender.hasPermission(permission), failMessage);
- }
-
- public static void isAuthorized(CommandSender sender, String permission) throws CommandException {
- Validate.isAuthorized(sender, permission, "You do not have permission to use that command");
- }
-
- //@Contract("null -> fail")
- public static void isPlayer(CommandSender sender) throws CommandException {
- isTrue(sender instanceof Player, "That command can only be used by players");
- }
-
- //@Contract("null -> fail")
- public static void isConsole(CommandSender sender) throws CommandException {
- isTrue(sender instanceof ConsoleCommandSender, "That command can only be used by the console");
- }
-
- public static <T> T returnIfPresent(Optional<T> maybe, String failMessage) throws CommandException {
- if (!maybe.isPresent()) {
- throw new CommandException(failMessage);
- }
- return maybe.get();
- }
-
-}
+package io.dico.dicore.command;
+
+import org.bukkit.command.CommandSender;
+import org.bukkit.command.ConsoleCommandSender;
+import org.bukkit.entity.Player;
+
+import java.util.Optional;
+
+public class Validate {
+
+ private Validate() {
+
+ }
+
+ //@Contract("false, _ -> fail")
+ public static void isTrue(boolean expression, String failMessage) throws CommandException {
+ if (!expression) {
+ throw new CommandException(failMessage);
+ }
+ }
+
+ //@Contract("null, _ -> fail")
+ public static void notNull(Object obj, String failMessage) throws CommandException {
+ Validate.isTrue(obj != null, failMessage);
+ }
+
+ public static void isAuthorized(CommandSender sender, String permission, String failMessage) throws CommandException {
+ Validate.isTrue(sender.hasPermission(permission), failMessage);
+ }
+
+ public static void isAuthorized(CommandSender sender, String permission) throws CommandException {
+ Validate.isAuthorized(sender, permission, "You do not have permission to use that command");
+ }
+
+ //@Contract("null -> fail")
+ public static void isPlayer(CommandSender sender) throws CommandException {
+ isTrue(sender instanceof Player, "That command can only be used by players");
+ }
+
+ //@Contract("null -> fail")
+ public static void isConsole(CommandSender sender) throws CommandException {
+ isTrue(sender instanceof ConsoleCommandSender, "That command can only be used by the console");
+ }
+
+ public static <T> T returnIfPresent(Optional<T> maybe, String failMessage) throws CommandException {
+ if (!maybe.isPresent()) {
+ throw new CommandException(failMessage);
+ }
+ return maybe.get();
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/BigRange.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/BigRange.java
index 467ba4b..b0277c6 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/BigRange.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/BigRange.java
@@ -1,52 +1,52 @@
-package io.dico.dicore.command.annotation;
-
-import io.dico.dicore.command.parameter.type.ParameterConfig;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.PARAMETER)
-public @interface BigRange {
- Class<?> MEMORY_CLASS = Memory.class;
- ParameterConfig<BigRange, Memory> CONFIG = ParameterConfig.getMemoryClassFromField(BigRange.class);
- Memory DEFAULT = new Memory("MIN", "MAX", "0");
-
- String min() default "MIN";
-
- String max() default "MAX";
-
- String defaultValue() default "0";
-
- class Memory {
- private final String min;
- private final String max;
- private final String defaultValue;
-
- public Memory(BigRange range) {
- this(range.min(), range.max(), range.defaultValue());
- }
-
- public Memory(String min, String max, String defaultValue) {
- this.min = min;
- this.max = max;
- this.defaultValue = defaultValue;
- }
-
- public String min() {
- return min;
- }
-
- public String max() {
- return max;
- }
-
- public String defaultValue() {
- return defaultValue;
- }
-
- }
-
-}
+package io.dico.dicore.command.annotation;
+
+import io.dico.dicore.command.parameter.type.ParameterConfig;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.PARAMETER)
+public @interface BigRange {
+ Class<?> MEMORY_CLASS = Memory.class;
+ ParameterConfig<BigRange, Memory> CONFIG = ParameterConfig.getMemoryClassFromField(BigRange.class);
+ Memory DEFAULT = new Memory("MIN", "MAX", "0");
+
+ String min() default "MIN";
+
+ String max() default "MAX";
+
+ String defaultValue() default "0";
+
+ class Memory {
+ private final String min;
+ private final String max;
+ private final String defaultValue;
+
+ public Memory(BigRange range) {
+ this(range.min(), range.max(), range.defaultValue());
+ }
+
+ public Memory(String min, String max, String defaultValue) {
+ this.min = min;
+ this.max = max;
+ this.defaultValue = defaultValue;
+ }
+
+ public String min() {
+ return min;
+ }
+
+ public String max() {
+ return max;
+ }
+
+ public String defaultValue() {
+ return defaultValue;
+ }
+
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Cmd.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Cmd.java
index 109490a..dfad0bb 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Cmd.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Cmd.java
@@ -1,16 +1,16 @@
-package io.dico.dicore.command.annotation;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.METHOD)
-public @interface Cmd {
-
- String value();
-
- String[] aliases() default {};
-
-}
+package io.dico.dicore.command.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface Cmd {
+
+ String value();
+
+ String[] aliases() default {};
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/CmdParamType.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/CmdParamType.java
index ea51e44..3eb2d53 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/CmdParamType.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/CmdParamType.java
@@ -1,27 +1,27 @@
-package io.dico.dicore.command.annotation;
-
-import io.dico.dicore.command.parameter.type.IParameterTypeSelector;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Annotation to mark methods that register a parameter type to the localized selector for use in reflective commands.
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.METHOD)
-public @interface CmdParamType {
-
- /**
- * If this flag is set, the type is registered without its annotation type.
- * As a result, the {@link IParameterTypeSelector} is more likely to select it (faster).
- * This is irrelevant if there is no annotation type or param config.
- *
- * @return true if this parameter type should be registered without its annotation type too
- */
- boolean infolessAlias() default false;
-
-}
-
+package io.dico.dicore.command.annotation;
+
+import io.dico.dicore.command.parameter.type.IParameterTypeSelector;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation to mark methods that register a parameter type to the localized selector for use in reflective commands.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface CmdParamType {
+
+ /**
+ * If this flag is set, the type is registered without its annotation type.
+ * As a result, the {@link IParameterTypeSelector} is more likely to select it (faster).
+ * This is irrelevant if there is no annotation type or param config.
+ *
+ * @return true if this parameter type should be registered without its annotation type too
+ */
+ boolean infolessAlias() default false;
+
+}
+
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/CommandAnnotationUtils.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/CommandAnnotationUtils.java
index 868884c..7983997 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/CommandAnnotationUtils.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/CommandAnnotationUtils.java
@@ -1,35 +1,35 @@
-package io.dico.dicore.command.annotation;
-
-public class CommandAnnotationUtils {
-
- /**
- * Get the short description from a {@link Desc} annotation.
- * If {@link Desc#shortVersion()} is given, returns that.
- * Otherwise, returns the first element of {@link Desc#value()}
- * If neither is available, returns null.
- *
- * @param desc the annotation
- * @return the short description
- */
- public static String getShortDescription(Desc desc) {
- String descString;
- if (desc == null) {
- descString = null;
- } else if (!desc.shortVersion().isEmpty()) {
- descString = desc.shortVersion();
- } else if (desc.value().length > 0) {
- descString = desc.value()[0];
- if (desc.value().length > 1) {
- //System.out.println("[Command Warning] Multiline descriptions not supported here. Keep it short for: " + targetIdentifier);
- }
- if (descString != null && descString.isEmpty()) {
- descString = null;
- }
- } else {
- descString = null;
- }
-
- return descString;
- }
-
-}
+package io.dico.dicore.command.annotation;
+
+public class CommandAnnotationUtils {
+
+ /**
+ * Get the short description from a {@link Desc} annotation.
+ * If {@link Desc#shortVersion()} is given, returns that.
+ * Otherwise, returns the first element of {@link Desc#value()}
+ * If neither is available, returns null.
+ *
+ * @param desc the annotation
+ * @return the short description
+ */
+ public static String getShortDescription(Desc desc) {
+ String descString;
+ if (desc == null) {
+ descString = null;
+ } else if (!desc.shortVersion().isEmpty()) {
+ descString = desc.shortVersion();
+ } else if (desc.value().length > 0) {
+ descString = desc.value()[0];
+ if (desc.value().length > 1) {
+ //System.out.println("[Command Warning] Multiline descriptions not supported here. Keep it short for: " + targetIdentifier);
+ }
+ if (descString != null && descString.isEmpty()) {
+ descString = null;
+ }
+ } else {
+ descString = null;
+ }
+
+ return descString;
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Desc.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Desc.java
index 0011fb8..ab3e555 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Desc.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Desc.java
@@ -1,27 +1,27 @@
-package io.dico.dicore.command.annotation;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-@Retention(RetentionPolicy.RUNTIME)
-public @interface Desc {
-
- /**
- * Multiline description if {@link #shortVersion} is set.
- * Otherwise, this should be an array with one element (aka, you don't have to add array brackets).
- *
- * @return the multiline description.
- * @see CommandAnnotationUtils#getShortDescription(Desc)
- */
- String[] value();
-
- /**
- * Short description, use if {@link #value} is multi-line.
- * To get a short description from a {@link Desc}, you should use {@link CommandAnnotationUtils#getShortDescription(Desc)}
- *
- * @return short description
- * @see CommandAnnotationUtils#getShortDescription(Desc)
- */
- String shortVersion() default "";
-
-}
+package io.dico.dicore.command.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Desc {
+
+ /**
+ * Multiline description if {@link #shortVersion} is set.
+ * Otherwise, this should be an array with one element (aka, you don't have to add array brackets).
+ *
+ * @return the multiline description.
+ * @see CommandAnnotationUtils#getShortDescription(Desc)
+ */
+ String[] value();
+
+ /**
+ * Short description, use if {@link #value} is multi-line.
+ * To get a short description from a {@link Desc}, you should use {@link CommandAnnotationUtils#getShortDescription(Desc)}
+ *
+ * @return short description
+ * @see CommandAnnotationUtils#getShortDescription(Desc)
+ */
+ String shortVersion() default "";
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Flag.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Flag.java
index 31a47dd..edc50ef 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Flag.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Flag.java
@@ -1,16 +1,16 @@
-package io.dico.dicore.command.annotation;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.PARAMETER)
-public @interface Flag {
-
- String value() default "";
-
- String permission() default "";
-
-}
+package io.dico.dicore.command.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.PARAMETER)
+public @interface Flag {
+
+ String value() default "";
+
+ String permission() default "";
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/GenerateCommands.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/GenerateCommands.java
index 9b7164d..eeff351 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/GenerateCommands.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/GenerateCommands.java
@@ -1,14 +1,14 @@
-package io.dico.dicore.command.annotation;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.METHOD)
-public @interface GenerateCommands {
-
- String[] value() default {"help"};
-
-}
+package io.dico.dicore.command.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface GenerateCommands {
+
+ String[] value() default {"help"};
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/GroupMatchedCommands.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/GroupMatchedCommands.java
index 53e3e9e..de89e00 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/GroupMatchedCommands.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/GroupMatchedCommands.java
@@ -1,68 +1,68 @@
-package io.dico.dicore.command.annotation;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Annotation to define sub-groups of the group registered reflectively from all methods in a class.
- * <p>
- * Commands are selected for grouping by matching their method's names to a regular expression.
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.TYPE)
-public @interface GroupMatchedCommands {
-
- @Retention(RetentionPolicy.RUNTIME)
- @interface GroupEntry {
-
- /**
- * Regular expression to match method names for this group
- * Must be non-empty
- *
- * @return the regular expression
- */
- String regex();
-
- /**
- * The name or main key of the sub-group or address
- * Must be non-empty
- *
- * @return the group name
- */
- String group();
-
- /**
- * The aliases for the sub-group
- *
- * @return the group aliases
- */
- String[] groupAliases() default {};
-
- /**
- * Generated (predefined) commands for the sub-group
- */
- String[] generatedCommands() default {};
-
- /**
- * @see Desc
- */
- String[] description() default {};
-
- /**
- * @see Desc
- */
- String shortDescription() default "";
- }
-
- /**
- * The defined groups.
- * If a method name matches the regex of multiple groups,
- * groups are prioritized by the order in which they appear in this array.
- *
- * @return the defined groups
- */
- GroupEntry[] value();
-
-}
+package io.dico.dicore.command.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation to define sub-groups of the group registered reflectively from all methods in a class.
+ * <p>
+ * Commands are selected for grouping by matching their method's names to a regular expression.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface GroupMatchedCommands {
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface GroupEntry {
+
+ /**
+ * Regular expression to match method names for this group
+ * Must be non-empty
+ *
+ * @return the regular expression
+ */
+ String regex();
+
+ /**
+ * The name or main key of the sub-group or address
+ * Must be non-empty
+ *
+ * @return the group name
+ */
+ String group();
+
+ /**
+ * The aliases for the sub-group
+ *
+ * @return the group aliases
+ */
+ String[] groupAliases() default {};
+
+ /**
+ * Generated (predefined) commands for the sub-group
+ */
+ String[] generatedCommands() default {};
+
+ /**
+ * @see Desc
+ */
+ String[] description() default {};
+
+ /**
+ * @see Desc
+ */
+ String shortDescription() default "";
+ }
+
+ /**
+ * The defined groups.
+ * If a method name matches the regex of multiple groups,
+ * groups are prioritized by the order in which they appear in this array.
+ *
+ * @return the defined groups
+ */
+ GroupEntry[] value();
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/NamedArg.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/NamedArg.java
index fa42e6b..d84358b 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/NamedArg.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/NamedArg.java
@@ -1,14 +1,14 @@
-package io.dico.dicore.command.annotation;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.PARAMETER)
-public @interface NamedArg {
-
- String value();
-
-}
+package io.dico.dicore.command.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.PARAMETER)
+public @interface NamedArg {
+
+ String value();
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/PreprocessArgs.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/PreprocessArgs.java
index 57f53bd..32b0723 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/PreprocessArgs.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/PreprocessArgs.java
@@ -1,17 +1,17 @@
-package io.dico.dicore.command.annotation;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.METHOD)
-@Deprecated
-public @interface PreprocessArgs {
-
- String tokens() default "\"\"";
-
- char escapeChar() default '\\';
-
-}
+package io.dico.dicore.command.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+@Deprecated
+public @interface PreprocessArgs {
+
+ String tokens() default "\"\"";
+
+ char escapeChar() default '\\';
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Range.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Range.java
index 3fd4160..98aec22 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Range.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Range.java
@@ -1,67 +1,67 @@
-package io.dico.dicore.command.annotation;
-
-import io.dico.dicore.command.CommandException;
-import io.dico.dicore.command.Validate;
-import io.dico.dicore.command.parameter.type.ParameterConfig;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.PARAMETER)
-public @interface Range {
- Class<?> MEMORY_CLASS = Memory.class;
- ParameterConfig<Range, Memory> CONFIG = ParameterConfig.getMemoryClassFromField(Range.class);
- Memory DEFAULT = new Memory(-Double.MAX_VALUE, Double.MAX_VALUE, 0);
-
- double min() default -Double.MAX_VALUE;
-
- double max() default Double.MAX_VALUE;
-
- double defaultValue() default 0;
-
- class Memory {
- private final double min;
- private final double max;
- private final double defaultValue;
-
- public Memory(Range range) {
- this(range.min(), range.max(), range.defaultValue());
- }
-
- public Memory(double min, double max, double defaultValue) {
- this.min = min;
- this.max = max;
- this.defaultValue = defaultValue;
- }
-
- public double min() {
- return min;
- }
-
- public double max() {
- return max;
- }
-
- public double defaultValue() {
- return defaultValue;
- }
-
- public void validate(Number x, String failMessage) throws CommandException {
- Validate.isTrue(valid(x), failMessage);
- }
-
- public boolean valid(Number x) {
- double d = x.doubleValue();
- return min <= d && d <= max;
- }
-
- public boolean isDefault() {
- return this == DEFAULT || (min == DEFAULT.min && max == DEFAULT.max && defaultValue == DEFAULT.defaultValue);
- }
-
- }
-
-}
+package io.dico.dicore.command.annotation;
+
+import io.dico.dicore.command.CommandException;
+import io.dico.dicore.command.Validate;
+import io.dico.dicore.command.parameter.type.ParameterConfig;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.PARAMETER)
+public @interface Range {
+ Class<?> MEMORY_CLASS = Memory.class;
+ ParameterConfig<Range, Memory> CONFIG = ParameterConfig.getMemoryClassFromField(Range.class);
+ Memory DEFAULT = new Memory(-Double.MAX_VALUE, Double.MAX_VALUE, 0);
+
+ double min() default -Double.MAX_VALUE;
+
+ double max() default Double.MAX_VALUE;
+
+ double defaultValue() default 0;
+
+ class Memory {
+ private final double min;
+ private final double max;
+ private final double defaultValue;
+
+ public Memory(Range range) {
+ this(range.min(), range.max(), range.defaultValue());
+ }
+
+ public Memory(double min, double max, double defaultValue) {
+ this.min = min;
+ this.max = max;
+ this.defaultValue = defaultValue;
+ }
+
+ public double min() {
+ return min;
+ }
+
+ public double max() {
+ return max;
+ }
+
+ public double defaultValue() {
+ return defaultValue;
+ }
+
+ public void validate(Number x, String failMessage) throws CommandException {
+ Validate.isTrue(valid(x), failMessage);
+ }
+
+ public boolean valid(Number x) {
+ double d = x.doubleValue();
+ return min <= d && d <= max;
+ }
+
+ public boolean isDefault() {
+ return this == DEFAULT || (min == DEFAULT.min && max == DEFAULT.max && defaultValue == DEFAULT.defaultValue);
+ }
+
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequireConsole.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequireConsole.java
index 362f05c..3510ca8 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequireConsole.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequireConsole.java
@@ -1,11 +1,11 @@
-package io.dico.dicore.command.annotation;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.METHOD)
-public @interface RequireConsole {
-}
+package io.dico.dicore.command.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface RequireConsole {
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequireParameters.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequireParameters.java
index 02f5548..68d5706 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequireParameters.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequireParameters.java
@@ -1,14 +1,14 @@
-package io.dico.dicore.command.annotation;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.METHOD)
-public @interface RequireParameters {
-
- int value();
-
-}
+package io.dico.dicore.command.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface RequireParameters {
+
+ int value();
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequirePermissions.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequirePermissions.java
index d2ba782..13d7167 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequirePermissions.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequirePermissions.java
@@ -1,33 +1,33 @@
-package io.dico.dicore.command.annotation;
-
-import io.dico.dicore.command.IContextFilter;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * If this annotation is not present, inheriting permissions is default.
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.METHOD)
-public @interface RequirePermissions {
-
- /**
- * Any permissions that must be present on the sender
- *
- * @return an array of permission nodes
- */
- String[] value();
-
- /**
- * Whether permissions should (also) be inherited from the parent.
- * This uses {@link IContextFilter#INHERIT_PERMISSIONS}
- * This is true by default.
- *
- * @return true if permissions should be inherited.
- */
- boolean inherit() default true;
-
-}
+package io.dico.dicore.command.annotation;
+
+import io.dico.dicore.command.IContextFilter;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * If this annotation is not present, inheriting permissions is default.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface RequirePermissions {
+
+ /**
+ * Any permissions that must be present on the sender
+ *
+ * @return an array of permission nodes
+ */
+ String[] value();
+
+ /**
+ * Whether permissions should (also) be inherited from the parent.
+ * This uses {@link IContextFilter#INHERIT_PERMISSIONS}
+ * This is true by default.
+ *
+ * @return true if permissions should be inherited.
+ */
+ boolean inherit() default true;
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequirePlayer.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequirePlayer.java
index 2165e05..3f447f9 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequirePlayer.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequirePlayer.java
@@ -1,11 +1,11 @@
-package io.dico.dicore.command.annotation;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.METHOD)
-public @interface RequirePlayer {
-}
+package io.dico.dicore.command.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface RequirePlayer {
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/AbstractChatHandler.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/AbstractChatHandler.java
index c0d23ee..ac2a76d 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/AbstractChatHandler.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/AbstractChatHandler.java
@@ -1,94 +1,94 @@
-package io.dico.dicore.command.chat;
-
-import io.dico.dicore.Formatting;
-import io.dico.dicore.command.EMessageType;
-import io.dico.dicore.command.ExecutionContext;
-import io.dico.dicore.command.ICommandAddress;
-import io.dico.dicore.command.chat.help.HelpPages;
-import org.bukkit.command.CommandSender;
-import org.jetbrains.annotations.NotNull;
-
-public class AbstractChatHandler implements IChatHandler {
- private @NotNull HelpPages helpPages;
-
- public AbstractChatHandler(@NotNull HelpPages helpPages) {
- this.helpPages = helpPages;
- }
-
- public AbstractChatHandler() {
- this(HelpPages.newDefaultHelpPages());
- }
-
- @NotNull
- public HelpPages getHelpPages() {
- return helpPages;
- }
-
- public void setHelpPages(@NotNull HelpPages helpPages) {
- this.helpPages = helpPages;
- }
-
- @Override
- public Formatting getChatFormatForType(EMessageType type) {
- switch (type) {
- case EXCEPTION:
- case BAD_NEWS:
- return Formatting.RED;
- case INSTRUCTION:
- case NEUTRAL:
- return Formatting.GRAY;
- case CUSTOM:
- return Formatting.WHITE;
- case INFORMATIVE:
- return Formatting.AQUA;
- case RESULT:
- default:
- case GOOD_NEWS:
- return Formatting.GREEN;
- case WARNING:
- return Formatting.YELLOW;
-
- case DESCRIPTION:
- return Formatting.GREEN;
- case SYNTAX:
- return Formatting.AQUA;
- case HIGHLIGHT:
- return Formatting.RED;
- case SUBCOMMAND:
- return Formatting.GRAY;
- case NUMBER:
- return Formatting.YELLOW;
- }
- }
-
- @Override
- public String getMessagePrefixForType(EMessageType type) {
- return "";
- }
-
- protected String createMessage(EMessageType type, String message) {
- if (message == null || message.isEmpty()) return null;
- return getMessagePrefixForType(type) + getChatFormatForType(type) + message;
- }
-
- @Override
- public String createMessage(ExecutionContext context, EMessageType type, String message) {
- return createMessage(type, message);
- }
-
- @Override
- public String createMessage(CommandSender sender, EMessageType type, String message) {
- return createMessage(type, message);
- }
-
- @Override
- public String createHelpMessage(CommandSender sender, ExecutionContext context, ICommandAddress address, int page) {
- return helpPages.getHelpPage(sender, context, address, page);
- }
-
- @Override
- public String createSyntaxMessage(CommandSender sender, ExecutionContext context, ICommandAddress address) {
- return helpPages.getSyntax(sender, context, address);
- }
-
-}
+package io.dico.dicore.command.chat;
+
+import io.dico.dicore.Formatting;
+import io.dico.dicore.command.EMessageType;
+import io.dico.dicore.command.ExecutionContext;
+import io.dico.dicore.command.ICommandAddress;
+import io.dico.dicore.command.chat.help.HelpPages;
+import org.bukkit.command.CommandSender;
+import org.jetbrains.annotations.NotNull;
+
+public class AbstractChatHandler implements IChatHandler {
+ private @NotNull HelpPages helpPages;
+
+ public AbstractChatHandler(@NotNull HelpPages helpPages) {
+ this.helpPages = helpPages;
+ }
+
+ public AbstractChatHandler() {
+ this(HelpPages.newDefaultHelpPages());
+ }
+
+ @NotNull
+ public HelpPages getHelpPages() {
+ return helpPages;
+ }
+
+ public void setHelpPages(@NotNull HelpPages helpPages) {
+ this.helpPages = helpPages;
+ }
+
+ @Override
+ public Formatting getChatFormatForType(EMessageType type) {
+ switch (type) {
+ case EXCEPTION:
+ case BAD_NEWS:
+ return Formatting.RED;
+ case INSTRUCTION:
+ case NEUTRAL:
+ return Formatting.GRAY;
+ case CUSTOM:
+ return Formatting.WHITE;
+ case INFORMATIVE:
+ return Formatting.AQUA;
+ case RESULT:
+ default:
+ case GOOD_NEWS:
+ return Formatting.GREEN;
+ case WARNING:
+ return Formatting.YELLOW;
+
+ case DESCRIPTION:
+ return Formatting.GREEN;
+ case SYNTAX:
+ return Formatting.AQUA;
+ case HIGHLIGHT:
+ return Formatting.RED;
+ case SUBCOMMAND:
+ return Formatting.GRAY;
+ case NUMBER:
+ return Formatting.YELLOW;
+ }
+ }
+
+ @Override
+ public String getMessagePrefixForType(EMessageType type) {
+ return "";
+ }
+
+ protected String createMessage(EMessageType type, String message) {
+ if (message == null || message.isEmpty()) return null;
+ return getMessagePrefixForType(type) + getChatFormatForType(type) + message;
+ }
+
+ @Override
+ public String createMessage(ExecutionContext context, EMessageType type, String message) {
+ return createMessage(type, message);
+ }
+
+ @Override
+ public String createMessage(CommandSender sender, EMessageType type, String message) {
+ return createMessage(type, message);
+ }
+
+ @Override
+ public String createHelpMessage(CommandSender sender, ExecutionContext context, ICommandAddress address, int page) {
+ return helpPages.getHelpPage(sender, context, address, page);
+ }
+
+ @Override
+ public String createSyntaxMessage(CommandSender sender, ExecutionContext context, ICommandAddress address) {
+ return helpPages.getSyntax(sender, context, address);
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/ChatHandlers.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/ChatHandlers.java
index 232d5cf..cebdb45 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/ChatHandlers.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/ChatHandlers.java
@@ -1,20 +1,20 @@
-package io.dico.dicore.command.chat;
-
-/**
- * Static factory methods for {@link IChatHandler}
- */
-public class ChatHandlers {
- private static final IChatHandler defaultChat;
-
- private ChatHandlers() {
-
- }
-
- public static IChatHandler defaultChat() {
- return defaultChat;
- }
-
- static {
- defaultChat = new AbstractChatHandler();
- }
-}
+package io.dico.dicore.command.chat;
+
+/**
+ * Static factory methods for {@link IChatHandler}
+ */
+public class ChatHandlers {
+ private static final IChatHandler defaultChat;
+
+ private ChatHandlers() {
+
+ }
+
+ public static IChatHandler defaultChat() {
+ return defaultChat;
+ }
+
+ static {
+ defaultChat = new AbstractChatHandler();
+ }
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/IChatHandler.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/IChatHandler.java
index 98283ef..0dd6002 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/IChatHandler.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/IChatHandler.java
@@ -1,60 +1,60 @@
-package io.dico.dicore.command.chat;
-
-import io.dico.dicore.Formatting;
-import io.dico.dicore.command.CommandException;
-import io.dico.dicore.command.EMessageType;
-import io.dico.dicore.command.ExecutionContext;
-import io.dico.dicore.command.ICommandAddress;
-import org.bukkit.command.CommandSender;
-
-//TODO add methods to send JSON messages
-public interface IChatHandler {
-
- default void sendMessage(ExecutionContext context, EMessageType type, String message) {
- message = createMessage(context, type, message);
- if (message != null) {
- context.getSender().sendMessage(message);
- }
- }
-
- default void sendMessage(CommandSender sender, EMessageType type, String message) {
- message = createMessage(sender, type, message);
- if (message != null) {
- sender.sendMessage(message);
- }
- }
-
- default void handleCommandException(CommandSender sender, ExecutionContext context, CommandException exception) {
- sendMessage(context, EMessageType.EXCEPTION, exception.getMessage());
- }
-
- default void handleException(CommandSender sender, ExecutionContext context, Throwable exception) {
- if (exception instanceof CommandException) {
- handleCommandException(sender, context, (CommandException) exception);
- } else {
- sendMessage(sender, EMessageType.EXCEPTION, "An internal error occurred whilst executing this command");
- exception.printStackTrace();
- }
- }
-
- default void sendHelpMessage(CommandSender sender, ExecutionContext context, ICommandAddress address, int page) {
- sender.sendMessage(createHelpMessage(sender, context, address, page));
- }
-
- default void sendSyntaxMessage(CommandSender sender, ExecutionContext context, ICommandAddress address) {
- sender.sendMessage(createSyntaxMessage(sender, context, address));
- }
-
- Formatting getChatFormatForType(EMessageType type);
-
- String getMessagePrefixForType(EMessageType type);
-
- String createMessage(ExecutionContext context, EMessageType type, String message);
-
- String createMessage(CommandSender sender, EMessageType type, String message);
-
- String createHelpMessage(CommandSender sender, ExecutionContext context, ICommandAddress address, int page);
-
- String createSyntaxMessage(CommandSender sender, ExecutionContext context, ICommandAddress address);
-
-}
+package io.dico.dicore.command.chat;
+
+import io.dico.dicore.Formatting;
+import io.dico.dicore.command.CommandException;
+import io.dico.dicore.command.EMessageType;
+import io.dico.dicore.command.ExecutionContext;
+import io.dico.dicore.command.ICommandAddress;
+import org.bukkit.command.CommandSender;
+
+//TODO add methods to send JSON messages
+public interface IChatHandler {
+
+ default void sendMessage(ExecutionContext context, EMessageType type, String message) {
+ message = createMessage(context, type, message);
+ if (message != null) {
+ context.getSender().sendMessage(message);
+ }
+ }
+
+ default void sendMessage(CommandSender sender, EMessageType type, String message) {
+ message = createMessage(sender, type, message);
+ if (message != null) {
+ sender.sendMessage(message);
+ }
+ }
+
+ default void handleCommandException(CommandSender sender, ExecutionContext context, CommandException exception) {
+ sendMessage(context, EMessageType.EXCEPTION, exception.getMessage());
+ }
+
+ default void handleException(CommandSender sender, ExecutionContext context, Throwable exception) {
+ if (exception instanceof CommandException) {
+ handleCommandException(sender, context, (CommandException) exception);
+ } else {
+ sendMessage(sender, EMessageType.EXCEPTION, "An internal error occurred whilst executing this command");
+ exception.printStackTrace();
+ }
+ }
+
+ default void sendHelpMessage(CommandSender sender, ExecutionContext context, ICommandAddress address, int page) {
+ sender.sendMessage(createHelpMessage(sender, context, address, page));
+ }
+
+ default void sendSyntaxMessage(CommandSender sender, ExecutionContext context, ICommandAddress address) {
+ sender.sendMessage(createSyntaxMessage(sender, context, address));
+ }
+
+ Formatting getChatFormatForType(EMessageType type);
+
+ String getMessagePrefixForType(EMessageType type);
+
+ String createMessage(ExecutionContext context, EMessageType type, String message);
+
+ String createMessage(CommandSender sender, EMessageType type, String message);
+
+ String createHelpMessage(CommandSender sender, ExecutionContext context, ICommandAddress address, int page);
+
+ String createSyntaxMessage(CommandSender sender, ExecutionContext context, ICommandAddress address);
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/HelpPages.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/HelpPages.java
index 4382d93..616e85a 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/HelpPages.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/HelpPages.java
@@ -1,92 +1,92 @@
-package io.dico.dicore.command.chat.help;
-
-import io.dico.dicore.command.ExecutionContext;
-import io.dico.dicore.command.ICommandAddress;
-import io.dico.dicore.command.chat.help.defaults.*;
-import org.bukkit.permissions.Permissible;
-import org.jetbrains.annotations.NotNull;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-public class HelpPages {
- private @NotNull IPageBuilder pageBuilder;
- private @NotNull IPageLayout pageLayout;
- private int pageLength;
- private @NotNull List<IHelpTopic> helpTopics;
- private @NotNull IHelpTopic syntaxTopic;
-
- public HelpPages(@NotNull IPageBuilder pageBuilder, @NotNull IPageLayout pageLayout, int pageLength, @NotNull IHelpTopic syntaxTopic, @NotNull List<IHelpTopic> helpTopics) {
- this.pageBuilder = pageBuilder;
- this.pageLayout = pageLayout;
- this.pageLength = pageLength;
- this.syntaxTopic = syntaxTopic;
- this.helpTopics = helpTopics;
- }
-
- public HelpPages(IPageBuilder pageBuilder, IPageLayout pageLayout, int pageLength, IHelpTopic syntaxTopic, IHelpTopic... helpTopics) {
- this(pageBuilder, pageLayout, pageLength, syntaxTopic, new ArrayList<>(Arrays.asList(helpTopics)));
- }
-
- @SuppressWarnings("RedundantArrayCreation")
- public static HelpPages newDefaultHelpPages() {
- IHelpTopic syntaxTopic = new SyntaxHelpTopic();
- return new HelpPages(new DefaultPageBuilder(), new DefaultPageLayout(), 12,
- syntaxTopic, new IHelpTopic[]{new DescriptionHelpTopic(), syntaxTopic, new SubcommandsHelpTopic()});
- }
-
- public @NotNull IPageBuilder getPageBuilder() {
- return pageBuilder;
- }
-
- public void setPageBuilder(@NotNull IPageBuilder pageBuilder) {
- this.pageBuilder = pageBuilder;
- }
-
- public @NotNull IPageLayout getPageLayout() {
- return pageLayout;
- }
-
- public void setPageLayout(@NotNull IPageLayout pageLayout) {
- this.pageLayout = pageLayout;
- }
-
- public int getPageLength() {
- return pageLength;
- }
-
- public void setPageLength(int pageLength) {
- this.pageLength = pageLength;
- }
-
- public @NotNull IHelpTopic getSyntaxTopic() {
- return syntaxTopic;
- }
-
- public void setSyntaxTopic(@NotNull IHelpTopic syntaxTopic) {
- this.syntaxTopic = syntaxTopic;
- }
-
- @NotNull
- public List<IHelpTopic> getHelpTopics() {
- return helpTopics;
- }
-
- public void setHelpTopics(@NotNull List<IHelpTopic> helpTopics) {
- this.helpTopics = helpTopics;
- }
-
- public @NotNull String getHelpPage(Permissible viewer, ExecutionContext context, ICommandAddress address, int page) {
- return pageBuilder.getPage(helpTopics, pageLayout, address, viewer, context, page, pageLength);
- }
-
- public @NotNull String getSyntax(Permissible viewer, ExecutionContext context, ICommandAddress address) {
- List<IHelpComponent> components = syntaxTopic.getComponents(address, viewer, context, false);
- if (components.isEmpty()) {
- return getHelpPage(viewer, context, address, 1);
- }
- return DefaultPageBuilder.combine(components);
- }
-
-}
+package io.dico.dicore.command.chat.help;
+
+import io.dico.dicore.command.ExecutionContext;
+import io.dico.dicore.command.ICommandAddress;
+import io.dico.dicore.command.chat.help.defaults.*;
+import org.bukkit.permissions.Permissible;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class HelpPages {
+ private @NotNull IPageBuilder pageBuilder;
+ private @NotNull IPageLayout pageLayout;
+ private int pageLength;
+ private @NotNull List<IHelpTopic> helpTopics;
+ private @NotNull IHelpTopic syntaxTopic;
+
+ public HelpPages(@NotNull IPageBuilder pageBuilder, @NotNull IPageLayout pageLayout, int pageLength, @NotNull IHelpTopic syntaxTopic, @NotNull List<IHelpTopic> helpTopics) {
+ this.pageBuilder = pageBuilder;
+ this.pageLayout = pageLayout;
+ this.pageLength = pageLength;
+ this.syntaxTopic = syntaxTopic;
+ this.helpTopics = helpTopics;
+ }
+
+ public HelpPages(IPageBuilder pageBuilder, IPageLayout pageLayout, int pageLength, IHelpTopic syntaxTopic, IHelpTopic... helpTopics) {
+ this(pageBuilder, pageLayout, pageLength, syntaxTopic, new ArrayList<>(Arrays.asList(helpTopics)));
+ }
+
+ @SuppressWarnings("RedundantArrayCreation")
+ public static HelpPages newDefaultHelpPages() {
+ IHelpTopic syntaxTopic = new SyntaxHelpTopic();
+ return new HelpPages(new DefaultPageBuilder(), new DefaultPageLayout(), 12,
+ syntaxTopic, new IHelpTopic[]{new DescriptionHelpTopic(), syntaxTopic, new SubcommandsHelpTopic()});
+ }
+
+ public @NotNull IPageBuilder getPageBuilder() {
+ return pageBuilder;
+ }
+
+ public void setPageBuilder(@NotNull IPageBuilder pageBuilder) {
+ this.pageBuilder = pageBuilder;
+ }
+
+ public @NotNull IPageLayout getPageLayout() {
+ return pageLayout;
+ }
+
+ public void setPageLayout(@NotNull IPageLayout pageLayout) {
+ this.pageLayout = pageLayout;
+ }
+
+ public int getPageLength() {
+ return pageLength;
+ }
+
+ public void setPageLength(int pageLength) {
+ this.pageLength = pageLength;
+ }
+
+ public @NotNull IHelpTopic getSyntaxTopic() {
+ return syntaxTopic;
+ }
+
+ public void setSyntaxTopic(@NotNull IHelpTopic syntaxTopic) {
+ this.syntaxTopic = syntaxTopic;
+ }
+
+ @NotNull
+ public List<IHelpTopic> getHelpTopics() {
+ return helpTopics;
+ }
+
+ public void setHelpTopics(@NotNull List<IHelpTopic> helpTopics) {
+ this.helpTopics = helpTopics;
+ }
+
+ public @NotNull String getHelpPage(Permissible viewer, ExecutionContext context, ICommandAddress address, int page) {
+ return pageBuilder.getPage(helpTopics, pageLayout, address, viewer, context, page, pageLength);
+ }
+
+ public @NotNull String getSyntax(Permissible viewer, ExecutionContext context, ICommandAddress address) {
+ List<IHelpComponent> components = syntaxTopic.getComponents(address, viewer, context, false);
+ if (components.isEmpty()) {
+ return getHelpPage(viewer, context, address, 1);
+ }
+ return DefaultPageBuilder.combine(components);
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/HelpTopicModifier.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/HelpTopicModifier.java
index 7aeb304..b295a35 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/HelpTopicModifier.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/HelpTopicModifier.java
@@ -1,24 +1,24 @@
-package io.dico.dicore.command.chat.help;
-
-import io.dico.dicore.command.ExecutionContext;
-import io.dico.dicore.command.ICommandAddress;
-import org.bukkit.permissions.Permissible;
-
-import java.util.List;
-import java.util.Objects;
-
-public abstract class HelpTopicModifier implements IHelpTopic {
- private final IHelpTopic delegate;
-
- public HelpTopicModifier(IHelpTopic delegate) {
- this.delegate = Objects.requireNonNull(delegate);
- }
-
- @Override
- public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) {
- return modify(delegate.getComponents(target, viewer, context, true), target, viewer, context);
- }
-
- protected abstract List<IHelpComponent> modify(List<IHelpComponent> components, ICommandAddress target, Permissible viewer, ExecutionContext context);
-
-}
+package io.dico.dicore.command.chat.help;
+
+import io.dico.dicore.command.ExecutionContext;
+import io.dico.dicore.command.ICommandAddress;
+import org.bukkit.permissions.Permissible;
+
+import java.util.List;
+import java.util.Objects;
+
+public abstract class HelpTopicModifier implements IHelpTopic {
+ private final IHelpTopic delegate;
+
+ public HelpTopicModifier(IHelpTopic delegate) {
+ this.delegate = Objects.requireNonNull(delegate);
+ }
+
+ @Override
+ public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) {
+ return modify(delegate.getComponents(target, viewer, context, true), target, viewer, context);
+ }
+
+ protected abstract List<IHelpComponent> modify(List<IHelpComponent> components, ICommandAddress target, Permissible viewer, ExecutionContext context);
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IHelpComponent.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IHelpComponent.java
index e1867b5..446c7d7 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IHelpComponent.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IHelpComponent.java
@@ -1,9 +1,9 @@
-package io.dico.dicore.command.chat.help;
-
-public interface IHelpComponent {
-
- int lineCount();
-
- void appendTo(StringBuilder sb);
-
-}
+package io.dico.dicore.command.chat.help;
+
+public interface IHelpComponent {
+
+ int lineCount();
+
+ void appendTo(StringBuilder sb);
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IHelpTopic.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IHelpTopic.java
index 0b3fba2..0f1b5c9 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IHelpTopic.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IHelpTopic.java
@@ -1,23 +1,23 @@
-package io.dico.dicore.command.chat.help;
-
-import io.dico.dicore.command.ExecutionContext;
-import io.dico.dicore.command.ICommandAddress;
-import org.bukkit.permissions.Permissible;
-
-import java.util.List;
-
-public interface IHelpTopic {
-
- /**
- * Get the components of this help topic
- *
- * @param target The address of the command to provide help about
- * @param viewer The permissible that the page will be shown to (null -> choose a default set).
- * @param context Context of the command execution
- * @param isForPage A boolean indicating if the components are to be used in a page (for help)
- * @return a mutable list of components to include in the help pages
- */
- List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage);
-
-
-}
+package io.dico.dicore.command.chat.help;
+
+import io.dico.dicore.command.ExecutionContext;
+import io.dico.dicore.command.ICommandAddress;
+import org.bukkit.permissions.Permissible;
+
+import java.util.List;
+
+public interface IHelpTopic {
+
+ /**
+ * Get the components of this help topic
+ *
+ * @param target The address of the command to provide help about
+ * @param viewer The permissible that the page will be shown to (null -> choose a default set).
+ * @param context Context of the command execution
+ * @param isForPage A boolean indicating if the components are to be used in a page (for help)
+ * @return a mutable list of components to include in the help pages
+ */
+ List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage);
+
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IPageBorder.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IPageBorder.java
index 1ae3561..2789975 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IPageBorder.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IPageBorder.java
@@ -1,7 +1,7 @@
-package io.dico.dicore.command.chat.help;
-
-public interface IPageBorder extends IHelpComponent {
-
- void setPageCount(int pageCount);
-
-}
+package io.dico.dicore.command.chat.help;
+
+public interface IPageBorder extends IHelpComponent {
+
+ void setPageCount(int pageCount);
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IPageBuilder.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IPageBuilder.java
index 86d9450..0584ade 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IPageBuilder.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IPageBuilder.java
@@ -1,13 +1,13 @@
-package io.dico.dicore.command.chat.help;
-
-import io.dico.dicore.command.ExecutionContext;
-import io.dico.dicore.command.ICommandAddress;
-import org.bukkit.permissions.Permissible;
-
-import java.util.List;
-
-public interface IPageBuilder {
-
- String getPage(List<IHelpTopic> helpTopics, IPageLayout pageLayout, ICommandAddress target, Permissible viewer, ExecutionContext context, int pageNum, int pageLen);
-
-}
+package io.dico.dicore.command.chat.help;
+
+import io.dico.dicore.command.ExecutionContext;
+import io.dico.dicore.command.ICommandAddress;
+import org.bukkit.permissions.Permissible;
+
+import java.util.List;
+
+public interface IPageBuilder {
+
+ String getPage(List<IHelpTopic> helpTopics, IPageLayout pageLayout, ICommandAddress target, Permissible viewer, ExecutionContext context, int pageNum, int pageLen);
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IPageLayout.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IPageLayout.java
index 3223e32..32c41d7 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IPageLayout.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IPageLayout.java
@@ -1,20 +1,20 @@
-package io.dico.dicore.command.chat.help;
-
-import io.dico.dicore.command.ExecutionContext;
-import io.dico.dicore.command.ICommandAddress;
-import org.bukkit.permissions.Permissible;
-
-public interface IPageLayout {
-
- /**
- * Get the page borders for a help page
- *
- * @param target the address that help is displayed for
- * @param viewer the viewer of the help page, or null if irrelevant
- * @param context the context of the execution
- * @param pageNum the page number as displayed in the help page (so it's 1-bound and not 0-bound)
- * @return the page borders.
- */
- PageBorders getPageBorders(ICommandAddress target, Permissible viewer, ExecutionContext context, int pageNum);
-
-}
+package io.dico.dicore.command.chat.help;
+
+import io.dico.dicore.command.ExecutionContext;
+import io.dico.dicore.command.ICommandAddress;
+import org.bukkit.permissions.Permissible;
+
+public interface IPageLayout {
+
+ /**
+ * Get the page borders for a help page
+ *
+ * @param target the address that help is displayed for
+ * @param viewer the viewer of the help page, or null if irrelevant
+ * @param context the context of the execution
+ * @param pageNum the page number as displayed in the help page (so it's 1-bound and not 0-bound)
+ * @return the page borders.
+ */
+ PageBorders getPageBorders(ICommandAddress target, Permissible viewer, ExecutionContext context, int pageNum);
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/PageBorders.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/PageBorders.java
index 43c0514..cd105b1 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/PageBorders.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/PageBorders.java
@@ -1,76 +1,76 @@
-package io.dico.dicore.command.chat.help;
-
-import java.util.Arrays;
-
-public class PageBorders {
- private final IPageBorder header, footer;
-
- public PageBorders(IPageBorder header, IPageBorder footer) {
- this.header = header;
- this.footer = footer;
- }
-
- public IPageBorder getHeader() {
- return header;
- }
-
- public IPageBorder getFooter() {
- return footer;
- }
-
- public static IPageBorder simpleBorder(String... lines) {
- return new SimplePageBorder(lines);
- }
-
- public static IPageBorder disappearingBorder(int pageNum, String... lines) {
- return disappearingBorder(pageNum, 0, lines);
- }
-
- public static IPageBorder disappearingBorder(int pageNum, int keptLines, String... lines) {
- return new DisappearingPageBorder(pageNum, keptLines, lines);
- }
-
- static class SimplePageBorder extends SimpleHelpComponent implements IPageBorder {
- private final String replacedSequence;
-
- public SimplePageBorder(String replacedSequence, String... lines) {
- super(lines);
- this.replacedSequence = replacedSequence;
- }
-
- public SimplePageBorder(String... lines) {
- super(lines);
- this.replacedSequence = "%pageCount%";
- }
-
- @Override
- public void setPageCount(int pageCount) {
- String[] lines = this.lines;
- for (int i = 0; i < lines.length; i++) {
- lines[i] = lines[i].replace(replacedSequence, Integer.toString(pageCount));
- }
- }
-
- }
-
- static class DisappearingPageBorder extends SimpleHelpComponent implements IPageBorder {
- private final int pageNum;
- private final int keptLines;
-
- public DisappearingPageBorder(int pageNum, int keptLines, String... lines) {
- super(lines);
- this.pageNum = pageNum;
- this.keptLines = keptLines;
- }
-
- @Override
- public void setPageCount(int pageCount) {
- if (pageCount == pageNum) {
- String[] lines = this.lines;
- this.lines = Arrays.copyOfRange(lines, Math.max(0, lines.length - keptLines), lines.length);
- }
- }
-
- }
-
-}
+package io.dico.dicore.command.chat.help;
+
+import java.util.Arrays;
+
+public class PageBorders {
+ private final IPageBorder header, footer;
+
+ public PageBorders(IPageBorder header, IPageBorder footer) {
+ this.header = header;
+ this.footer = footer;
+ }
+
+ public IPageBorder getHeader() {
+ return header;
+ }
+
+ public IPageBorder getFooter() {
+ return footer;
+ }
+
+ public static IPageBorder simpleBorder(String... lines) {
+ return new SimplePageBorder(lines);
+ }
+
+ public static IPageBorder disappearingBorder(int pageNum, String... lines) {
+ return disappearingBorder(pageNum, 0, lines);
+ }
+
+ public static IPageBorder disappearingBorder(int pageNum, int keptLines, String... lines) {
+ return new DisappearingPageBorder(pageNum, keptLines, lines);
+ }
+
+ static class SimplePageBorder extends SimpleHelpComponent implements IPageBorder {
+ private final String replacedSequence;
+
+ public SimplePageBorder(String replacedSequence, String... lines) {
+ super(lines);
+ this.replacedSequence = replacedSequence;
+ }
+
+ public SimplePageBorder(String... lines) {
+ super(lines);
+ this.replacedSequence = "%pageCount%";
+ }
+
+ @Override
+ public void setPageCount(int pageCount) {
+ String[] lines = this.lines;
+ for (int i = 0; i < lines.length; i++) {
+ lines[i] = lines[i].replace(replacedSequence, Integer.toString(pageCount));
+ }
+ }
+
+ }
+
+ static class DisappearingPageBorder extends SimpleHelpComponent implements IPageBorder {
+ private final int pageNum;
+ private final int keptLines;
+
+ public DisappearingPageBorder(int pageNum, int keptLines, String... lines) {
+ super(lines);
+ this.pageNum = pageNum;
+ this.keptLines = keptLines;
+ }
+
+ @Override
+ public void setPageCount(int pageCount) {
+ if (pageCount == pageNum) {
+ String[] lines = this.lines;
+ this.lines = Arrays.copyOfRange(lines, Math.max(0, lines.length - keptLines), lines.length);
+ }
+ }
+
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/SimpleHelpComponent.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/SimpleHelpComponent.java
index 22707fd..1b74d0f 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/SimpleHelpComponent.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/SimpleHelpComponent.java
@@ -1,27 +1,27 @@
-package io.dico.dicore.command.chat.help;
-
-public class SimpleHelpComponent implements IHelpComponent {
- String[] lines;
-
- public SimpleHelpComponent(String... lines) {
- this.lines = lines;
- }
-
- @Override
- public int lineCount() {
- return lines.length;
- }
-
- @Override
- public void appendTo(StringBuilder sb) {
- String[] lines = this.lines;
- int len = lines.length;
- if (0 < len) {
- sb.append(lines[0]);
- }
- for (int i = 1; i < len; i++) {
- sb.append('\n').append(lines[i]);
- }
- }
-
-}
+package io.dico.dicore.command.chat.help;
+
+public class SimpleHelpComponent implements IHelpComponent {
+ String[] lines;
+
+ public SimpleHelpComponent(String... lines) {
+ this.lines = lines;
+ }
+
+ @Override
+ public int lineCount() {
+ return lines.length;
+ }
+
+ @Override
+ public void appendTo(StringBuilder sb) {
+ String[] lines = this.lines;
+ int len = lines.length;
+ if (0 < len) {
+ sb.append(lines[0]);
+ }
+ for (int i = 1; i < len; i++) {
+ sb.append('\n').append(lines[i]);
+ }
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/DefaultPageBuilder.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/DefaultPageBuilder.java
index a584e7e..e06730f 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/DefaultPageBuilder.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/DefaultPageBuilder.java
@@ -1,115 +1,115 @@
-package io.dico.dicore.command.chat.help.defaults;
-
-import io.dico.dicore.command.ExecutionContext;
-import io.dico.dicore.command.ICommandAddress;
-import io.dico.dicore.command.chat.help.*;
-import org.bukkit.permissions.Permissible;
-
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.ListIterator;
-
-public class DefaultPageBuilder implements IPageBuilder {
-
- @Override
- public String getPage(List<IHelpTopic> helpTopics, IPageLayout pageLayout, ICommandAddress target,
- Permissible viewer, ExecutionContext context, int pageNum, int pageLen) {
- if (pageLen <= 0 || pageNum < 0) {
- throw new IllegalArgumentException();
- }
-
- List<IHelpComponent> components = new LinkedList<>();
- for (IHelpTopic topic : helpTopics) {
- components.addAll(topic.getComponents(target, viewer, context, true));
- }
-
- PageBorders pageBorders = null;
- int componentStartIdx = -1;
- int componentEndIdx = -1;
- int totalPageCount = 0;
- int curPageLines = 0;
-
- ListIterator<IHelpComponent> iterator = components.listIterator();
-
- while (iterator.hasNext()) {
- if (curPageLines == 0) {
-
- if (pageBorders != null) {
- iterator.add(pageBorders.getFooter());
- }
-
- if (pageNum == totalPageCount) {
- componentStartIdx = iterator.nextIndex();
- } else if (pageNum + 1 == totalPageCount) {
- componentEndIdx = iterator.nextIndex();
- }
-
- pageBorders = pageLayout.getPageBorders(target, viewer, context, totalPageCount + 1);
-
- if (pageBorders != null) {
- iterator.add(pageBorders.getHeader());
- iterator.previous();
-
- curPageLines += pageBorders.getFooter().lineCount();
- }
-
- totalPageCount++;
- }
-
- IHelpComponent component = iterator.next();
- int lineCount = component.lineCount();
- curPageLines += lineCount;
-
- if (curPageLines >= pageLen) {
- curPageLines = 0;
- }
- }
-
- if (componentStartIdx == -1) {
- // page does not exist
- return "";
- }
-
- if (componentEndIdx == -1) {
- componentEndIdx = components.size();
- }
-
- StringBuilder sb = new StringBuilder();
- iterator = components.listIterator(componentStartIdx);
- int count = componentEndIdx - componentStartIdx;
- boolean first = true;
-
- while (count-- > 0) {
- IHelpComponent component = iterator.next();
- if (component instanceof IPageBorder) {
- ((IPageBorder) component).setPageCount(totalPageCount);
- }
- if (first) {
- first = false;
- } else {
- sb.append('\n');
- }
- component.appendTo(sb);
-
- }
-
- return sb.toString();
- }
-
- public static String combine(List<IHelpComponent> components) {
- StringBuilder rv = new StringBuilder();
-
- Iterator<IHelpComponent> iterator = components.iterator();
- if (iterator.hasNext()) {
- iterator.next().appendTo(rv);
- }
- while (iterator.hasNext()) {
- rv.append('\n');
- iterator.next().appendTo(rv);
- }
-
- return rv.toString();
- }
-
-}
+package io.dico.dicore.command.chat.help.defaults;
+
+import io.dico.dicore.command.ExecutionContext;
+import io.dico.dicore.command.ICommandAddress;
+import io.dico.dicore.command.chat.help.*;
+import org.bukkit.permissions.Permissible;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+
+public class DefaultPageBuilder implements IPageBuilder {
+
+ @Override
+ public String getPage(List<IHelpTopic> helpTopics, IPageLayout pageLayout, ICommandAddress target,
+ Permissible viewer, ExecutionContext context, int pageNum, int pageLen) {
+ if (pageLen <= 0 || pageNum < 0) {
+ throw new IllegalArgumentException();
+ }
+
+ List<IHelpComponent> components = new LinkedList<>();
+ for (IHelpTopic topic : helpTopics) {
+ components.addAll(topic.getComponents(target, viewer, context, true));
+ }
+
+ PageBorders pageBorders = null;
+ int componentStartIdx = -1;
+ int componentEndIdx = -1;
+ int totalPageCount = 0;
+ int curPageLines = 0;
+
+ ListIterator<IHelpComponent> iterator = components.listIterator();
+
+ while (iterator.hasNext()) {
+ if (curPageLines == 0) {
+
+ if (pageBorders != null) {
+ iterator.add(pageBorders.getFooter());
+ }
+
+ if (pageNum == totalPageCount) {
+ componentStartIdx = iterator.nextIndex();
+ } else if (pageNum + 1 == totalPageCount) {
+ componentEndIdx = iterator.nextIndex();
+ }
+
+ pageBorders = pageLayout.getPageBorders(target, viewer, context, totalPageCount + 1);
+
+ if (pageBorders != null) {
+ iterator.add(pageBorders.getHeader());
+ iterator.previous();
+
+ curPageLines += pageBorders.getFooter().lineCount();
+ }
+
+ totalPageCount++;
+ }
+
+ IHelpComponent component = iterator.next();
+ int lineCount = component.lineCount();
+ curPageLines += lineCount;
+
+ if (curPageLines >= pageLen) {
+ curPageLines = 0;
+ }
+ }
+
+ if (componentStartIdx == -1) {
+ // page does not exist
+ return "";
+ }
+
+ if (componentEndIdx == -1) {
+ componentEndIdx = components.size();
+ }
+
+ StringBuilder sb = new StringBuilder();
+ iterator = components.listIterator(componentStartIdx);
+ int count = componentEndIdx - componentStartIdx;
+ boolean first = true;
+
+ while (count-- > 0) {
+ IHelpComponent component = iterator.next();
+ if (component instanceof IPageBorder) {
+ ((IPageBorder) component).setPageCount(totalPageCount);
+ }
+ if (first) {
+ first = false;
+ } else {
+ sb.append('\n');
+ }
+ component.appendTo(sb);
+
+ }
+
+ return sb.toString();
+ }
+
+ public static String combine(List<IHelpComponent> components) {
+ StringBuilder rv = new StringBuilder();
+
+ Iterator<IHelpComponent> iterator = components.iterator();
+ if (iterator.hasNext()) {
+ iterator.next().appendTo(rv);
+ }
+ while (iterator.hasNext()) {
+ rv.append('\n');
+ iterator.next().appendTo(rv);
+ }
+
+ return rv.toString();
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/DefaultPageLayout.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/DefaultPageLayout.java
index 8d3d004..d950b09 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/DefaultPageLayout.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/DefaultPageLayout.java
@@ -1,40 +1,40 @@
-package io.dico.dicore.command.chat.help.defaults;
-
-import io.dico.dicore.command.EMessageType;
-import io.dico.dicore.command.ExecutionContext;
-import io.dico.dicore.command.ICommandAddress;
-import io.dico.dicore.command.ModifiableCommandAddress;
-import io.dico.dicore.Formatting;
-import io.dico.dicore.command.chat.IChatHandler;
-import io.dico.dicore.command.chat.help.IPageBorder;
-import io.dico.dicore.command.chat.help.IPageLayout;
-import io.dico.dicore.command.chat.help.PageBorders;
-import org.bukkit.permissions.Permissible;
-
-public class DefaultPageLayout implements IPageLayout {
-
- @Override
- public PageBorders getPageBorders(ICommandAddress target, Permissible viewer, ExecutionContext context, int pageNum) {
- IChatHandler c = context.getAddress().getChatHandler();
- String prefix = c.getMessagePrefixForType(EMessageType.INFORMATIVE);
- Formatting informative = c.getChatFormatForType(EMessageType.INFORMATIVE);
- Formatting number = c.getChatFormatForType(EMessageType.NEUTRAL);
-
- String nextPageCommand;
- ICommandAddress executor = context.getAddress();
- if (((ModifiableCommandAddress) executor).hasHelpCommand()) {
- nextPageCommand = ((ModifiableCommandAddress) executor).getHelpCommand().getAddress() + ' ' + (pageNum + 1);
- } else {
- nextPageCommand = executor.getAddress() + ' ' + (pageNum + 1);
- }
-
- String header = prefix + informative + "Help page " + number + pageNum + informative +
- '/' + number + "%pageCount%" + informative + " for /" + target.getAddress();
- String footer = informative + "Type /" + nextPageCommand + " for the next page";
-
- IPageBorder headerBorder = PageBorders.simpleBorder("", header);
- IPageBorder footerBorder = PageBorders.disappearingBorder(pageNum, footer);
- return new PageBorders(headerBorder, footerBorder);
- }
-
-}
+package io.dico.dicore.command.chat.help.defaults;
+
+import io.dico.dicore.command.EMessageType;
+import io.dico.dicore.command.ExecutionContext;
+import io.dico.dicore.command.ICommandAddress;
+import io.dico.dicore.command.ModifiableCommandAddress;
+import io.dico.dicore.Formatting;
+import io.dico.dicore.command.chat.IChatHandler;
+import io.dico.dicore.command.chat.help.IPageBorder;
+import io.dico.dicore.command.chat.help.IPageLayout;
+import io.dico.dicore.command.chat.help.PageBorders;
+import org.bukkit.permissions.Permissible;
+
+public class DefaultPageLayout implements IPageLayout {
+
+ @Override
+ public PageBorders getPageBorders(ICommandAddress target, Permissible viewer, ExecutionContext context, int pageNum) {
+ IChatHandler c = context.getAddress().getChatHandler();
+ String prefix = c.getMessagePrefixForType(EMessageType.INFORMATIVE);
+ Formatting informative = c.getChatFormatForType(EMessageType.INFORMATIVE);
+ Formatting number = c.getChatFormatForType(EMessageType.NEUTRAL);
+
+ String nextPageCommand;
+ ICommandAddress executor = context.getAddress();
+ if (((ModifiableCommandAddress) executor).hasHelpCommand()) {
+ nextPageCommand = ((ModifiableCommandAddress) executor).getHelpCommand().getAddress() + ' ' + (pageNum + 1);
+ } else {
+ nextPageCommand = executor.getAddress() + ' ' + (pageNum + 1);
+ }
+
+ String header = prefix + informative + "Help page " + number + pageNum + informative +
+ '/' + number + "%pageCount%" + informative + " for /" + target.getAddress();
+ String footer = informative + "Type /" + nextPageCommand + " for the next page";
+
+ IPageBorder headerBorder = PageBorders.simpleBorder("", header);
+ IPageBorder footerBorder = PageBorders.disappearingBorder(pageNum, footer);
+ return new PageBorders(headerBorder, footerBorder);
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/DescriptionHelpTopic.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/DescriptionHelpTopic.java
index ae88ea2..0899db4 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/DescriptionHelpTopic.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/DescriptionHelpTopic.java
@@ -1,45 +1,45 @@
-package io.dico.dicore.command.chat.help.defaults;
-
-import io.dico.dicore.command.Command;
-import io.dico.dicore.command.EMessageType;
-import io.dico.dicore.command.ExecutionContext;
-import io.dico.dicore.command.ICommandAddress;
-import io.dico.dicore.Formatting;
-import io.dico.dicore.command.chat.help.IHelpComponent;
-import io.dico.dicore.command.chat.help.IHelpTopic;
-import io.dico.dicore.command.chat.help.SimpleHelpComponent;
-import org.bukkit.permissions.Permissible;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class DescriptionHelpTopic implements IHelpTopic {
-
- @Override
- public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) {
- List<IHelpComponent> out = new ArrayList<>();
- Formatting format = context.getFormat(EMessageType.DESCRIPTION);
-
- if (!target.hasCommand()) {
- return out;
- }
- Command command = target.getCommand();
- String[] description = command.getDescription();
- if (description.length == 0) {
- String shortDescription = command.getShortDescription();
- if (shortDescription == null) {
- return out;
- }
-
- description = new String[]{shortDescription};
- }
-
- for (int i = 0; i < description.length; i++) {
- description[i] = format + description[i];
- }
-
- out.add(new SimpleHelpComponent(description));
- return out;
- }
-
-}
+package io.dico.dicore.command.chat.help.defaults;
+
+import io.dico.dicore.command.Command;
+import io.dico.dicore.command.EMessageType;
+import io.dico.dicore.command.ExecutionContext;
+import io.dico.dicore.command.ICommandAddress;
+import io.dico.dicore.Formatting;
+import io.dico.dicore.command.chat.help.IHelpComponent;
+import io.dico.dicore.command.chat.help.IHelpTopic;
+import io.dico.dicore.command.chat.help.SimpleHelpComponent;
+import org.bukkit.permissions.Permissible;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DescriptionHelpTopic implements IHelpTopic {
+
+ @Override
+ public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) {
+ List<IHelpComponent> out = new ArrayList<>();
+ Formatting format = context.getFormat(EMessageType.DESCRIPTION);
+
+ if (!target.hasCommand()) {
+ return out;
+ }
+ Command command = target.getCommand();
+ String[] description = command.getDescription();
+ if (description.length == 0) {
+ String shortDescription = command.getShortDescription();
+ if (shortDescription == null) {
+ return out;
+ }
+
+ description = new String[]{shortDescription};
+ }
+
+ for (int i = 0; i < description.length; i++) {
+ description[i] = format + description[i];
+ }
+
+ out.add(new SimpleHelpComponent(description));
+ return out;
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/SubcommandsHelpTopic.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/SubcommandsHelpTopic.java
index 0e680ae..b302769 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/SubcommandsHelpTopic.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/SubcommandsHelpTopic.java
@@ -1,59 +1,59 @@
-package io.dico.dicore.command.chat.help.defaults;
-
-import io.dico.dicore.command.EMessageType;
-import io.dico.dicore.command.ExecutionContext;
-import io.dico.dicore.command.ICommandAddress;
-import io.dico.dicore.Formatting;
-import io.dico.dicore.command.chat.help.IHelpComponent;
-import io.dico.dicore.command.chat.help.IHelpTopic;
-import io.dico.dicore.command.chat.help.SimpleHelpComponent;
-import org.bukkit.command.CommandSender;
-import org.bukkit.permissions.Permissible;
-
-import java.util.*;
-
-public class SubcommandsHelpTopic implements IHelpTopic {
-
- @Override
- public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) {
- Collection<String> mainKeys = target.getChildrenMainKeys();
- if (mainKeys.isEmpty()) {
- return Collections.emptyList();
- }
-
- List<IHelpComponent> result = new ArrayList<>();
-
- mainKeys = new ArrayList<>(target.getChildrenMainKeys());
- ((ArrayList<String>) mainKeys).sort(null);
-
- CommandSender sender = viewer instanceof CommandSender ? (CommandSender) viewer : context.getSender();
- for (String key : mainKeys) {
- ICommandAddress child = target.getChild(key);
- if ((child.hasChildren() || child.hasUserDeclaredCommand()) && child.getCommand().isVisibleTo(sender)) {
- result.add(getComponent(child, viewer, context));
- }
- }
-
- return result;
- }
-
- public IHelpComponent getComponent(ICommandAddress child, Permissible viewer, ExecutionContext context) {
- Formatting subcommand = colorOf(context, EMessageType.SUBCOMMAND);
- Formatting highlight = colorOf(context, EMessageType.HIGHLIGHT);
-
- String address = subcommand + "/" + child.getParent().getAddress() + ' ' + highlight + child.getMainKey();
-
- String description = child.hasCommand() ? child.getCommand().getShortDescription() : null;
- if (description != null) {
- Formatting descriptionFormat = colorOf(context, EMessageType.DESCRIPTION);
- return new SimpleHelpComponent(address, descriptionFormat + description);
- }
-
- return new SimpleHelpComponent(address);
- }
-
- private static Formatting colorOf(ExecutionContext context, EMessageType type) {
- return context.getAddress().getChatHandler().getChatFormatForType(type);
- }
-
-}
+package io.dico.dicore.command.chat.help.defaults;
+
+import io.dico.dicore.command.EMessageType;
+import io.dico.dicore.command.ExecutionContext;
+import io.dico.dicore.command.ICommandAddress;
+import io.dico.dicore.Formatting;
+import io.dico.dicore.command.chat.help.IHelpComponent;
+import io.dico.dicore.command.chat.help.IHelpTopic;
+import io.dico.dicore.command.chat.help.SimpleHelpComponent;
+import org.bukkit.command.CommandSender;
+import org.bukkit.permissions.Permissible;
+
+import java.util.*;
+
+public class SubcommandsHelpTopic implements IHelpTopic {
+
+ @Override
+ public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) {
+ Collection<String> mainKeys = target.getChildrenMainKeys();
+ if (mainKeys.isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ List<IHelpComponent> result = new ArrayList<>();
+
+ mainKeys = new ArrayList<>(target.getChildrenMainKeys());
+ ((ArrayList<String>) mainKeys).sort(null);
+
+ CommandSender sender = viewer instanceof CommandSender ? (CommandSender) viewer : context.getSender();
+ for (String key : mainKeys) {
+ ICommandAddress child = target.getChild(key);
+ if ((child.hasChildren() || child.hasUserDeclaredCommand()) && child.getCommand().isVisibleTo(sender)) {
+ result.add(getComponent(child, viewer, context));
+ }
+ }
+
+ return result;
+ }
+
+ public IHelpComponent getComponent(ICommandAddress child, Permissible viewer, ExecutionContext context) {
+ Formatting subcommand = colorOf(context, EMessageType.SUBCOMMAND);
+ Formatting highlight = colorOf(context, EMessageType.HIGHLIGHT);
+
+ String address = subcommand + "/" + child.getParent().getAddress() + ' ' + highlight + child.getMainKey();
+
+ String description = child.hasCommand() ? child.getCommand().getShortDescription() : null;
+ if (description != null) {
+ Formatting descriptionFormat = colorOf(context, EMessageType.DESCRIPTION);
+ return new SimpleHelpComponent(address, descriptionFormat + description);
+ }
+
+ return new SimpleHelpComponent(address);
+ }
+
+ private static Formatting colorOf(ExecutionContext context, EMessageType type) {
+ return context.getAddress().getChatHandler().getChatFormatForType(type);
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/SyntaxHelpTopic.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/SyntaxHelpTopic.java
index d0e3ebe..f0f820f 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/SyntaxHelpTopic.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/SyntaxHelpTopic.java
@@ -1,92 +1,92 @@
-package io.dico.dicore.command.chat.help.defaults;
-
-import io.dico.dicore.command.Command;
-import io.dico.dicore.command.EMessageType;
-import io.dico.dicore.command.ExecutionContext;
-import io.dico.dicore.command.ICommandAddress;
-import io.dico.dicore.Formatting;
-import io.dico.dicore.command.chat.help.IHelpComponent;
-import io.dico.dicore.command.chat.help.IHelpTopic;
-import io.dico.dicore.command.chat.help.SimpleHelpComponent;
-import io.dico.dicore.command.parameter.Parameter;
-import io.dico.dicore.command.parameter.ParameterList;
-import org.bukkit.permissions.Permissible;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
-public class SyntaxHelpTopic implements IHelpTopic {
-
- @Override
- public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) {
- if (!target.hasCommand()) {
- return Collections.emptyList();
- }
-
- if (target.hasChildren()) {
- if (!isForPage) {
- // HelpPages will send help instead of syntax, which might in turn include syntax as well.
- return Collections.emptyList();
- }
-
- if (!target.hasUserDeclaredCommand() && !target.getCommand().getParameterList().hasAnyParameters()) {
- // no point adding syntax at all
- return Collections.emptyList();
- }
- }
-
- StringBuilder line = new StringBuilder();
- if (isForPage)
- line.append(context.getFormat(EMessageType.SYNTAX))
- .append("Syntax: ");
-
- line.append('/')
- .append(context.getFormat(EMessageType.INSTRUCTION))
- .append(target.getAddress())
- .append(' ');
-
- addShortSyntax(line, target, context);
-
- return Collections.singletonList(new SimpleHelpComponent(line.toString()));
- }
-
- private static void addShortSyntax(StringBuilder builder, ICommandAddress address, ExecutionContext ctx) {
- if (address.hasCommand()) {
- Formatting syntaxColor = ctx.getFormat(EMessageType.SYNTAX);
- Formatting highlight = ctx.getFormat(EMessageType.HIGHLIGHT);
- builder.append(syntaxColor);
-
- Command command = address.getCommand();
- ParameterList list = command.getParameterList();
- Parameter<?, ?> repeated = list.getRepeatedParameter();
-
- int requiredCount = list.getRequiredCount();
- List<Parameter<?, ?>> indexedParameters = list.getIndexedParameters();
- for (int i = 0, n = indexedParameters.size(); i < n; i++) {
- builder.append(i < requiredCount ? " <" : " [");
- Parameter<?, ?> param = indexedParameters.get(i);
- builder.append(param.getName());
- if (param == repeated) {
- builder.append(highlight).append("...").append(syntaxColor);
- }
- builder.append(i < requiredCount ? '>' : ']');
- }
-
- Map<String, Parameter<?, ?>> parametersByName = list.getParametersByName();
- for (Parameter<?, ?> param : parametersByName.values()) {
- if (param.isFlag()) {
- builder.append(" [").append(param.getName());
- if (param.expectsInput()) {
- builder.append(" <").append(param.getName()).append(">");
- }
- builder.append(']');
- }
- }
-
- } else {
- builder.append(' ');
- }
- }
-
-}
+package io.dico.dicore.command.chat.help.defaults;
+
+import io.dico.dicore.command.Command;
+import io.dico.dicore.command.EMessageType;
+import io.dico.dicore.command.ExecutionContext;
+import io.dico.dicore.command.ICommandAddress;
+import io.dico.dicore.Formatting;
+import io.dico.dicore.command.chat.help.IHelpComponent;
+import io.dico.dicore.command.chat.help.IHelpTopic;
+import io.dico.dicore.command.chat.help.SimpleHelpComponent;
+import io.dico.dicore.command.parameter.Parameter;
+import io.dico.dicore.command.parameter.ParameterList;
+import org.bukkit.permissions.Permissible;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+public class SyntaxHelpTopic implements IHelpTopic {
+
+ @Override
+ public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) {
+ if (!target.hasCommand()) {
+ return Collections.emptyList();
+ }
+
+ if (target.hasChildren()) {
+ if (!isForPage) {
+ // HelpPages will send help instead of syntax, which might in turn include syntax as well.
+ return Collections.emptyList();
+ }
+
+ if (!target.hasUserDeclaredCommand() && !target.getCommand().getParameterList().hasAnyParameters()) {
+ // no point adding syntax at all
+ return Collections.emptyList();
+ }
+ }
+
+ StringBuilder line = new StringBuilder();
+ if (isForPage)
+ line.append(context.getFormat(EMessageType.SYNTAX))
+ .append("Syntax: ");
+
+ line.append('/')
+ .append(context.getFormat(EMessageType.INSTRUCTION))
+ .append(target.getAddress())
+ .append(' ');
+
+ addShortSyntax(line, target, context);
+
+ return Collections.singletonList(new SimpleHelpComponent(line.toString()));
+ }
+
+ private static void addShortSyntax(StringBuilder builder, ICommandAddress address, ExecutionContext ctx) {
+ if (address.hasCommand()) {
+ Formatting syntaxColor = ctx.getFormat(EMessageType.SYNTAX);
+ Formatting highlight = ctx.getFormat(EMessageType.HIGHLIGHT);
+ builder.append(syntaxColor);
+
+ Command command = address.getCommand();
+ ParameterList list = command.getParameterList();
+ Parameter<?, ?> repeated = list.getRepeatedParameter();
+
+ int requiredCount = list.getRequiredCount();
+ List<Parameter<?, ?>> indexedParameters = list.getIndexedParameters();
+ for (int i = 0, n = indexedParameters.size(); i < n; i++) {
+ builder.append(i < requiredCount ? " <" : " [");
+ Parameter<?, ?> param = indexedParameters.get(i);
+ builder.append(param.getName());
+ if (param == repeated) {
+ builder.append(highlight).append("...").append(syntaxColor);
+ }
+ builder.append(i < requiredCount ? '>' : ']');
+ }
+
+ Map<String, Parameter<?, ?>> parametersByName = list.getParametersByName();
+ for (Parameter<?, ?> param : parametersByName.values()) {
+ if (param.isFlag()) {
+ builder.append(" [").append(param.getName());
+ if (param.expectsInput()) {
+ builder.append(" <").append(param.getName()).append(">");
+ }
+ builder.append(']');
+ }
+ }
+
+ } else {
+ builder.append(' ');
+ }
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/EInsertionStage.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/EInsertionStage.java
index 4f0026d..eb3605d 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/EInsertionStage.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/EInsertionStage.java
@@ -1,29 +1,29 @@
-package io.dico.dicore.command.chat.help.insertion;
-
-import io.dico.dicore.command.ExecutionContext;
-import io.dico.dicore.command.ICommandAddress;
-import io.dico.dicore.command.chat.help.IHelpComponent;
-import org.bukkit.permissions.Permissible;
-
-import java.util.List;
-
-public enum EInsertionStage implements IInsertionFunction {
- START {
- @Override
- public int insertionIndex(List<IHelpComponent> current, ICommandAddress target, Permissible viewer, ExecutionContext context) {
- return 0;
- }
- },
- CENTER {
- @Override
- public int insertionIndex(List<IHelpComponent> current, ICommandAddress target, Permissible viewer, ExecutionContext context) {
- return current.size() / 2;
- }
- },
- END {
- @Override
- public int insertionIndex(List<IHelpComponent> current, ICommandAddress target, Permissible viewer, ExecutionContext context) {
- return current.size();
- }
- }
-}
+package io.dico.dicore.command.chat.help.insertion;
+
+import io.dico.dicore.command.ExecutionContext;
+import io.dico.dicore.command.ICommandAddress;
+import io.dico.dicore.command.chat.help.IHelpComponent;
+import org.bukkit.permissions.Permissible;
+
+import java.util.List;
+
+public enum EInsertionStage implements IInsertionFunction {
+ START {
+ @Override
+ public int insertionIndex(List<IHelpComponent> current, ICommandAddress target, Permissible viewer, ExecutionContext context) {
+ return 0;
+ }
+ },
+ CENTER {
+ @Override
+ public int insertionIndex(List<IHelpComponent> current, ICommandAddress target, Permissible viewer, ExecutionContext context) {
+ return current.size() / 2;
+ }
+ },
+ END {
+ @Override
+ public int insertionIndex(List<IHelpComponent> current, ICommandAddress target, Permissible viewer, ExecutionContext context) {
+ return current.size();
+ }
+ }
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/HelpComponentInserter.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/HelpComponentInserter.java
index f153165..75b52d0 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/HelpComponentInserter.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/HelpComponentInserter.java
@@ -1,43 +1,43 @@
-package io.dico.dicore.command.chat.help.insertion;
-
-import io.dico.dicore.command.ExecutionContext;
-import io.dico.dicore.command.ICommandAddress;
-import io.dico.dicore.command.chat.help.HelpTopicModifier;
-import io.dico.dicore.command.chat.help.IHelpComponent;
-import io.dico.dicore.command.chat.help.IHelpTopic;
-import org.bukkit.permissions.Permissible;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class HelpComponentInserter extends HelpTopicModifier {
- private List<IInsertion> insertions = new ArrayList<>();
-
- public HelpComponentInserter(IHelpTopic delegate) {
- super(delegate);
- }
-
- @Override
- protected List<IHelpComponent> modify(List<IHelpComponent> components, ICommandAddress target, Permissible viewer, ExecutionContext context) {
- // int componentCount = components.size();
-
- for (int i = insertions.size() - 1; i >= 0; i--) {
- IInsertion insertion = insertions.get(i);
- int idx = insertion.insertionIndex(components, target, viewer, context);
- List<IHelpComponent> inserted = insertion.getComponents(target, viewer, context, true);
- components.addAll(idx, inserted);
- }
-
- return components;
- }
-
- public HelpComponentInserter insert(IInsertionFunction insertionFunction, IHelpTopic helpTopic) {
- return insert(Insertions.combine(helpTopic, insertionFunction));
- }
-
- public HelpComponentInserter insert(IInsertion insertion) {
- insertions.add(insertion);
- return this;
- }
-
-}
+package io.dico.dicore.command.chat.help.insertion;
+
+import io.dico.dicore.command.ExecutionContext;
+import io.dico.dicore.command.ICommandAddress;
+import io.dico.dicore.command.chat.help.HelpTopicModifier;
+import io.dico.dicore.command.chat.help.IHelpComponent;
+import io.dico.dicore.command.chat.help.IHelpTopic;
+import org.bukkit.permissions.Permissible;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class HelpComponentInserter extends HelpTopicModifier {
+ private List<IInsertion> insertions = new ArrayList<>();
+
+ public HelpComponentInserter(IHelpTopic delegate) {
+ super(delegate);
+ }
+
+ @Override
+ protected List<IHelpComponent> modify(List<IHelpComponent> components, ICommandAddress target, Permissible viewer, ExecutionContext context) {
+ // int componentCount = components.size();
+
+ for (int i = insertions.size() - 1; i >= 0; i--) {
+ IInsertion insertion = insertions.get(i);
+ int idx = insertion.insertionIndex(components, target, viewer, context);
+ List<IHelpComponent> inserted = insertion.getComponents(target, viewer, context, true);
+ components.addAll(idx, inserted);
+ }
+
+ return components;
+ }
+
+ public HelpComponentInserter insert(IInsertionFunction insertionFunction, IHelpTopic helpTopic) {
+ return insert(Insertions.combine(helpTopic, insertionFunction));
+ }
+
+ public HelpComponentInserter insert(IInsertion insertion) {
+ insertions.add(insertion);
+ return this;
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/IInsertion.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/IInsertion.java
index 757cb91..92309d3 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/IInsertion.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/IInsertion.java
@@ -1,7 +1,7 @@
-package io.dico.dicore.command.chat.help.insertion;
-
-import io.dico.dicore.command.chat.help.IHelpTopic;
-
-interface IInsertion extends IHelpTopic, IInsertionFunction {
-
-}
+package io.dico.dicore.command.chat.help.insertion;
+
+import io.dico.dicore.command.chat.help.IHelpTopic;
+
+interface IInsertion extends IHelpTopic, IInsertionFunction {
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/IInsertionFunction.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/IInsertionFunction.java
index e99c246..356d002 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/IInsertionFunction.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/IInsertionFunction.java
@@ -1,14 +1,14 @@
-package io.dico.dicore.command.chat.help.insertion;
-
-import io.dico.dicore.command.ExecutionContext;
-import io.dico.dicore.command.ICommandAddress;
-import io.dico.dicore.command.chat.help.IHelpComponent;
-import org.bukkit.permissions.Permissible;
-
-import java.util.List;
-
-public interface IInsertionFunction {
-
- int insertionIndex(List<IHelpComponent> current, ICommandAddress target, Permissible viewer, ExecutionContext context);
-
-}
+package io.dico.dicore.command.chat.help.insertion;
+
+import io.dico.dicore.command.ExecutionContext;
+import io.dico.dicore.command.ICommandAddress;
+import io.dico.dicore.command.chat.help.IHelpComponent;
+import org.bukkit.permissions.Permissible;
+
+import java.util.List;
+
+public interface IInsertionFunction {
+
+ int insertionIndex(List<IHelpComponent> current, ICommandAddress target, Permissible viewer, ExecutionContext context);
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/Insertions.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/Insertions.java
index 39b2784..220f05b 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/Insertions.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/Insertions.java
@@ -1,31 +1,31 @@
-package io.dico.dicore.command.chat.help.insertion;
-
-import io.dico.dicore.command.ExecutionContext;
-import io.dico.dicore.command.ICommandAddress;
-import io.dico.dicore.command.chat.help.IHelpComponent;
-import io.dico.dicore.command.chat.help.IHelpTopic;
-import org.bukkit.permissions.Permissible;
-
-import java.util.List;
-
-public class Insertions {
-
- private Insertions() {
-
- }
-
- public static IInsertion combine(IHelpTopic topic, IInsertionFunction function) {
- return new IInsertion() {
- @Override
- public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) {
- return topic.getComponents(target, viewer, context, true);
- }
-
- @Override
- public int insertionIndex(List<IHelpComponent> current, ICommandAddress target, Permissible viewer, ExecutionContext context) {
- return function.insertionIndex(current, target, viewer, context);
- }
- };
- }
-
-}
+package io.dico.dicore.command.chat.help.insertion;
+
+import io.dico.dicore.command.ExecutionContext;
+import io.dico.dicore.command.ICommandAddress;
+import io.dico.dicore.command.chat.help.IHelpComponent;
+import io.dico.dicore.command.chat.help.IHelpTopic;
+import org.bukkit.permissions.Permissible;
+
+import java.util.List;
+
+public class Insertions {
+
+ private Insertions() {
+
+ }
+
+ public static IInsertion combine(IHelpTopic topic, IInsertionFunction function) {
+ return new IInsertion() {
+ @Override
+ public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) {
+ return topic.getComponents(target, viewer, context, true);
+ }
+
+ @Override
+ public int insertionIndex(List<IHelpComponent> current, ICommandAddress target, Permissible viewer, ExecutionContext context) {
+ return function.insertionIndex(current, target, viewer, context);
+ }
+ };
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ArgumentBuffer.java b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ArgumentBuffer.java
index aa69730..5646814 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ArgumentBuffer.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ArgumentBuffer.java
@@ -1,295 +1,295 @@
-package io.dico.dicore.command.parameter;
-
-import io.dico.dicore.command.CommandException;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.*;
-
-/**
- * Buffer for the arguments.
- * Easy to traverse for the parser.
- */
-public class ArgumentBuffer extends AbstractList<String> implements Iterator<String>, RandomAccess {
- private String[] array;
- private int cursor = 0; // index of the next return value
- private transient ArgumentBuffer unaffectingCopy = null; // see #getUnaffectingCopy()
-
- public ArgumentBuffer(String label, String[] args) {
- this(combine(label, args));
- }
-
- private static String[] combine(String label, String[] args) {
- String[] result;
- //if (args.length > 0 && "".equals(args[args.length - 1])) {
- // // drop the last element of args if it is empty
- // result = args;
- //} else {
- result = new String[args.length + 1];
- //}
- System.arraycopy(args, 0, result, 1, result.length - 1);
- result[0] = Objects.requireNonNull(label);
- return result;
- }
-
- /**
- * Constructs a new ArgumentBuffer using the given array, without copying it first.
- * None of the array its elements should be empty.
- *
- * @param array the array
- * @throws NullPointerException if the array or any of its elements are null
- */
- public ArgumentBuffer(String[] array) {
- for (String elem : array) {
- if (elem == null) throw new NullPointerException("ArgumentBuffer array element");
- }
- this.array = array;
-
- }
-
- public int getCursor() {
- return cursor;
- }
-
- public @NotNull ArgumentBuffer setCursor(int cursor) {
- if (cursor <= 0) {
- cursor = 0;
- } else if (size() <= cursor) {
- cursor = size();
- }
- this.cursor = cursor;
- return this;
- }
-
- @Override
- public int size() {
- return array.length;
- }
-
- @Override
- public @NotNull String get(int index) {
- return array[index];
- }
-
- public int nextIndex() {
- return cursor;
- }
-
- public int previousIndex() {
- return cursor - 1;
- }
-
- public int remainingElements() {
- return size() - nextIndex() - 1;
- }
-
- @Override
- public boolean hasNext() {
- return nextIndex() < size();
- }
-
- public boolean hasPrevious() {
- return 0 <= previousIndex();
- }
-
- /**
- * Unlike conventional ListIterator implementations, this returns null if there is no next element
- *
- * @return the next value, or null
- */
- @Override
- public @Nullable String next() {
- return hasNext() ? get(cursor++) : null;
- }
-
- public @NotNull String requireNext(String parameterName) throws CommandException {
- String next = next();
- if (next == null) {
- throw CommandException.missingArgument(parameterName);
- }
- return next;
- }
-
- // useful for completion code
- public @NotNull String nextOrEmpty() {
- return hasNext() ? get(cursor++) : "";
- }
-
- /**
- * Unlike conventional ListIterator implementations, this returns null if there is no previous element
- *
- * @return the previous value, or null
- */
- public @Nullable String previous() {
- return hasPrevious() ? get(--cursor) : null;
- }
-
- public @Nullable String peekNext() {
- return hasNext() ? get(cursor) : null;
- }
-
- public @Nullable String peekPrevious() {
- return hasPrevious() ? get(cursor - 1) : null;
- }
-
- public @NotNull ArgumentBuffer advance() {
- return advance(1);
- }
-
- public @NotNull ArgumentBuffer advance(int amount) {
- cursor = Math.min(Math.max(0, cursor + amount), size());
- return this;
- }
-
- public @NotNull ArgumentBuffer rewind() {
- return rewind(1);
- }
-
- public @NotNull ArgumentBuffer rewind(int amount) {
- return advance(-amount);
- }
-
- @NotNull String[] getArray() {
- return array;
- }
-
- public @NotNull String[] getArrayFromCursor() {
- return getArrayFromIndex(cursor);
- }
-
- public @NotNull String[] getArrayFromIndex(int index) {
- return Arrays.copyOfRange(array, index, array.length);
- }
-
- public @NotNull String getRawInput() {
- return String.join(" ", array);
- }
-
- public @NotNull String[] toArray() {
- return array.clone();
- }
-
- @Override
- public @NotNull Iterator<String> iterator() {
- return this;
- }
-
- @Override
- public @NotNull ListIterator<String> listIterator() {
- return new ListIterator<String>() {
- @Override
- public boolean hasNext() {
- return ArgumentBuffer.this.hasNext();
- }
-
- @Override
- public String next() {
- if (!hasNext()) {
- throw new NoSuchElementException();
- }
- return ArgumentBuffer.this.next();
- }
-
- @Override
- public boolean hasPrevious() {
- return ArgumentBuffer.this.hasPrevious();
- }
-
- @Override
- public String previous() {
- if (!hasPrevious()) {
- throw new NoSuchElementException();
- }
- return ArgumentBuffer.this.previous();
- }
-
- @Override
- public int nextIndex() {
- return ArgumentBuffer.this.nextIndex();
- }
-
- @Override
- public int previousIndex() {
- return ArgumentBuffer.this.previousIndex();
- }
-
- @Override
- public void remove() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void set(String s) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void add(String s) {
- throw new UnsupportedOperationException();
- }
- };
- }
-
- public void dropTrailingEmptyElements() {
- int removeCount = 0;
- String[] array = this.array;
- for (int i = array.length - 1; i >= 0; i--) {
- if ("".equals(array[i])) {
- removeCount++;
- }
- }
-
- if (removeCount > 0) {
- String[] newArray = new String[array.length - removeCount];
- System.arraycopy(array, 0, newArray, 0, newArray.length);
- this.array = newArray;
-
- if (cursor > newArray.length) {
- cursor = newArray.length;
- }
- }
- }
-
- /**
- * Preprocess this argument buffer with the given preprocessor
- *
- * @param preProcessor preprocessor
- * @return a new ArgumentBuffer with processed contents. Might be this buffer if nothing changed.
- */
- public @NotNull ArgumentBuffer preprocessArguments(IArgumentPreProcessor preProcessor) {
- return preProcessor.process(this, -1);
- }
-
- /**
- * Allows a piece of code to traverse this buffer without modifying its cursor.
- * After this method has been called for the first time on this instance, if this method
- * or the {@link #clone()} method are called, the operation carried out on the prior result has finished.
- * As such, the same instance might be returned again.
- *
- * @return A view of this buffer that doesn't affect this buffer's cursor.
- */
- public ArgumentBuffer getUnaffectingCopy() {
- // the copy doesn't alter the cursor of this ArgumentBuffer when moved, but traverses the same array reference.
- // there is only ever one copy of an ArgumentBuffer, the cursor of which is updated on every call to this method.
-
- ArgumentBuffer unaffectingCopy = this.unaffectingCopy;
- if (unaffectingCopy == null) {
- this.unaffectingCopy = unaffectingCopy = new ArgumentBuffer(array);
- }
- unaffectingCopy.cursor = this.cursor;
- return unaffectingCopy;
- }
-
- @SuppressWarnings("MethodDoesntCallSuperMethod")
- public @NotNull ArgumentBuffer clone() {
- ArgumentBuffer result = getUnaffectingCopy();
- this.unaffectingCopy = null;
- return result;
- }
-
- @Override
- public String toString() {
- return String.format("ArgumentBuffer(size = %d, cursor = %d)", size(), getCursor());
- }
-
-}
+package io.dico.dicore.command.parameter;
+
+import io.dico.dicore.command.CommandException;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.*;
+
+/**
+ * Buffer for the arguments.
+ * Easy to traverse for the parser.
+ */
+public class ArgumentBuffer extends AbstractList<String> implements Iterator<String>, RandomAccess {
+ private String[] array;
+ private int cursor = 0; // index of the next return value
+ private transient ArgumentBuffer unaffectingCopy = null; // see #getUnaffectingCopy()
+
+ public ArgumentBuffer(String label, String[] args) {
+ this(combine(label, args));
+ }
+
+ private static String[] combine(String label, String[] args) {
+ String[] result;
+ //if (args.length > 0 && "".equals(args[args.length - 1])) {
+ // // drop the last element of args if it is empty
+ // result = args;
+ //} else {
+ result = new String[args.length + 1];
+ //}
+ System.arraycopy(args, 0, result, 1, result.length - 1);
+ result[0] = Objects.requireNonNull(label);
+ return result;
+ }
+
+ /**
+ * Constructs a new ArgumentBuffer using the given array, without copying it first.
+ * None of the array its elements should be empty.
+ *
+ * @param array the array
+ * @throws NullPointerException if the array or any of its elements are null
+ */
+ public ArgumentBuffer(String[] array) {
+ for (String elem : array) {
+ if (elem == null) throw new NullPointerException("ArgumentBuffer array element");
+ }
+ this.array = array;
+
+ }
+
+ public int getCursor() {
+ return cursor;
+ }
+
+ public @NotNull ArgumentBuffer setCursor(int cursor) {
+ if (cursor <= 0) {
+ cursor = 0;
+ } else if (size() <= cursor) {
+ cursor = size();
+ }
+ this.cursor = cursor;
+ return this;
+ }
+
+ @Override
+ public int size() {
+ return array.length;
+ }
+
+ @Override
+ public @NotNull String get(int index) {
+ return array[index];
+ }
+
+ public int nextIndex() {
+ return cursor;
+ }
+
+ public int previousIndex() {
+ return cursor - 1;
+ }
+
+ public int remainingElements() {
+ return size() - nextIndex() - 1;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return nextIndex() < size();
+ }
+
+ public boolean hasPrevious() {
+ return 0 <= previousIndex();
+ }
+
+ /**
+ * Unlike conventional ListIterator implementations, this returns null if there is no next element
+ *
+ * @return the next value, or null
+ */
+ @Override
+ public @Nullable String next() {
+ return hasNext() ? get(cursor++) : null;
+ }
+
+ public @NotNull String requireNext(String parameterName) throws CommandException {
+ String next = next();
+ if (next == null) {
+ throw CommandException.missingArgument(parameterName);
+ }
+ return next;
+ }
+
+ // useful for completion code
+ public @NotNull String nextOrEmpty() {
+ return hasNext() ? get(cursor++) : "";
+ }
+
+ /**
+ * Unlike conventional ListIterator implementations, this returns null if there is no previous element
+ *
+ * @return the previous value, or null
+ */
+ public @Nullable String previous() {
+ return hasPrevious() ? get(--cursor) : null;
+ }
+
+ public @Nullable String peekNext() {
+ return hasNext() ? get(cursor) : null;
+ }
+
+ public @Nullable String peekPrevious() {
+ return hasPrevious() ? get(cursor - 1) : null;
+ }
+
+ public @NotNull ArgumentBuffer advance() {
+ return advance(1);
+ }
+
+ public @NotNull ArgumentBuffer advance(int amount) {
+ cursor = Math.min(Math.max(0, cursor + amount), size());
+ return this;
+ }
+
+ public @NotNull ArgumentBuffer rewind() {
+ return rewind(1);
+ }
+
+ public @NotNull ArgumentBuffer rewind(int amount) {
+ return advance(-amount);
+ }
+
+ @NotNull String[] getArray() {
+ return array;
+ }
+
+ public @NotNull String[] getArrayFromCursor() {
+ return getArrayFromIndex(cursor);
+ }
+
+ public @NotNull String[] getArrayFromIndex(int index) {
+ return Arrays.copyOfRange(array, index, array.length);
+ }
+
+ public @NotNull String getRawInput() {
+ return String.join(" ", array);
+ }
+
+ public @NotNull String[] toArray() {
+ return array.clone();
+ }
+
+ @Override
+ public @NotNull Iterator<String> iterator() {
+ return this;
+ }
+
+ @Override
+ public @NotNull ListIterator<String> listIterator() {
+ return new ListIterator<String>() {
+ @Override
+ public boolean hasNext() {
+ return ArgumentBuffer.this.hasNext();
+ }
+
+ @Override
+ public String next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ return ArgumentBuffer.this.next();
+ }
+
+ @Override
+ public boolean hasPrevious() {
+ return ArgumentBuffer.this.hasPrevious();
+ }
+
+ @Override
+ public String previous() {
+ if (!hasPrevious()) {
+ throw new NoSuchElementException();
+ }
+ return ArgumentBuffer.this.previous();
+ }
+
+ @Override
+ public int nextIndex() {
+ return ArgumentBuffer.this.nextIndex();
+ }
+
+ @Override
+ public int previousIndex() {
+ return ArgumentBuffer.this.previousIndex();
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void set(String s) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void add(String s) {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ public void dropTrailingEmptyElements() {
+ int removeCount = 0;
+ String[] array = this.array;
+ for (int i = array.length - 1; i >= 0; i--) {
+ if ("".equals(array[i])) {
+ removeCount++;
+ }
+ }
+
+ if (removeCount > 0) {
+ String[] newArray = new String[array.length - removeCount];
+ System.arraycopy(array, 0, newArray, 0, newArray.length);
+ this.array = newArray;
+
+ if (cursor > newArray.length) {
+ cursor = newArray.length;
+ }
+ }
+ }
+
+ /**
+ * Preprocess this argument buffer with the given preprocessor
+ *
+ * @param preProcessor preprocessor
+ * @return a new ArgumentBuffer with processed contents. Might be this buffer if nothing changed.
+ */
+ public @NotNull ArgumentBuffer preprocessArguments(IArgumentPreProcessor preProcessor) {
+ return preProcessor.process(this, -1);
+ }
+
+ /**
+ * Allows a piece of code to traverse this buffer without modifying its cursor.
+ * After this method has been called for the first time on this instance, if this method
+ * or the {@link #clone()} method are called, the operation carried out on the prior result has finished.
+ * As such, the same instance might be returned again.
+ *
+ * @return A view of this buffer that doesn't affect this buffer's cursor.
+ */
+ public ArgumentBuffer getUnaffectingCopy() {
+ // the copy doesn't alter the cursor of this ArgumentBuffer when moved, but traverses the same array reference.
+ // there is only ever one copy of an ArgumentBuffer, the cursor of which is updated on every call to this method.
+
+ ArgumentBuffer unaffectingCopy = this.unaffectingCopy;
+ if (unaffectingCopy == null) {
+ this.unaffectingCopy = unaffectingCopy = new ArgumentBuffer(array);
+ }
+ unaffectingCopy.cursor = this.cursor;
+ return unaffectingCopy;
+ }
+
+ @SuppressWarnings("MethodDoesntCallSuperMethod")
+ public @NotNull ArgumentBuffer clone() {
+ ArgumentBuffer result = getUnaffectingCopy();
+ this.unaffectingCopy = null;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("ArgumentBuffer(size = %d, cursor = %d)", size(), getCursor());
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ArgumentMergingPreProcessor.java b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ArgumentMergingPreProcessor.java
index ce818b7..a9ccd20 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ArgumentMergingPreProcessor.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ArgumentMergingPreProcessor.java
@@ -1,177 +1,177 @@
-package io.dico.dicore.command.parameter;
-
-public class ArgumentMergingPreProcessor implements IArgumentPreProcessor {
- private final String tokens;
- private final char escapeChar;
-
- public ArgumentMergingPreProcessor(String tokens, char escapeChar) {
- if ((tokens.length() & 1) != 0 || tokens.isEmpty()) throw new IllegalArgumentException();
- this.tokens = tokens;
- this.escapeChar = escapeChar;
- }
-
- @Override
- public ArgumentBuffer process(ArgumentBuffer buffer, int count) {
- Parser parser = new Parser(buffer.getArray().clone(), buffer.getCursor(), count);
- String[] array = parser.doProcess();
- ArgumentBuffer result = new ArgumentBuffer(array);
- parser.updateBuffer(result);
- return result;
- }
-
- private class Parser {
- private final String[] args;
- private final int start;
- private final int count;
-
- private int foundSectionCount;
- private int currentIndex;
- private int sectionStart;
- private char closingToken;
- private int sectionEnd;
- private int removeCount;
-
- Parser(String[] args, int start, int count) {
- this.start = start;
- this.args = args;
- this.count = count;
- }
-
- private void reset() {
- foundSectionCount = 0;
- currentIndex = start;
- sectionStart = -1;
- closingToken = 0;
- sectionEnd = -1;
- removeCount = 0;
- }
-
- private boolean findNextSectionStart() {
- if (count >= 0 && foundSectionCount >= count) return false;
-
- while (currentIndex < args.length) {
- String arg = args[currentIndex];
- if (arg == null) {
- throw new IllegalArgumentException();
- }
-
- if (arg.isEmpty()) {
- ++currentIndex;
- continue;
- }
-
- int openingTokenIndex = tokens.indexOf(arg.charAt(0));
- if (openingTokenIndex == -1 || (openingTokenIndex & 1) != 0) {
- ++currentIndex;
- continue;
- }
-
- // found
- closingToken = tokens.charAt(openingTokenIndex | 1);
- sectionStart = currentIndex;
- return true;
- }
-
- return false;
- }
-
- private boolean findNextSectionEnd() {
- while (currentIndex < args.length) {
- String arg = args[currentIndex];
- if (arg == null) {
- throw new IllegalArgumentException();
- }
-
- if (arg.isEmpty()
- || arg.charAt(arg.length() - 1) != closingToken
- || (sectionStart == currentIndex && arg.length() == 1)) {
- ++currentIndex;
- continue;
- }
-
- if (escapeChar != 0
- && arg.length() > 1
- && arg.charAt(arg.length() - 2) == escapeChar) {
- // escaped
- ++currentIndex;
- continue;
- }
-
- // found
- closingToken = 0;
- sectionEnd = currentIndex;
- ++currentIndex;
- return true;
- }
-
- return false;
- }
-
- private void processFoundSection() {
- if (sectionStart == sectionEnd) {
- String arg = args[sectionStart];
- args[sectionStart] = arg.substring(1, arg.length() - 1);
- return;
- }
-
- removeCount += sectionEnd - sectionStart;
-
- StringBuilder sb = new StringBuilder();
- sb.append(args[sectionStart].substring(1));
-
- for (int i = sectionStart + 1; i < sectionEnd; i++) {
- sb.append(' ');
- sb.append(args[i]);
- args[i] = null;
- }
- sb.append(' ');
- sb.append(args[sectionEnd].substring(0, args[sectionEnd].length() - 1));
- args[sectionEnd] = null;
-
- args[sectionStart] = sb.toString();
-
- sectionStart = -1;
- sectionEnd = -1;
-
- ++foundSectionCount;
- }
-
- String[] doProcess() {
- reset();
-
- while (findNextSectionStart()) {
- if (findNextSectionEnd()) {
- processFoundSection();
- } else {
- currentIndex = sectionStart + 1;
- }
- }
-
- if (removeCount == 0) {
- return args;
- }
-
- String[] result = new String[args.length - removeCount];
- int i = 0;
- for (String arg : args) {
- if (arg != null) {
- result[i++] = arg;
- }
- }
-
- return result;
- }
-
- void updateBuffer(ArgumentBuffer buffer) {
- if (count < 0) {
- buffer.setCursor(start);
- } else {
- buffer.setCursor(currentIndex);
- }
- }
-
- }
-
-}
-
-
+package io.dico.dicore.command.parameter;
+
+public class ArgumentMergingPreProcessor implements IArgumentPreProcessor {
+ private final String tokens;
+ private final char escapeChar;
+
+ public ArgumentMergingPreProcessor(String tokens, char escapeChar) {
+ if ((tokens.length() & 1) != 0 || tokens.isEmpty()) throw new IllegalArgumentException();
+ this.tokens = tokens;
+ this.escapeChar = escapeChar;
+ }
+
+ @Override
+ public ArgumentBuffer process(ArgumentBuffer buffer, int count) {
+ Parser parser = new Parser(buffer.getArray().clone(), buffer.getCursor(), count);
+ String[] array = parser.doProcess();
+ ArgumentBuffer result = new ArgumentBuffer(array);
+ parser.updateBuffer(result);
+ return result;
+ }
+
+ private class Parser {
+ private final String[] args;
+ private final int start;
+ private final int count;
+
+ private int foundSectionCount;
+ private int currentIndex;
+ private int sectionStart;
+ private char closingToken;
+ private int sectionEnd;
+ private int removeCount;
+
+ Parser(String[] args, int start, int count) {
+ this.start = start;
+ this.args = args;
+ this.count = count;
+ }
+
+ private void reset() {
+ foundSectionCount = 0;
+ currentIndex = start;
+ sectionStart = -1;
+ closingToken = 0;
+ sectionEnd = -1;
+ removeCount = 0;
+ }
+
+ private boolean findNextSectionStart() {
+ if (count >= 0 && foundSectionCount >= count) return false;
+
+ while (currentIndex < args.length) {
+ String arg = args[currentIndex];
+ if (arg == null) {
+ throw new IllegalArgumentException();
+ }
+
+ if (arg.isEmpty()) {
+ ++currentIndex;
+ continue;
+ }
+
+ int openingTokenIndex = tokens.indexOf(arg.charAt(0));
+ if (openingTokenIndex == -1 || (openingTokenIndex & 1) != 0) {
+ ++currentIndex;
+ continue;
+ }
+
+ // found
+ closingToken = tokens.charAt(openingTokenIndex | 1);
+ sectionStart = currentIndex;
+ return true;
+ }
+
+ return false;
+ }
+
+ private boolean findNextSectionEnd() {
+ while (currentIndex < args.length) {
+ String arg = args[currentIndex];
+ if (arg == null) {
+ throw new IllegalArgumentException();
+ }
+
+ if (arg.isEmpty()
+ || arg.charAt(arg.length() - 1) != closingToken
+ || (sectionStart == currentIndex && arg.length() == 1)) {
+ ++currentIndex;
+ continue;
+ }
+
+ if (escapeChar != 0
+ && arg.length() > 1
+ && arg.charAt(arg.length() - 2) == escapeChar) {
+ // escaped
+ ++currentIndex;
+ continue;
+ }
+
+ // found
+ closingToken = 0;
+ sectionEnd = currentIndex;
+ ++currentIndex;
+ return true;
+ }
+
+ return false;
+ }
+
+ private void processFoundSection() {
+ if (sectionStart == sectionEnd) {
+ String arg = args[sectionStart];
+ args[sectionStart] = arg.substring(1, arg.length() - 1);
+ return;
+ }
+
+ removeCount += sectionEnd - sectionStart;
+
+ StringBuilder sb = new StringBuilder();
+ sb.append(args[sectionStart].substring(1));
+
+ for (int i = sectionStart + 1; i < sectionEnd; i++) {
+ sb.append(' ');
+ sb.append(args[i]);
+ args[i] = null;
+ }
+ sb.append(' ');
+ sb.append(args[sectionEnd].substring(0, args[sectionEnd].length() - 1));
+ args[sectionEnd] = null;
+
+ args[sectionStart] = sb.toString();
+
+ sectionStart = -1;
+ sectionEnd = -1;
+
+ ++foundSectionCount;
+ }
+
+ String[] doProcess() {
+ reset();
+
+ while (findNextSectionStart()) {
+ if (findNextSectionEnd()) {
+ processFoundSection();
+ } else {
+ currentIndex = sectionStart + 1;
+ }
+ }
+
+ if (removeCount == 0) {
+ return args;
+ }
+
+ String[] result = new String[args.length - removeCount];
+ int i = 0;
+ for (String arg : args) {
+ if (arg != null) {
+ result[i++] = arg;
+ }
+ }
+
+ return result;
+ }
+
+ void updateBuffer(ArgumentBuffer buffer) {
+ if (count < 0) {
+ buffer.setCursor(start);
+ } else {
+ buffer.setCursor(currentIndex);
+ }
+ }
+
+ }
+
+}
+
+
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ContextParser.java b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ContextParser.java
index a5afce5..f486f52 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ContextParser.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ContextParser.java
@@ -1,276 +1,276 @@
-package io.dico.dicore.command.parameter;
-
-import io.dico.dicore.command.CommandException;
-import io.dico.dicore.command.ExecutionContext;
-
-import java.lang.reflect.Array;
-import java.util.*;
-
-public class ContextParser {
- private final ExecutionContext m_context;
- private final ArgumentBuffer m_buffer;
- private final ParameterList m_paramList;
- private final Parameter<?, ?> m_repeatedParam;
- private final List<Parameter<?, ?>> m_indexedParams;
- private final int m_maxIndex;
- private final int m_maxRequiredIndex;
-
- private Map<String, Object> m_valueMap;
- private Set<String> m_parsedKeys;
- private int m_completionCursor = -1;
- private Parameter<?, ?> m_completionTarget = null;
-
- public ContextParser(ExecutionContext context,
- ParameterList parameterList,
- Map<String, Object> valueMap,
- Set<String> keySet) {
- m_context = context;
- m_paramList = parameterList;
- m_valueMap = valueMap;
- m_parsedKeys = keySet;
-
- m_buffer = context.getBuffer();
- m_repeatedParam = m_paramList.getRepeatedParameter();
- m_indexedParams = m_paramList.getIndexedParameters();
- m_maxIndex = m_indexedParams.size() - 1;
- m_maxRequiredIndex = m_paramList.getRequiredCount() - 1;
- }
-
- public ExecutionContext getContext() {
- return m_context;
- }
-
- public Map<String, Object> getValueMap() {
- return m_valueMap;
- }
-
- public Set<String> getParsedKeys() {
- return m_parsedKeys;
- }
-
- public void parse() throws CommandException {
- parseAllParameters();
- }
-
- public int getCompletionCursor() {
- if (!m_done) {
- throw new IllegalStateException();
- }
- return m_completionCursor;
- }
-
- public Parameter<?, ?> getCompletionTarget() {
- if (!m_done) {
- throw new IllegalStateException();
- }
- return m_completionTarget;
- }
-
- // ################################
- // # PARSING METHODS #
- // ################################
-
- private boolean m_repeating = false;
- private boolean m_done = false;
- private int m_curParamIndex = -1;
- private Parameter<?, ?> m_curParam = null;
- private List<Object> m_curRepeatingList = null;
-
- private void parseAllParameters() throws CommandException {
- try {
- do {
- prepareStateToParseParam();
- if (m_done) break;
- parseCurParam();
- } while (!m_done);
-
- } finally {
- m_curParam = null;
- m_curRepeatingList = null;
- assignDefaultValuesToUncomputedParams();
- arrayifyRepeatedParamValue();
- }
- }
-
- private void prepareStateToParseParam() throws CommandException {
-
- boolean requireInput;
- if (identifyFlag()) {
- m_buffer.advance();
- prepareRepeatedParameterIfSet();
- requireInput = false;
-
- } else if (m_repeating) {
- m_curParam = m_repeatedParam;
- requireInput = false;
-
- } else if (m_curParamIndex < m_maxIndex) {
- m_curParamIndex++;
- m_curParam = m_indexedParams.get(m_curParamIndex);
- prepareRepeatedParameterIfSet();
- requireInput = m_curParamIndex <= m_maxRequiredIndex;
-
- } else if (m_buffer.hasNext()) {
- throw new CommandException("Too many arguments for /" + m_context.getAddress().getAddress());
-
- } else {
- m_done = true;
- return;
- }
-
- if (!m_buffer.hasNext()) {
- if (requireInput) {
- reportParameterRequired(m_curParam);
- }
-
- if (m_repeating) {
- m_done = true;
- }
- }
-
- }
-
- private boolean identifyFlag() {
- String potentialFlag = m_buffer.peekNext();
- Parameter<?, ?> target;
- if (potentialFlag != null
- && potentialFlag.startsWith("-")
- && (target = m_paramList.getParameterByName(potentialFlag)) != null
- && target.isFlag()
- && !m_valueMap.containsKey(potentialFlag)
-
-// Disabled because it's checked by {@link Parameter#parse(ExecutionContext, ArgumentBuffer)}
-// && (target.getFlagPermission() == null || m_context.getSender().hasPermission(target.getFlagPermission()))
- ) {
- m_curParam = target;
- return true;
- }
-
- return false;
- }
-
- private void prepareRepeatedParameterIfSet() throws CommandException {
- if (m_curParam != null && m_curParam == m_repeatedParam) {
-
- if (m_curParam.isFlag() && m_curParamIndex < m_maxRequiredIndex) {
- Parameter<?, ?> requiredParam = m_indexedParams.get(m_curParamIndex + 1);
- reportParameterRequired(requiredParam);
- }
-
- m_curRepeatingList = new ArrayList<>();
- assignValue(m_curRepeatingList);
- m_repeating = true;
- }
- }
-
- private void reportParameterRequired(Parameter<?, ?> param) throws CommandException {
- throw new CommandException("The argument '" + param.getName() + "' is required");
- }
-
- private void parseCurParam() throws CommandException {
- if (!m_buffer.hasNext() && !m_curParam.isFlag()) {
- assignDefaultValue();
- return;
- }
-
- int cursorStart = m_buffer.getCursor();
-
- if (m_context.isTabComplete() && "".equals(m_buffer.peekNext())) {
- assignAsCompletionTarget(cursorStart);
- return;
- }
-
- Object parseResult;
- try {
- parseResult = m_curParam.parse(m_context, m_buffer);
- } catch (CommandException e) {
- assignAsCompletionTarget(cursorStart);
- throw e;
- }
-
- assignValue(parseResult);
- m_parsedKeys.add(m_curParam.getName());
- }
-
- private void assignDefaultValue() throws CommandException {
- assignValue(m_curParam.getDefaultValue(m_context, m_buffer));
- }
-
- private void assignAsCompletionTarget(int cursor) {
- m_completionCursor = cursor;
- m_completionTarget = m_curParam;
- m_done = true;
- }
-
- private void assignValue(Object value) {
- if (m_repeating) {
- m_curRepeatingList.add(value);
- } else {
- m_valueMap.put(m_curParam.getName(), value);
- }
- }
-
- private void assignDefaultValuesToUncomputedParams() throws CommandException {
- // add default values for unset parameters
- for (Map.Entry<String, Parameter<?, ?>> entry : m_paramList.getParametersByName().entrySet()) {
- String name = entry.getKey();
- if (!m_valueMap.containsKey(name)) {
- if (m_repeatedParam == entry.getValue()) {
- // below value will be turned into an array later
- m_valueMap.put(name, Collections.emptyList());
- } else {
- m_valueMap.put(name, entry.getValue().getDefaultValue(m_context, m_buffer));
- }
- }
- }
- }
-
- private void arrayifyRepeatedParamValue() {
- if (m_repeatedParam != null) {
- m_valueMap.computeIfPresent(m_repeatedParam.getName(), (k, v) -> {
- List list = (List) v;
- Class<?> returnType = m_repeatedParam.getType().getReturnType();
- Object array = Array.newInstance(returnType, list.size());
- ArraySetter setter = ArraySetter.getSetter(returnType);
- for (int i = 0, n = list.size(); i < n; i++) {
- setter.set(array, i, list.get(i));
- }
-
- return array;
- });
- }
- }
-
- private interface ArraySetter {
- void set(Object array, int index, Object value);
-
- static ArraySetter getSetter(Class<?> clazz) {
- if (!clazz.isPrimitive()) {
- return (array, index, value) -> ((Object[]) array)[index] = value;
- }
-
- switch (clazz.getSimpleName()) {
- case "boolean":
- return (array, index, value) -> ((boolean[]) array)[index] = (boolean) value;
- case "int":
- return (array, index, value) -> ((int[]) array)[index] = (int) value;
- case "double":
- return (array, index, value) -> ((double[]) array)[index] = (double) value;
- case "long":
- return (array, index, value) -> ((long[]) array)[index] = (long) value;
- case "short":
- return (array, index, value) -> ((short[]) array)[index] = (short) value;
- case "byte":
- return (array, index, value) -> ((byte[]) array)[index] = (byte) value;
- case "float":
- return (array, index, value) -> ((float[]) array)[index] = (float) value;
- case "char":
- return (array, index, value) -> ((char[]) array)[index] = (char) value;
- case "void":
- default:
- throw new InternalError("This should not happen");
- }
- }
- }
-
-}
+package io.dico.dicore.command.parameter;
+
+import io.dico.dicore.command.CommandException;
+import io.dico.dicore.command.ExecutionContext;
+
+import java.lang.reflect.Array;
+import java.util.*;
+
+public class ContextParser {
+ private final ExecutionContext m_context;
+ private final ArgumentBuffer m_buffer;
+ private final ParameterList m_paramList;
+ private final Parameter<?, ?> m_repeatedParam;
+ private final List<Parameter<?, ?>> m_indexedParams;
+ private final int m_maxIndex;
+ private final int m_maxRequiredIndex;
+
+ private Map<String, Object> m_valueMap;
+ private Set<String> m_parsedKeys;
+ private int m_completionCursor = -1;
+ private Parameter<?, ?> m_completionTarget = null;
+
+ public ContextParser(ExecutionContext context,
+ ParameterList parameterList,
+ Map<String, Object> valueMap,
+ Set<String> keySet) {
+ m_context = context;
+ m_paramList = parameterList;
+ m_valueMap = valueMap;
+ m_parsedKeys = keySet;
+
+ m_buffer = context.getBuffer();
+ m_repeatedParam = m_paramList.getRepeatedParameter();
+ m_indexedParams = m_paramList.getIndexedParameters();
+ m_maxIndex = m_indexedParams.size() - 1;
+ m_maxRequiredIndex = m_paramList.getRequiredCount() - 1;
+ }
+
+ public ExecutionContext getContext() {
+ return m_context;
+ }
+
+ public Map<String, Object> getValueMap() {
+ return m_valueMap;
+ }
+
+ public Set<String> getParsedKeys() {
+ return m_parsedKeys;
+ }
+
+ public void parse() throws CommandException {
+ parseAllParameters();
+ }
+
+ public int getCompletionCursor() {
+ if (!m_done) {
+ throw new IllegalStateException();
+ }
+ return m_completionCursor;
+ }
+
+ public Parameter<?, ?> getCompletionTarget() {
+ if (!m_done) {
+ throw new IllegalStateException();
+ }
+ return m_completionTarget;
+ }
+
+ // ################################
+ // # PARSING METHODS #
+ // ################################
+
+ private boolean m_repeating = false;
+ private boolean m_done = false;
+ private int m_curParamIndex = -1;
+ private Parameter<?, ?> m_curParam = null;
+ private List<Object> m_curRepeatingList = null;
+
+ private void parseAllParameters() throws CommandException {
+ try {
+ do {
+ prepareStateToParseParam();
+ if (m_done) break;
+ parseCurParam();
+ } while (!m_done);
+
+ } finally {
+ m_curParam = null;
+ m_curRepeatingList = null;
+ assignDefaultValuesToUncomputedParams();
+ arrayifyRepeatedParamValue();
+ }
+ }
+
+ private void prepareStateToParseParam() throws CommandException {
+
+ boolean requireInput;
+ if (identifyFlag()) {
+ m_buffer.advance();
+ prepareRepeatedParameterIfSet();
+ requireInput = false;
+
+ } else if (m_repeating) {
+ m_curParam = m_repeatedParam;
+ requireInput = false;
+
+ } else if (m_curParamIndex < m_maxIndex) {
+ m_curParamIndex++;
+ m_curParam = m_indexedParams.get(m_curParamIndex);
+ prepareRepeatedParameterIfSet();
+ requireInput = m_curParamIndex <= m_maxRequiredIndex;
+
+ } else if (m_buffer.hasNext()) {
+ throw new CommandException("Too many arguments for /" + m_context.getAddress().getAddress());
+
+ } else {
+ m_done = true;
+ return;
+ }
+
+ if (!m_buffer.hasNext()) {
+ if (requireInput) {
+ reportParameterRequired(m_curParam);
+ }
+
+ if (m_repeating) {
+ m_done = true;
+ }
+ }
+
+ }
+
+ private boolean identifyFlag() {
+ String potentialFlag = m_buffer.peekNext();
+ Parameter<?, ?> target;
+ if (potentialFlag != null
+ && potentialFlag.startsWith("-")
+ && (target = m_paramList.getParameterByName(potentialFlag)) != null
+ && target.isFlag()
+ && !m_valueMap.containsKey(potentialFlag)
+
+// Disabled because it's checked by {@link Parameter#parse(ExecutionContext, ArgumentBuffer)}
+// && (target.getFlagPermission() == null || m_context.getSender().hasPermission(target.getFlagPermission()))
+ ) {
+ m_curParam = target;
+ return true;
+ }
+
+ return false;
+ }
+
+ private void prepareRepeatedParameterIfSet() throws CommandException {
+ if (m_curParam != null && m_curParam == m_repeatedParam) {
+
+ if (m_curParam.isFlag() && m_curParamIndex < m_maxRequiredIndex) {
+ Parameter<?, ?> requiredParam = m_indexedParams.get(m_curParamIndex + 1);
+ reportParameterRequired(requiredParam);
+ }
+
+ m_curRepeatingList = new ArrayList<>();
+ assignValue(m_curRepeatingList);
+ m_repeating = true;
+ }
+ }
+
+ private void reportParameterRequired(Parameter<?, ?> param) throws CommandException {
+ throw new CommandException("The argument '" + param.getName() + "' is required");
+ }
+
+ private void parseCurParam() throws CommandException {
+ if (!m_buffer.hasNext() && !m_curParam.isFlag()) {
+ assignDefaultValue();
+ return;
+ }
+
+ int cursorStart = m_buffer.getCursor();
+
+ if (m_context.isTabComplete() && "".equals(m_buffer.peekNext())) {
+ assignAsCompletionTarget(cursorStart);
+ return;
+ }
+
+ Object parseResult;
+ try {
+ parseResult = m_curParam.parse(m_context, m_buffer);
+ } catch (CommandException e) {
+ assignAsCompletionTarget(cursorStart);
+ throw e;
+ }
+
+ assignValue(parseResult);
+ m_parsedKeys.add(m_curParam.getName());
+ }
+
+ private void assignDefaultValue() throws CommandException {
+ assignValue(m_curParam.getDefaultValue(m_context, m_buffer));
+ }
+
+ private void assignAsCompletionTarget(int cursor) {
+ m_completionCursor = cursor;
+ m_completionTarget = m_curParam;
+ m_done = true;
+ }
+
+ private void assignValue(Object value) {
+ if (m_repeating) {
+ m_curRepeatingList.add(value);
+ } else {
+ m_valueMap.put(m_curParam.getName(), value);
+ }
+ }
+
+ private void assignDefaultValuesToUncomputedParams() throws CommandException {
+ // add default values for unset parameters
+ for (Map.Entry<String, Parameter<?, ?>> entry : m_paramList.getParametersByName().entrySet()) {
+ String name = entry.getKey();
+ if (!m_valueMap.containsKey(name)) {
+ if (m_repeatedParam == entry.getValue()) {
+ // below value will be turned into an array later
+ m_valueMap.put(name, Collections.emptyList());
+ } else {
+ m_valueMap.put(name, entry.getValue().getDefaultValue(m_context, m_buffer));
+ }
+ }
+ }
+ }
+
+ private void arrayifyRepeatedParamValue() {
+ if (m_repeatedParam != null) {
+ m_valueMap.computeIfPresent(m_repeatedParam.getName(), (k, v) -> {
+ List list = (List) v;
+ Class<?> returnType = m_repeatedParam.getType().getReturnType();
+ Object array = Array.newInstance(returnType, list.size());
+ ArraySetter setter = ArraySetter.getSetter(returnType);
+ for (int i = 0, n = list.size(); i < n; i++) {
+ setter.set(array, i, list.get(i));
+ }
+
+ return array;
+ });
+ }
+ }
+
+ private interface ArraySetter {
+ void set(Object array, int index, Object value);
+
+ static ArraySetter getSetter(Class<?> clazz) {
+ if (!clazz.isPrimitive()) {
+ return (array, index, value) -> ((Object[]) array)[index] = value;
+ }
+
+ switch (clazz.getSimpleName()) {
+ case "boolean":
+ return (array, index, value) -> ((boolean[]) array)[index] = (boolean) value;
+ case "int":
+ return (array, index, value) -> ((int[]) array)[index] = (int) value;
+ case "double":
+ return (array, index, value) -> ((double[]) array)[index] = (double) value;
+ case "long":
+ return (array, index, value) -> ((long[]) array)[index] = (long) value;
+ case "short":
+ return (array, index, value) -> ((short[]) array)[index] = (short) value;
+ case "byte":
+ return (array, index, value) -> ((byte[]) array)[index] = (byte) value;
+ case "float":
+ return (array, index, value) -> ((float[]) array)[index] = (float) value;
+ case "char":
+ return (array, index, value) -> ((char[]) array)[index] = (char) value;
+ case "void":
+ default:
+ throw new InternalError("This should not happen");
+ }
+ }
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/IArgumentPreProcessor.java b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/IArgumentPreProcessor.java
index 0b8198e..5495cce 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/IArgumentPreProcessor.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/IArgumentPreProcessor.java
@@ -1,41 +1,41 @@
-package io.dico.dicore.command.parameter;
-
-/**
- * An interface to process tokens such as quotes
- */
-@Deprecated
-public interface IArgumentPreProcessor {
-
- /**
- * Preprocess the arguments contained within the given ArgumentBuffer.
- * If no changes are made, this might return the same buffer.
- * Any arguments preceding {@code buffer.getCursor()} will not be affected.
- *
- * <p>
- * If {@code count} is non-negative, it declares a limit on the number of arguments after preprocessing.
- * In that case, the buffer's cursor is set to the index of the first argument following processed arguments.
- * </p>
- *
- * @param buffer the argument buffer
- * @param count the maximum number of (processed) arguments
- * @return the arguments after preprocessing
- */
- ArgumentBuffer process(ArgumentBuffer buffer, int count);
-
- IArgumentPreProcessor NONE = (buffer, count) -> buffer;
-
- /**
- * Get an IArgumentPreProcessor that merges arguments between any two tokens
- *
- * @param tokens The tokens that the merged arguments should be enclosed by, in subsequent pairs.
- * Example: []{}""
- * This would mean the following would be merged: [ hello this is a merged argument]
- * @param escapeChar the char that can be used to escape the given tokens
- * @return The IArgumentPreProcessor
- */
- static IArgumentPreProcessor mergeOnTokens(String tokens, char escapeChar) {
- return new ArgumentMergingPreProcessor(tokens, escapeChar);
- }
-
-}
-
+package io.dico.dicore.command.parameter;
+
+/**
+ * An interface to process tokens such as quotes
+ */
+@Deprecated
+public interface IArgumentPreProcessor {
+
+ /**
+ * Preprocess the arguments contained within the given ArgumentBuffer.
+ * If no changes are made, this might return the same buffer.
+ * Any arguments preceding {@code buffer.getCursor()} will not be affected.
+ *
+ * <p>
+ * If {@code count} is non-negative, it declares a limit on the number of arguments after preprocessing.
+ * In that case, the buffer's cursor is set to the index of the first argument following processed arguments.
+ * </p>
+ *
+ * @param buffer the argument buffer
+ * @param count the maximum number of (processed) arguments
+ * @return the arguments after preprocessing
+ */
+ ArgumentBuffer process(ArgumentBuffer buffer, int count);
+
+ IArgumentPreProcessor NONE = (buffer, count) -> buffer;
+
+ /**
+ * Get an IArgumentPreProcessor that merges arguments between any two tokens
+ *
+ * @param tokens The tokens that the merged arguments should be enclosed by, in subsequent pairs.
+ * Example: []{}""
+ * This would mean the following would be merged: [ hello this is a merged argument]
+ * @param escapeChar the char that can be used to escape the given tokens
+ * @return The IArgumentPreProcessor
+ */
+ static IArgumentPreProcessor mergeOnTokens(String tokens, char escapeChar) {
+ return new ArgumentMergingPreProcessor(tokens, escapeChar);
+ }
+
+}
+
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ParameterList.java b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ParameterList.java
index 613d057..f35628c 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ParameterList.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ParameterList.java
@@ -1,148 +1,148 @@
-package io.dico.dicore.command.parameter;
-
-import java.util.*;
-
-/**
- * IParameter definition for a command
- */
-@SuppressWarnings("UnusedReturnValue")
-public class ParameterList {
- //private ParameterList parent;
- private List<Parameter<?, ?>> indexedParameters;
- private Map<String, Parameter<?, ?>> byName;
- //private IArgumentPreProcessor argumentPreProcessor = IArgumentPreProcessor.NONE;
- private int requiredCount = -1;
- private boolean repeatFinalParameter;
-
- // if the final parameter is repeated and the command is implemented through reflection,
- // the repeated parameter is simply the last parameter of the method, rather than the last
- // indexed parameter. This might be a flag. As such, this field exists to ensure the correct
- // parameter is taken for repeating
- private boolean finalParameterMayBeFlag;
-
- /*
- public ParameterList(ParameterList parent) {
- this();
- if (parent.repeatFinalParameter) {
- throw new IllegalArgumentException("Parent may not have repeating parameters");
- }
- this.parent = parent;
- }*/
-
- public ParameterList() {
- this.indexedParameters = new ArrayList<>();
- this.byName = new LinkedHashMap<>();
- this.repeatFinalParameter = false;
- }
-
- /*
- public IArgumentPreProcessor getArgumentPreProcessor() {
- return argumentPreProcessor;
- }
-
- public ParameterList setArgumentPreProcessor(IArgumentPreProcessor argumentPreProcessor) {
- this.argumentPreProcessor = argumentPreProcessor == null ? IArgumentPreProcessor.NONE : argumentPreProcessor;
- return this;
- }*/
-
- public boolean repeatFinalParameter() {
- return repeatFinalParameter;
- }
-
- public ParameterList setRepeatFinalParameter(boolean repeatFinalParameter) {
- this.repeatFinalParameter = repeatFinalParameter;
- return this;
- }
-
- public boolean finalParameterMayBeFlag() {
- return finalParameterMayBeFlag;
- }
-
- public ParameterList setFinalParameterMayBeFlag(boolean finalParameterMayBeFlag) {
- this.finalParameterMayBeFlag = finalParameterMayBeFlag;
- return this;
- }
-
- public int getRequiredCount() {
- return requiredCount == -1 ? indexedParameters.size() : requiredCount;
- }
-
- public ParameterList setRequiredCount(int requiredCount) {
- this.requiredCount = requiredCount;
- return this;
- }
-
- public boolean hasAnyParameters() {
- return !byName.isEmpty();
- }
-
- public int getIndexedParameterCount() {
- return indexedParameters.size();
- }
-
- public List<Parameter<?, ?>> getIndexedParameters() {
- return Collections.unmodifiableList(indexedParameters);
- }
-
- public Parameter<?, ?> getParameterByName(String name) {
- return byName.get(name);
- }
-
- public String getIndexedParameterName(int index) {
- return indexedParameters.get(index).getName();
- }
-
- public Map<String, Parameter<?, ?>> getParametersByName() {
- return Collections.unmodifiableMap(byName);
- }
-
- /**
- * Add the given parameter to the end of this parameter list
- * Can be a flag
- *
- * @param parameter the parameter
- * @return this
- */
- public ParameterList addParameter(Parameter<?, ?> parameter) {
- return addParameter(-1, parameter);
- }
-
- /**
- * Add the given parameter to this parameter list
- * If the parameter is a flag, the index is ignored
- *
- * @param index parameter index number, -1 if end
- * @param parameter the parameter
- * @return this
- * @throws NullPointerException if parameter is null
- */
- public ParameterList addParameter(int index, Parameter<?, ?> parameter) {
- //System.out.println("Added parameter " + parameter.getName() + ", flag: " + parameter.isFlag());
- byName.put(parameter.getName(), parameter);
- if (!parameter.isFlag()) {
- indexedParameters.add(index == -1 ? indexedParameters.size() : index, parameter);
- }
- return this;
- }
-
- public Parameter<?, ?> getRepeatedParameter() {
- if (!repeatFinalParameter) {
- return null;
- }
- if (finalParameterMayBeFlag) {
- Iterator<Parameter<?, ?>> iterator = byName.values().iterator();
- Parameter<?, ?> result = null;
- while (iterator.hasNext()) {
- result = iterator.next();
- }
- return result;
- }
-
- if (indexedParameters.isEmpty()) {
- return null;
- }
-
- return indexedParameters.get(indexedParameters.size() - 1);
- }
-
-}
+package io.dico.dicore.command.parameter;
+
+import java.util.*;
+
+/**
+ * IParameter definition for a command
+ */
+@SuppressWarnings("UnusedReturnValue")
+public class ParameterList {
+ //private ParameterList parent;
+ private List<Parameter<?, ?>> indexedParameters;
+ private Map<String, Parameter<?, ?>> byName;
+ //private IArgumentPreProcessor argumentPreProcessor = IArgumentPreProcessor.NONE;
+ private int requiredCount = -1;
+ private boolean repeatFinalParameter;
+
+ // if the final parameter is repeated and the command is implemented through reflection,
+ // the repeated parameter is simply the last parameter of the method, rather than the last
+ // indexed parameter. This might be a flag. As such, this field exists to ensure the correct
+ // parameter is taken for repeating
+ private boolean finalParameterMayBeFlag;
+
+ /*
+ public ParameterList(ParameterList parent) {
+ this();
+ if (parent.repeatFinalParameter) {
+ throw new IllegalArgumentException("Parent may not have repeating parameters");
+ }
+ this.parent = parent;
+ }*/
+
+ public ParameterList() {
+ this.indexedParameters = new ArrayList<>();
+ this.byName = new LinkedHashMap<>();
+ this.repeatFinalParameter = false;
+ }
+
+ /*
+ public IArgumentPreProcessor getArgumentPreProcessor() {
+ return argumentPreProcessor;
+ }
+
+ public ParameterList setArgumentPreProcessor(IArgumentPreProcessor argumentPreProcessor) {
+ this.argumentPreProcessor = argumentPreProcessor == null ? IArgumentPreProcessor.NONE : argumentPreProcessor;
+ return this;
+ }*/
+
+ public boolean repeatFinalParameter() {
+ return repeatFinalParameter;
+ }
+
+ public ParameterList setRepeatFinalParameter(boolean repeatFinalParameter) {
+ this.repeatFinalParameter = repeatFinalParameter;
+ return this;
+ }
+
+ public boolean finalParameterMayBeFlag() {
+ return finalParameterMayBeFlag;
+ }
+
+ public ParameterList setFinalParameterMayBeFlag(boolean finalParameterMayBeFlag) {
+ this.finalParameterMayBeFlag = finalParameterMayBeFlag;
+ return this;
+ }
+
+ public int getRequiredCount() {
+ return requiredCount == -1 ? indexedParameters.size() : requiredCount;
+ }
+
+ public ParameterList setRequiredCount(int requiredCount) {
+ this.requiredCount = requiredCount;
+ return this;
+ }
+
+ public boolean hasAnyParameters() {
+ return !byName.isEmpty();
+ }
+
+ public int getIndexedParameterCount() {
+ return indexedParameters.size();
+ }
+
+ public List<Parameter<?, ?>> getIndexedParameters() {
+ return Collections.unmodifiableList(indexedParameters);
+ }
+
+ public Parameter<?, ?> getParameterByName(String name) {
+ return byName.get(name);
+ }
+
+ public String getIndexedParameterName(int index) {
+ return indexedParameters.get(index).getName();
+ }
+
+ public Map<String, Parameter<?, ?>> getParametersByName() {
+ return Collections.unmodifiableMap(byName);
+ }
+
+ /**
+ * Add the given parameter to the end of this parameter list
+ * Can be a flag
+ *
+ * @param parameter the parameter
+ * @return this
+ */
+ public ParameterList addParameter(Parameter<?, ?> parameter) {
+ return addParameter(-1, parameter);
+ }
+
+ /**
+ * Add the given parameter to this parameter list
+ * If the parameter is a flag, the index is ignored
+ *
+ * @param index parameter index number, -1 if end
+ * @param parameter the parameter
+ * @return this
+ * @throws NullPointerException if parameter is null
+ */
+ public ParameterList addParameter(int index, Parameter<?, ?> parameter) {
+ //System.out.println("Added parameter " + parameter.getName() + ", flag: " + parameter.isFlag());
+ byName.put(parameter.getName(), parameter);
+ if (!parameter.isFlag()) {
+ indexedParameters.add(index == -1 ? indexedParameters.size() : index, parameter);
+ }
+ return this;
+ }
+
+ public Parameter<?, ?> getRepeatedParameter() {
+ if (!repeatFinalParameter) {
+ return null;
+ }
+ if (finalParameterMayBeFlag) {
+ Iterator<Parameter<?, ?>> iterator = byName.values().iterator();
+ Parameter<?, ?> result = null;
+ while (iterator.hasNext()) {
+ result = iterator.next();
+ }
+ return result;
+ }
+
+ if (indexedParameters.isEmpty()) {
+ return null;
+ }
+
+ return indexedParameters.get(indexedParameters.size() - 1);
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/EnumParameterType.java b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/EnumParameterType.java
index c23e09b..063d381 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/EnumParameterType.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/EnumParameterType.java
@@ -1,46 +1,46 @@
-package io.dico.dicore.command.parameter.type;
-
-import io.dico.dicore.command.CommandException;
-import io.dico.dicore.command.parameter.ArgumentBuffer;
-import io.dico.dicore.command.parameter.Parameter;
-import org.bukkit.Location;
-import org.bukkit.command.CommandSender;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class EnumParameterType<E extends Enum> extends SimpleParameterType<E, Void> {
- private final E[] universe;
-
- public EnumParameterType(Class<E> returnType) {
- super(returnType);
- universe = returnType.getEnumConstants();
- if (universe == null) {
- throw new IllegalArgumentException("returnType must be an enum");
- }
- }
-
- @Override
- protected E parse(Parameter<E, Void> parameter, CommandSender sender, String input) throws CommandException {
- for (E constant : universe) {
- if (constant.name().equalsIgnoreCase(input)) {
- return constant;
- }
- }
-
- throw CommandException.invalidArgument(parameter.getName(), "the enum value does not exist");
- }
-
- @Override
- public List<String> complete(Parameter<E, Void> parameter, CommandSender sender, Location location, ArgumentBuffer buffer) {
- String input = buffer.next().toUpperCase();
- List<String> result = new ArrayList<>();
- for (E constant : universe) {
- if (constant.name().toUpperCase().startsWith(input.toUpperCase())) {
- result.add(constant.name().toLowerCase());
- }
- }
- return result;
- }
-
-}
+package io.dico.dicore.command.parameter.type;
+
+import io.dico.dicore.command.CommandException;
+import io.dico.dicore.command.parameter.ArgumentBuffer;
+import io.dico.dicore.command.parameter.Parameter;
+import org.bukkit.Location;
+import org.bukkit.command.CommandSender;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class EnumParameterType<E extends Enum> extends SimpleParameterType<E, Void> {
+ private final E[] universe;
+
+ public EnumParameterType(Class<E> returnType) {
+ super(returnType);
+ universe = returnType.getEnumConstants();
+ if (universe == null) {
+ throw new IllegalArgumentException("returnType must be an enum");
+ }
+ }
+
+ @Override
+ protected E parse(Parameter<E, Void> parameter, CommandSender sender, String input) throws CommandException {
+ for (E constant : universe) {
+ if (constant.name().equalsIgnoreCase(input)) {
+ return constant;
+ }
+ }
+
+ throw CommandException.invalidArgument(parameter.getName(), "the enum value does not exist");
+ }
+
+ @Override
+ public List<String> complete(Parameter<E, Void> parameter, CommandSender sender, Location location, ArgumentBuffer buffer) {
+ String input = buffer.next().toUpperCase();
+ List<String> result = new ArrayList<>();
+ for (E constant : universe) {
+ if (constant.name().toUpperCase().startsWith(input.toUpperCase())) {
+ result.add(constant.name().toLowerCase());
+ }
+ }
+ return result;
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/IParameterTypeSelector.java b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/IParameterTypeSelector.java
index 780ea0d..381b15c 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/IParameterTypeSelector.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/IParameterTypeSelector.java
@@ -1,44 +1,44 @@
-package io.dico.dicore.command.parameter.type;
-
-import java.lang.annotation.Annotation;
-
-/**
- * An interface for an object that stores parameter types by {@link ParameterKey} and finds appropriate types for {@link ParameterKey parameterKeys}
- */
-public interface IParameterTypeSelector {
-
- <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExact(ParameterKey key);
-
- //<TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExactOrSubclass(ParameterKey key);
-
- <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectAny(ParameterKey key);
-
-
- default <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExact(Class<?> returnType) {
- return selectExact(returnType, null);
- }
-
- default <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExact(Class<?> returnType, Class<? extends Annotation> annotationClass) {
- return selectExact(new ParameterKey(returnType, annotationClass));
- }
-
- /*
- default <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExactOrSubclass(Class<?> returnType) {
- return selectExactOrSubclass(returnType, null);
- }
-
- default <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExactOrSubclass(Class<?> returnType, Class<? extends Annotation> annotationClass) {
- return selectExactOrSubclass(new ParameterKey(returnType, annotationClass));
- }
- */
- default <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectAny(Class<?> returnType) {
- return selectAny(returnType, null);
- }
-
- default <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectAny(Class<?> returnType, Class<? extends Annotation> annotationClass) {
- return selectAny(new ParameterKey(returnType, annotationClass));
- }
-
- void addType(boolean infolessAlias, ParameterType<?, ?> type);
-
-}
+package io.dico.dicore.command.parameter.type;
+
+import java.lang.annotation.Annotation;
+
+/**
+ * An interface for an object that stores parameter types by {@link ParameterKey} and finds appropriate types for {@link ParameterKey parameterKeys}
+ */
+public interface IParameterTypeSelector {
+
+ <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExact(ParameterKey key);
+
+ //<TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExactOrSubclass(ParameterKey key);
+
+ <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectAny(ParameterKey key);
+
+
+ default <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExact(Class<?> returnType) {
+ return selectExact(returnType, null);
+ }
+
+ default <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExact(Class<?> returnType, Class<? extends Annotation> annotationClass) {
+ return selectExact(new ParameterKey(returnType, annotationClass));
+ }
+
+ /*
+ default <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExactOrSubclass(Class<?> returnType) {
+ return selectExactOrSubclass(returnType, null);
+ }
+
+ default <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExactOrSubclass(Class<?> returnType, Class<? extends Annotation> annotationClass) {
+ return selectExactOrSubclass(new ParameterKey(returnType, annotationClass));
+ }
+ */
+ default <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectAny(Class<?> returnType) {
+ return selectAny(returnType, null);
+ }
+
+ default <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectAny(Class<?> returnType, Class<? extends Annotation> annotationClass) {
+ return selectAny(new ParameterKey(returnType, annotationClass));
+ }
+
+ void addType(boolean infolessAlias, ParameterType<?, ?> type);
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/MapBasedParameterTypeSelector.java b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/MapBasedParameterTypeSelector.java
index d407f87..ef86eab 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/MapBasedParameterTypeSelector.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/MapBasedParameterTypeSelector.java
@@ -1,114 +1,114 @@
-package io.dico.dicore.command.parameter.type;
-
-import java.lang.annotation.Annotation;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Map based implementation of {@link IParameterTypeSelector}
- */
-public class MapBasedParameterTypeSelector implements IParameterTypeSelector {
- static final MapBasedParameterTypeSelector defaultSelector = new MapBasedParameterTypeSelector(false);
- private final Map<ParameterKey, ParameterType<?, ?>> parameterTypeMap;
- private final boolean useDefault;
-
- public MapBasedParameterTypeSelector(boolean useDefault) {
- this.parameterTypeMap = new HashMap<>();
- this.useDefault = useDefault;
- }
-
- @Override
- public <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExact(ParameterKey key) {
- ParameterType<?, ?> out = parameterTypeMap.get(key);
- if (useDefault && out == null) {
- out = defaultSelector.selectExact(key);
- }
- if (out == null && key.getReturnType().isEnum()) {
- //noinspection unchecked
- out = new EnumParameterType(key.getReturnType());
- addType(false, out);
- }
- return cast(out);
- }
-
- @Override
- public <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectAny(ParameterKey key) {
- ParameterType<TReturn, TParamInfo> exact = selectExact(key);
- if (exact != null) {
- return exact;
- }
-
- if (key.getAnnotationClass() != null) {
- exact = selectExact(new ParameterKey(key.getReturnType()));
- if (exact != null) {
- return exact;
- }
- }
-
- Class<?> returnType = key.getReturnType();
- Class<? extends Annotation> annotationClass = key.getAnnotationClass();
-
- ParameterType<?, ?> out = selectByReturnType(parameterTypeMap, returnType, annotationClass, false);
- if (out == null && useDefault) {
- out = selectByReturnType(defaultSelector.parameterTypeMap, returnType, annotationClass, false);
- }
- if (out == null) {
- out = selectByReturnType(parameterTypeMap, returnType, annotationClass, true);
- }
- if (out == null && useDefault) {
- out = selectByReturnType(defaultSelector.parameterTypeMap, returnType, annotationClass, true);
- }
- return cast(out);
- }
-
- private static ParameterType<?, ?> selectByReturnType(Map<ParameterKey, ParameterType<?, ?>> map, Class<?> returnType,
- Class<? extends Annotation> annotationClass, boolean allowSubclass) {
- ParameterType<?, ?> out = null;
- if (allowSubclass) {
- for (ParameterType<?, ?> type : map.values()) {
- if (returnType.isAssignableFrom(type.getReturnType())) {
- if (annotationClass == type.getAnnotationClass()) {
- out = type;
- break;
- }
- if (out == null) {
- out = type;
- }
- }
- }
- } else {
- for (ParameterType<?, ?> type : map.values()) {
- if (returnType == type.getReturnType()) {
- if (annotationClass == type.getAnnotationClass()) {
- out = type;
- break;
- }
- if (out == null) {
- out = type;
- }
- }
- }
- }
- return out;
- }
-
- private static <T> T cast(Object o) {
- //noinspection unchecked
- return (T) o;
- }
-
- @Override
- public void addType(boolean infolessAlias, ParameterType<?, ?> type) {
- parameterTypeMap.put(type.getTypeKey(), type);
-
- if (infolessAlias) {
- parameterTypeMap.putIfAbsent(type.getInfolessTypeKey(), type);
- }
- }
-
- static {
- // registers default parameter types
- ParameterTypes.clinit();
- }
-
-}
+package io.dico.dicore.command.parameter.type;
+
+import java.lang.annotation.Annotation;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Map based implementation of {@link IParameterTypeSelector}
+ */
+public class MapBasedParameterTypeSelector implements IParameterTypeSelector {
+ static final MapBasedParameterTypeSelector defaultSelector = new MapBasedParameterTypeSelector(false);
+ private final Map<ParameterKey, ParameterType<?, ?>> parameterTypeMap;
+ private final boolean useDefault;
+
+ public MapBasedParameterTypeSelector(boolean useDefault) {
+ this.parameterTypeMap = new HashMap<>();
+ this.useDefault = useDefault;
+ }
+
+ @Override
+ public <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExact(ParameterKey key) {
+ ParameterType<?, ?> out = parameterTypeMap.get(key);
+ if (useDefault && out == null) {
+ out = defaultSelector.selectExact(key);
+ }
+ if (out == null && key.getReturnType().isEnum()) {
+ //noinspection unchecked
+ out = new EnumParameterType(key.getReturnType());
+ addType(false, out);
+ }
+ return cast(out);
+ }
+
+ @Override
+ public <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectAny(ParameterKey key) {
+ ParameterType<TReturn, TParamInfo> exact = selectExact(key);
+ if (exact != null) {
+ return exact;
+ }
+
+ if (key.getAnnotationClass() != null) {
+ exact = selectExact(new ParameterKey(key.getReturnType()));
+ if (exact != null) {
+ return exact;
+ }
+ }
+
+ Class<?> returnType = key.getReturnType();
+ Class<? extends Annotation> annotationClass = key.getAnnotationClass();
+
+ ParameterType<?, ?> out = selectByReturnType(parameterTypeMap, returnType, annotationClass, false);
+ if (out == null && useDefault) {
+ out = selectByReturnType(defaultSelector.parameterTypeMap, returnType, annotationClass, false);
+ }
+ if (out == null) {
+ out = selectByReturnType(parameterTypeMap, returnType, annotationClass, true);
+ }
+ if (out == null && useDefault) {
+ out = selectByReturnType(defaultSelector.parameterTypeMap, returnType, annotationClass, true);
+ }
+ return cast(out);
+ }
+
+ private static ParameterType<?, ?> selectByReturnType(Map<ParameterKey, ParameterType<?, ?>> map, Class<?> returnType,
+ Class<? extends Annotation> annotationClass, boolean allowSubclass) {
+ ParameterType<?, ?> out = null;
+ if (allowSubclass) {
+ for (ParameterType<?, ?> type : map.values()) {
+ if (returnType.isAssignableFrom(type.getReturnType())) {
+ if (annotationClass == type.getAnnotationClass()) {
+ out = type;
+ break;
+ }
+ if (out == null) {
+ out = type;
+ }
+ }
+ }
+ } else {
+ for (ParameterType<?, ?> type : map.values()) {
+ if (returnType == type.getReturnType()) {
+ if (annotationClass == type.getAnnotationClass()) {
+ out = type;
+ break;
+ }
+ if (out == null) {
+ out = type;
+ }
+ }
+ }
+ }
+ return out;
+ }
+
+ private static <T> T cast(Object o) {
+ //noinspection unchecked
+ return (T) o;
+ }
+
+ @Override
+ public void addType(boolean infolessAlias, ParameterType<?, ?> type) {
+ parameterTypeMap.put(type.getTypeKey(), type);
+
+ if (infolessAlias) {
+ parameterTypeMap.putIfAbsent(type.getInfolessTypeKey(), type);
+ }
+ }
+
+ static {
+ // registers default parameter types
+ ParameterTypes.clinit();
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/ParameterConfig.java b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/ParameterConfig.java
index dbd7590..d33932b 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/ParameterConfig.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/ParameterConfig.java
@@ -1,80 +1,80 @@
-package io.dico.dicore.command.parameter.type;
-
-import io.dico.dicore.Reflection;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Constructor;
-
-/**
- * This class serves the purpose of having annotated parameter configurations (such as ranges for number parameters).
- * Such configurations must be possible to obtain without using annotations, and as such, there should be a class conveying the information
- * that is separate from the annotation itself. This class acts as a bridge from the annotation to said class conveying the information.
- *
- * @param <TAnnotation> the annotation type for parameters
- * @param <TParamInfo> the object type that holds the information required in memory
- */
-public abstract class ParameterConfig<TAnnotation extends Annotation, TParamInfo> implements Comparable<ParameterConfig<?, ?>> {
- private final Class<TAnnotation> annotationClass;
- // protected final TParamInfo defaultValue;
-
- public ParameterConfig(Class<TAnnotation> annotationClass/*, TParamInfo defaultValue*/) {
- this.annotationClass = annotationClass;
- //this.defaultValue = defaultValue;
- }
-
- public final Class<TAnnotation> getAnnotationClass() {
- return annotationClass;
- }
- /*
- public TParamInfo getDefaultValue() {
- return defaultValue;
- }*/
-
- protected abstract TParamInfo toParameterInfo(TAnnotation annotation);
-
- public TParamInfo getParameterInfo(Annotation annotation) {
- //noinspection unchecked
- return toParameterInfo((TAnnotation) annotation);
- }
-
- public static <TAnnotation extends Annotation, TParamInfo> ParameterConfig<TAnnotation, TParamInfo>
- includeMemoryClass(Class<TAnnotation> annotationClass, Class<TParamInfo> memoryClass) {
- Constructor<TParamInfo> constructor;
- //TParamInfo defaultValue;
- try {
- constructor = memoryClass.getConstructor(annotationClass);
- //defaultValue = Reflection.getStaticFieldValue(annotationClass, "DEFAULT");
- } catch (NoSuchMethodException | IllegalArgumentException ex) {
- throw new IllegalArgumentException(ex);
- }
- /*
- if (defaultValue == null) try {
- defaultValue = memoryClass.newInstance();
- } catch (IllegalAccessException | InstantiationException ex) {
- throw new IllegalArgumentException("Failed to get a default value for the param info", ex);
- }*/
-
- return new ParameterConfig<TAnnotation, TParamInfo>(annotationClass/*, defaultValue*/) {
-
- @Override
- public TParamInfo toParameterInfo(TAnnotation annotation) {
- try {
- return constructor.newInstance(annotation);
- } catch (Exception ex) {
- throw new RuntimeException(ex);
- }
- }
- };
- }
-
- public static <TAnnotation extends Annotation, TParamInfo> ParameterConfig<TAnnotation, TParamInfo> getMemoryClassFromField(Class<TAnnotation> annotationClass) {
- return ParameterConfig.includeMemoryClass(annotationClass, Reflection.getStaticFieldValue(annotationClass, "MEMORY_CLASS"));
- }
-
- @Override
- public int compareTo(ParameterConfig<?, ?> o) {
- return 0;
- }
-
-}
-
+package io.dico.dicore.command.parameter.type;
+
+import io.dico.dicore.Reflection;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+
+/**
+ * This class serves the purpose of having annotated parameter configurations (such as ranges for number parameters).
+ * Such configurations must be possible to obtain without using annotations, and as such, there should be a class conveying the information
+ * that is separate from the annotation itself. This class acts as a bridge from the annotation to said class conveying the information.
+ *
+ * @param <TAnnotation> the annotation type for parameters
+ * @param <TParamInfo> the object type that holds the information required in memory
+ */
+public abstract class ParameterConfig<TAnnotation extends Annotation, TParamInfo> implements Comparable<ParameterConfig<?, ?>> {
+ private final Class<TAnnotation> annotationClass;
+ // protected final TParamInfo defaultValue;
+
+ public ParameterConfig(Class<TAnnotation> annotationClass/*, TParamInfo defaultValue*/) {
+ this.annotationClass = annotationClass;
+ //this.defaultValue = defaultValue;
+ }
+
+ public final Class<TAnnotation> getAnnotationClass() {
+ return annotationClass;
+ }
+ /*
+ public TParamInfo getDefaultValue() {
+ return defaultValue;
+ }*/
+
+ protected abstract TParamInfo toParameterInfo(TAnnotation annotation);
+
+ public TParamInfo getParameterInfo(Annotation annotation) {
+ //noinspection unchecked
+ return toParameterInfo((TAnnotation) annotation);
+ }
+
+ public static <TAnnotation extends Annotation, TParamInfo> ParameterConfig<TAnnotation, TParamInfo>
+ includeMemoryClass(Class<TAnnotation> annotationClass, Class<TParamInfo> memoryClass) {
+ Constructor<TParamInfo> constructor;
+ //TParamInfo defaultValue;
+ try {
+ constructor = memoryClass.getConstructor(annotationClass);
+ //defaultValue = Reflection.getStaticFieldValue(annotationClass, "DEFAULT");
+ } catch (NoSuchMethodException | IllegalArgumentException ex) {
+ throw new IllegalArgumentException(ex);
+ }
+ /*
+ if (defaultValue == null) try {
+ defaultValue = memoryClass.newInstance();
+ } catch (IllegalAccessException | InstantiationException ex) {
+ throw new IllegalArgumentException("Failed to get a default value for the param info", ex);
+ }*/
+
+ return new ParameterConfig<TAnnotation, TParamInfo>(annotationClass/*, defaultValue*/) {
+
+ @Override
+ public TParamInfo toParameterInfo(TAnnotation annotation) {
+ try {
+ return constructor.newInstance(annotation);
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ };
+ }
+
+ public static <TAnnotation extends Annotation, TParamInfo> ParameterConfig<TAnnotation, TParamInfo> getMemoryClassFromField(Class<TAnnotation> annotationClass) {
+ return ParameterConfig.includeMemoryClass(annotationClass, Reflection.getStaticFieldValue(annotationClass, "MEMORY_CLASS"));
+ }
+
+ @Override
+ public int compareTo(ParameterConfig<?, ?> o) {
+ return 0;
+ }
+
+}
+
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/ParameterType.java b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/ParameterType.java
index e1a62fa..e9cca7f 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/ParameterType.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/ParameterType.java
@@ -1,149 +1,149 @@
-package io.dico.dicore.command.parameter.type;
-
-import io.dico.dicore.Reflection;
-import io.dico.dicore.command.CommandException;
-import io.dico.dicore.command.ExecutionContext;
-import io.dico.dicore.command.annotation.Range;
-import io.dico.dicore.command.parameter.ArgumentBuffer;
-import io.dico.dicore.command.parameter.Parameter;
-import org.bukkit.Location;
-import org.bukkit.command.CommandSender;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * A parameter type.
- * Takes care of parsing, default values as well as completions.
- *
- * @param <TReturn> type of the parameter
- * @param <TParamInfo> the info object type for the parameter (Example: {@link Range.Memory}
- */
-public abstract class ParameterType<TReturn, TParamInfo> {
- private final Class<TReturn> returnType;
- private final ParameterConfig<?, TParamInfo> parameterConfig;
- protected final ParameterType<TReturn, TParamInfo> otherType; // flag or non-flag, depending on current
-
- public ParameterType(Class<TReturn> returnType) {
- this(returnType, null);
- }
-
- public ParameterType(Class<TReturn> returnType, ParameterConfig<?, TParamInfo> paramConfig) {
- this.returnType = Objects.requireNonNull(returnType);
- this.parameterConfig = paramConfig;
-
- ParameterType<TReturn, TParamInfo> otherType = flagTypeParameter();
- this.otherType = otherType == null ? this : otherType;
- }
-
- protected ParameterType(Class<TReturn> returnType, ParameterConfig<?, TParamInfo> parameterConfig, ParameterType<TReturn, TParamInfo> otherType) {
- this.returnType = returnType;
- this.parameterConfig = parameterConfig;
- this.otherType = otherType;
- }
-
- public int getExpectedAmountOfConsumedArguments() {
- return 1;
- }
-
- public boolean canBeFlag() {
- return this == otherType;
- }
-
- public boolean isFlagExplicitly() {
- return this instanceof FlagParameterType;
- }
-
- /**
- * @return The return type
- */
- public final Class<TReturn> getReturnType() {
- return returnType;
- }
-
- public final Class<?> getAnnotationClass() {
- return parameterConfig == null ? null : parameterConfig.getAnnotationClass();
- }
-
- public final ParameterConfig<?, TParamInfo> getParameterConfig() {
- return parameterConfig;
- }
-
- public ParameterKey getTypeKey() {
- return new ParameterKey(returnType, parameterConfig != null ? parameterConfig.getAnnotationClass() : null);
- }
-
- public ParameterKey getInfolessTypeKey() {
- return new ParameterKey(returnType, null);
- }
-
- protected FlagParameterType<TReturn, TParamInfo> flagTypeParameter() {
- return null;
- }
-
- public ParameterType<TReturn, TParamInfo> asFlagParameter() {
- return canBeFlag() ? this : otherType;
- }
-
- public ParameterType<TReturn, TParamInfo> asNormalParameter() {
- return isFlagExplicitly() ? otherType : this;
- }
-
- public abstract TReturn parse(Parameter<TReturn, TParamInfo> parameter, CommandSender sender, ArgumentBuffer buffer) throws CommandException;
-
- public TReturn parseForContext(Parameter<TReturn, TParamInfo> parameter, ExecutionContext context, ArgumentBuffer buffer) throws CommandException {
- return parse(parameter, context.getSender(), buffer);
- }
-
- public TReturn getDefaultValue(Parameter<TReturn, TParamInfo> parameter, CommandSender sender, ArgumentBuffer buffer) throws CommandException {
- return null;
- }
-
- public TReturn getDefaultValueForContext(Parameter<TReturn, TParamInfo> parameter, ExecutionContext context, ArgumentBuffer buffer) throws CommandException {
- return getDefaultValue(parameter, context.getSender(), buffer);
- }
-
- public List<String> complete(Parameter<TReturn, TParamInfo> parameter, CommandSender sender, Location location, ArgumentBuffer buffer) {
- return Collections.emptyList();
- }
-
- public List<String> completeForContext(Parameter<TReturn, TParamInfo> parameter, ExecutionContext context, Location location, ArgumentBuffer buffer) {
- return complete(parameter, context.getSender(), location, buffer);
- }
-
- protected static abstract class FlagParameterType<TResult, TParamInfo> extends ParameterType<TResult, TParamInfo> {
-
- protected FlagParameterType(ParameterType<TResult, TParamInfo> otherType) {
- super(otherType.returnType, otherType.parameterConfig, otherType);
- }
-
- @Override
- public int getExpectedAmountOfConsumedArguments() {
- return otherType.getExpectedAmountOfConsumedArguments();
- }
-
- @Override
- public boolean canBeFlag() {
- return true;
- }
-
- @Override
- protected final FlagParameterType<TResult, TParamInfo> flagTypeParameter() {
- return this;
- }
-
- @Override
- public ParameterType<TResult, TParamInfo> asFlagParameter() {
- return this;
- }
-
- @Override
- public ParameterType<TResult, TParamInfo> asNormalParameter() {
- return otherType;
- }
-
- }
-
-}
+package io.dico.dicore.command.parameter.type;
+
+import io.dico.dicore.Reflection;
+import io.dico.dicore.command.CommandException;
+import io.dico.dicore.command.ExecutionContext;
+import io.dico.dicore.command.annotation.Range;
+import io.dico.dicore.command.parameter.ArgumentBuffer;
+import io.dico.dicore.command.parameter.Parameter;
+import org.bukkit.Location;
+import org.bukkit.command.CommandSender;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A parameter type.
+ * Takes care of parsing, default values as well as completions.
+ *
+ * @param <TReturn> type of the parameter
+ * @param <TParamInfo> the info object type for the parameter (Example: {@link Range.Memory}
+ */
+public abstract class ParameterType<TReturn, TParamInfo> {
+ private final Class<TReturn> returnType;
+ private final ParameterConfig<?, TParamInfo> parameterConfig;
+ protected final ParameterType<TReturn, TParamInfo> otherType; // flag or non-flag, depending on current
+
+ public ParameterType(Class<TReturn> returnType) {
+ this(returnType, null);
+ }
+
+ public ParameterType(Class<TReturn> returnType, ParameterConfig<?, TParamInfo> paramConfig) {
+ this.returnType = Objects.requireNonNull(returnType);
+ this.parameterConfig = paramConfig;
+
+ ParameterType<TReturn, TParamInfo> otherType = flagTypeParameter();
+ this.otherType = otherType == null ? this : otherType;
+ }
+
+ protected ParameterType(Class<TReturn> returnType, ParameterConfig<?, TParamInfo> parameterConfig, ParameterType<TReturn, TParamInfo> otherType) {
+ this.returnType = returnType;
+ this.parameterConfig = parameterConfig;
+ this.otherType = otherType;
+ }
+
+ public int getExpectedAmountOfConsumedArguments() {
+ return 1;
+ }
+
+ public boolean canBeFlag() {
+ return this == otherType;
+ }
+
+ public boolean isFlagExplicitly() {
+ return this instanceof FlagParameterType;
+ }
+
+ /**
+ * @return The return type
+ */
+ public final Class<TReturn> getReturnType() {
+ return returnType;
+ }
+
+ public final Class<?> getAnnotationClass() {
+ return parameterConfig == null ? null : parameterConfig.getAnnotationClass();
+ }
+
+ public final ParameterConfig<?, TParamInfo> getParameterConfig() {
+ return parameterConfig;
+ }
+
+ public ParameterKey getTypeKey() {
+ return new ParameterKey(returnType, parameterConfig != null ? parameterConfig.getAnnotationClass() : null);
+ }
+
+ public ParameterKey getInfolessTypeKey() {
+ return new ParameterKey(returnType, null);
+ }
+
+ protected FlagParameterType<TReturn, TParamInfo> flagTypeParameter() {
+ return null;
+ }
+
+ public ParameterType<TReturn, TParamInfo> asFlagParameter() {
+ return canBeFlag() ? this : otherType;
+ }
+
+ public ParameterType<TReturn, TParamInfo> asNormalParameter() {
+ return isFlagExplicitly() ? otherType : this;
+ }
+
+ public abstract TReturn parse(Parameter<TReturn, TParamInfo> parameter, CommandSender sender, ArgumentBuffer buffer) throws CommandException;
+
+ public TReturn parseForContext(Parameter<TReturn, TParamInfo> parameter, ExecutionContext context, ArgumentBuffer buffer) throws CommandException {
+ return parse(parameter, context.getSender(), buffer);
+ }
+
+ public TReturn getDefaultValue(Parameter<TReturn, TParamInfo> parameter, CommandSender sender, ArgumentBuffer buffer) throws CommandException {
+ return null;
+ }
+
+ public TReturn getDefaultValueForContext(Parameter<TReturn, TParamInfo> parameter, ExecutionContext context, ArgumentBuffer buffer) throws CommandException {
+ return getDefaultValue(parameter, context.getSender(), buffer);
+ }
+
+ public List<String> complete(Parameter<TReturn, TParamInfo> parameter, CommandSender sender, Location location, ArgumentBuffer buffer) {
+ return Collections.emptyList();
+ }
+
+ public List<String> completeForContext(Parameter<TReturn, TParamInfo> parameter, ExecutionContext context, Location location, ArgumentBuffer buffer) {
+ return complete(parameter, context.getSender(), location, buffer);
+ }
+
+ protected static abstract class FlagParameterType<TResult, TParamInfo> extends ParameterType<TResult, TParamInfo> {
+
+ protected FlagParameterType(ParameterType<TResult, TParamInfo> otherType) {
+ super(otherType.returnType, otherType.parameterConfig, otherType);
+ }
+
+ @Override
+ public int getExpectedAmountOfConsumedArguments() {
+ return otherType.getExpectedAmountOfConsumedArguments();
+ }
+
+ @Override
+ public boolean canBeFlag() {
+ return true;
+ }
+
+ @Override
+ protected final FlagParameterType<TResult, TParamInfo> flagTypeParameter() {
+ return this;
+ }
+
+ @Override
+ public ParameterType<TResult, TParamInfo> asFlagParameter() {
+ return this;
+ }
+
+ @Override
+ public ParameterType<TResult, TParamInfo> asNormalParameter() {
+ return otherType;
+ }
+
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/SimpleParameterType.java b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/SimpleParameterType.java
index ecf19ce..667b2c7 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/SimpleParameterType.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/SimpleParameterType.java
@@ -1,31 +1,31 @@
-package io.dico.dicore.command.parameter.type;
-
-import io.dico.dicore.command.CommandException;
-import io.dico.dicore.command.parameter.ArgumentBuffer;
-import io.dico.dicore.command.parameter.Parameter;
-import org.bukkit.command.CommandSender;
-
-/**
- * An abstraction for parameter types that only parse a single argument
- *
- * @param <TReturn> the parameter type
- * @param <TParamInfo> parameter info object type
- */
-public abstract class SimpleParameterType<TReturn, TParamInfo> extends ParameterType<TReturn, TParamInfo> {
-
- public SimpleParameterType(Class<TReturn> returnType) {
- super(returnType);
- }
-
- public SimpleParameterType(Class<TReturn> returnType, ParameterConfig<?, TParamInfo> paramConfig) {
- super(returnType, paramConfig);
- }
-
- protected abstract TReturn parse(Parameter<TReturn, TParamInfo> parameter, CommandSender sender, String input) throws CommandException;
-
- @Override
- public TReturn parse(Parameter<TReturn, TParamInfo> parameter, CommandSender sender, ArgumentBuffer buffer) throws CommandException {
- return parse(parameter, sender, buffer.requireNext(parameter.getName()));
- }
-
-}
+package io.dico.dicore.command.parameter.type;
+
+import io.dico.dicore.command.CommandException;
+import io.dico.dicore.command.parameter.ArgumentBuffer;
+import io.dico.dicore.command.parameter.Parameter;
+import org.bukkit.command.CommandSender;
+
+/**
+ * An abstraction for parameter types that only parse a single argument
+ *
+ * @param <TReturn> the parameter type
+ * @param <TParamInfo> parameter info object type
+ */
+public abstract class SimpleParameterType<TReturn, TParamInfo> extends ParameterType<TReturn, TParamInfo> {
+
+ public SimpleParameterType(Class<TReturn> returnType) {
+ super(returnType);
+ }
+
+ public SimpleParameterType(Class<TReturn> returnType, ParameterConfig<?, TParamInfo> paramConfig) {
+ super(returnType, paramConfig);
+ }
+
+ protected abstract TReturn parse(Parameter<TReturn, TParamInfo> parameter, CommandSender sender, String input) throws CommandException;
+
+ @Override
+ public TReturn parse(Parameter<TReturn, TParamInfo> parameter, CommandSender sender, ArgumentBuffer buffer) throws CommandException {
+ return parse(parameter, sender, buffer.requireNext(parameter.getName()));
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/predef/DefaultGroupCommand.java b/dicore3/command/src/main/java/io/dico/dicore/command/predef/DefaultGroupCommand.java
index e664cef..6828fcc 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/predef/DefaultGroupCommand.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/predef/DefaultGroupCommand.java
@@ -1,56 +1,56 @@
-package io.dico.dicore.command.predef;
-
-import io.dico.dicore.command.CommandException;
-import io.dico.dicore.command.ExecutionContext;
-import io.dico.dicore.command.IContextFilter;
-import org.bukkit.command.CommandSender;
-
-public class DefaultGroupCommand extends PredefinedCommand<DefaultGroupCommand> {
- private static final DefaultGroupCommand instance;
- private static final IContextFilter noArgumentFilter;
-
- public static DefaultGroupCommand getInstance() {
- return instance;
- }
-
- private DefaultGroupCommand(boolean modifiable) {
- addContextFilter(IContextFilter.INHERIT_PERMISSIONS);
- addContextFilter(noArgumentFilter);
- this.modifiable = modifiable;
- }
-
- public DefaultGroupCommand() {
- this(true);
- }
-
- @Override
- protected DefaultGroupCommand newModifiableInstance() {
- return new DefaultGroupCommand(true);
- }
-
- @Override
- public String execute(CommandSender sender, ExecutionContext context) throws CommandException {
- context.getAddress().getChatHandler().sendHelpMessage(sender, context, context.getAddress(), 1);
- return null;
- }
-
- static {
- noArgumentFilter = new IContextFilter() {
- @Override
- public void filterContext(ExecutionContext context) throws CommandException {
- if (context.getBuffer().hasNext()) {
- throw new CommandException("No such command: /" + context.getAddress().getAddress()
- + " " + context.getBuffer().next());
- }
- }
-
- @Override
- public Priority getPriority() {
- return Priority.EARLY;
- }
- };
-
- instance = new DefaultGroupCommand(false);
- }
-
-}
+package io.dico.dicore.command.predef;
+
+import io.dico.dicore.command.CommandException;
+import io.dico.dicore.command.ExecutionContext;
+import io.dico.dicore.command.IContextFilter;
+import org.bukkit.command.CommandSender;
+
+public class DefaultGroupCommand extends PredefinedCommand<DefaultGroupCommand> {
+ private static final DefaultGroupCommand instance;
+ private static final IContextFilter noArgumentFilter;
+
+ public static DefaultGroupCommand getInstance() {
+ return instance;
+ }
+
+ private DefaultGroupCommand(boolean modifiable) {
+ addContextFilter(IContextFilter.INHERIT_PERMISSIONS);
+ addContextFilter(noArgumentFilter);
+ this.modifiable = modifiable;
+ }
+
+ public DefaultGroupCommand() {
+ this(true);
+ }
+
+ @Override
+ protected DefaultGroupCommand newModifiableInstance() {
+ return new DefaultGroupCommand(true);
+ }
+
+ @Override
+ public String execute(CommandSender sender, ExecutionContext context) throws CommandException {
+ context.getAddress().getChatHandler().sendHelpMessage(sender, context, context.getAddress(), 1);
+ return null;
+ }
+
+ static {
+ noArgumentFilter = new IContextFilter() {
+ @Override
+ public void filterContext(ExecutionContext context) throws CommandException {
+ if (context.getBuffer().hasNext()) {
+ throw new CommandException("No such command: /" + context.getAddress().getAddress()
+ + " " + context.getBuffer().next());
+ }
+ }
+
+ @Override
+ public Priority getPriority() {
+ return Priority.EARLY;
+ }
+ };
+
+ instance = new DefaultGroupCommand(false);
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/predef/HelpCommand.java b/dicore3/command/src/main/java/io/dico/dicore/command/predef/HelpCommand.java
index def0db1..985e055 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/predef/HelpCommand.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/predef/HelpCommand.java
@@ -1,76 +1,76 @@
-package io.dico.dicore.command.predef;
-
-import io.dico.dicore.command.*;
-import io.dico.dicore.command.annotation.Range;
-import io.dico.dicore.command.parameter.ArgumentBuffer;
-import io.dico.dicore.command.parameter.Parameter;
-import io.dico.dicore.command.parameter.type.NumberParameterType;
-import org.bukkit.command.CommandSender;
-
-/**
- * The help command
- */
-public class HelpCommand extends PredefinedCommand<HelpCommand> {
- private static final Parameter<Integer, Range.Memory> pageParameter;
- public static final HelpCommand INSTANCE;
-
- private HelpCommand(boolean modifiable) {
- super(modifiable);
- getParameterList().addParameter(pageParameter);
- getParameterList().setRequiredCount(0);
- setDescription("Shows this help page");
- }
-
- @Override
- protected HelpCommand newModifiableInstance() {
- return new HelpCommand(true);
- }
-
- @Override
- public String execute(CommandSender sender, ExecutionContext context) throws CommandException {
- ICommandAddress target = context.getAddress();
- if (context.getAddress().getCommand() == this) {
- target = target.getParent();
- }
-
- context.getAddress().getChatHandler().sendHelpMessage(sender, context, target, context.<Integer>get("page") - 1);
- return null;
- }
-
- public static void registerAsChild(ICommandAddress address) {
- registerAsChild(address, "help");
- }
-
- public static void registerAsChild(ICommandAddress address, String main, String... aliases) {
- ((ModifiableCommandAddress) address).addChild(new ChildCommandAddress(INSTANCE, main, aliases));
- }
-
- static {
- pageParameter = new Parameter<>("page", "the page number",
- new NumberParameterType<Integer>(Integer.TYPE) {
- @Override
- protected Integer parse(String input) throws NumberFormatException {
- return Integer.parseInt(input);
- }
-
- @Override
- protected Integer select(Number number) {
- return number.intValue();
- }
-
- @Override
- public Integer parseForContext(Parameter<Integer, Range.Memory> parameter, ExecutionContext context, ArgumentBuffer buffer) throws CommandException {
- if (context.getAddress().getCommand() == null || context.getAddress().getCommand().getClass() != HelpCommand.class) {
- // An address was executed with its help command as target
- buffer.next();
- return 1;
- }
- return parse(parameter, context.getSender(), buffer);
- }
- },
- new Range.Memory(1, Integer.MAX_VALUE, 1));
-
- INSTANCE = new HelpCommand(false);
- }
-
-}
+package io.dico.dicore.command.predef;
+
+import io.dico.dicore.command.*;
+import io.dico.dicore.command.annotation.Range;
+import io.dico.dicore.command.parameter.ArgumentBuffer;
+import io.dico.dicore.command.parameter.Parameter;
+import io.dico.dicore.command.parameter.type.NumberParameterType;
+import org.bukkit.command.CommandSender;
+
+/**
+ * The help command
+ */
+public class HelpCommand extends PredefinedCommand<HelpCommand> {
+ private static final Parameter<Integer, Range.Memory> pageParameter;
+ public static final HelpCommand INSTANCE;
+
+ private HelpCommand(boolean modifiable) {
+ super(modifiable);
+ getParameterList().addParameter(pageParameter);
+ getParameterList().setRequiredCount(0);
+ setDescription("Shows this help page");
+ }
+
+ @Override
+ protected HelpCommand newModifiableInstance() {
+ return new HelpCommand(true);
+ }
+
+ @Override
+ public String execute(CommandSender sender, ExecutionContext context) throws CommandException {
+ ICommandAddress target = context.getAddress();
+ if (context.getAddress().getCommand() == this) {
+ target = target.getParent();
+ }
+
+ context.getAddress().getChatHandler().sendHelpMessage(sender, context, target, context.<Integer>get("page") - 1);
+ return null;
+ }
+
+ public static void registerAsChild(ICommandAddress address) {
+ registerAsChild(address, "help");
+ }
+
+ public static void registerAsChild(ICommandAddress address, String main, String... aliases) {
+ ((ModifiableCommandAddress) address).addChild(new ChildCommandAddress(INSTANCE, main, aliases));
+ }
+
+ static {
+ pageParameter = new Parameter<>("page", "the page number",
+ new NumberParameterType<Integer>(Integer.TYPE) {
+ @Override
+ protected Integer parse(String input) throws NumberFormatException {
+ return Integer.parseInt(input);
+ }
+
+ @Override
+ protected Integer select(Number number) {
+ return number.intValue();
+ }
+
+ @Override
+ public Integer parseForContext(Parameter<Integer, Range.Memory> parameter, ExecutionContext context, ArgumentBuffer buffer) throws CommandException {
+ if (context.getAddress().getCommand() == null || context.getAddress().getCommand().getClass() != HelpCommand.class) {
+ // An address was executed with its help command as target
+ buffer.next();
+ return 1;
+ }
+ return parse(parameter, context.getSender(), buffer);
+ }
+ },
+ new Range.Memory(1, Integer.MAX_VALUE, 1));
+
+ INSTANCE = new HelpCommand(false);
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/predef/PredefinedCommand.java b/dicore3/command/src/main/java/io/dico/dicore/command/predef/PredefinedCommand.java
index 4340356..b7c5587 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/predef/PredefinedCommand.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/predef/PredefinedCommand.java
@@ -1,50 +1,50 @@
-package io.dico.dicore.command.predef;
-
-import io.dico.dicore.command.CommandBuilder;
-import io.dico.dicore.command.ExtendedCommand;
-import io.dico.dicore.command.ICommandAddress;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.function.Consumer;
-
-/**
- * Marker class for commands that are generated. These commands can be replaced using methods in {@link CommandBuilder}
- */
-public abstract class PredefinedCommand<T extends PredefinedCommand<T>> extends ExtendedCommand<T> {
- static final Map<String, Consumer<ICommandAddress>> predefinedCommandGenerators = new HashMap<>();
-
- /**
- * Get a predefined command
- *
- * @param name the name
- * @return the subscriber
- */
- public static Consumer<ICommandAddress> getPredefinedCommandGenerator(String name) {
- return predefinedCommandGenerators.get(name);
- }
-
- /**
- * Register a predefined command
- *
- * @param name the name
- * @param consumer the generator which adds the child to the address
- * @return true if and only if the subscriber was registered (false if the name exists)
- */
- public static boolean registerPredefinedCommandGenerator(String name, Consumer<ICommandAddress> consumer) {
- return predefinedCommandGenerators.putIfAbsent(name, consumer) == null;
- }
-
- static {
- registerPredefinedCommandGenerator("help", HelpCommand::registerAsChild);
- //noinspection StaticInitializerReferencesSubClass
- registerPredefinedCommandGenerator("syntax", SyntaxCommand::registerAsChild);
- }
-
- public PredefinedCommand() {
- }
-
- public PredefinedCommand(boolean modifiable) {
- super(modifiable);
- }
-}
+package io.dico.dicore.command.predef;
+
+import io.dico.dicore.command.CommandBuilder;
+import io.dico.dicore.command.ExtendedCommand;
+import io.dico.dicore.command.ICommandAddress;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Consumer;
+
+/**
+ * Marker class for commands that are generated. These commands can be replaced using methods in {@link CommandBuilder}
+ */
+public abstract class PredefinedCommand<T extends PredefinedCommand<T>> extends ExtendedCommand<T> {
+ static final Map<String, Consumer<ICommandAddress>> predefinedCommandGenerators = new HashMap<>();
+
+ /**
+ * Get a predefined command
+ *
+ * @param name the name
+ * @return the subscriber
+ */
+ public static Consumer<ICommandAddress> getPredefinedCommandGenerator(String name) {
+ return predefinedCommandGenerators.get(name);
+ }
+
+ /**
+ * Register a predefined command
+ *
+ * @param name the name
+ * @param consumer the generator which adds the child to the address
+ * @return true if and only if the subscriber was registered (false if the name exists)
+ */
+ public static boolean registerPredefinedCommandGenerator(String name, Consumer<ICommandAddress> consumer) {
+ return predefinedCommandGenerators.putIfAbsent(name, consumer) == null;
+ }
+
+ static {
+ registerPredefinedCommandGenerator("help", HelpCommand::registerAsChild);
+ //noinspection StaticInitializerReferencesSubClass
+ registerPredefinedCommandGenerator("syntax", SyntaxCommand::registerAsChild);
+ }
+
+ public PredefinedCommand() {
+ }
+
+ public PredefinedCommand(boolean modifiable) {
+ super(modifiable);
+ }
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/predef/SyntaxCommand.java b/dicore3/command/src/main/java/io/dico/dicore/command/predef/SyntaxCommand.java
index 7ae8638..8532584 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/predef/SyntaxCommand.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/predef/SyntaxCommand.java
@@ -1,36 +1,36 @@
-package io.dico.dicore.command.predef;
-
-import io.dico.dicore.command.*;
-import org.bukkit.command.CommandSender;
-
-/**
- * The syntax command
- */
-public class SyntaxCommand extends PredefinedCommand<SyntaxCommand> {
- public static final SyntaxCommand INSTANCE = new SyntaxCommand(false);
-
- private SyntaxCommand(boolean modifiable) {
- super(modifiable);
- setDescription("Describes how to use the command");
- }
-
- @Override
- protected SyntaxCommand newModifiableInstance() {
- return new SyntaxCommand(true);
- }
-
- @Override
- public String execute(CommandSender sender, ExecutionContext context) throws CommandException {
- context.getAddress().getChatHandler().sendSyntaxMessage(sender, context, context.getAddress().getParent());
- return null;
- }
-
- public static void registerAsChild(ICommandAddress address) {
- registerAsChild(address, "syntax");
- }
-
- public static void registerAsChild(ICommandAddress address, String main, String... aliases) {
- ((ModifiableCommandAddress) address).addChild(new ChildCommandAddress(INSTANCE, main, aliases));
- }
-
-}
+package io.dico.dicore.command.predef;
+
+import io.dico.dicore.command.*;
+import org.bukkit.command.CommandSender;
+
+/**
+ * The syntax command
+ */
+public class SyntaxCommand extends PredefinedCommand<SyntaxCommand> {
+ public static final SyntaxCommand INSTANCE = new SyntaxCommand(false);
+
+ private SyntaxCommand(boolean modifiable) {
+ super(modifiable);
+ setDescription("Describes how to use the command");
+ }
+
+ @Override
+ protected SyntaxCommand newModifiableInstance() {
+ return new SyntaxCommand(true);
+ }
+
+ @Override
+ public String execute(CommandSender sender, ExecutionContext context) throws CommandException {
+ context.getAddress().getChatHandler().sendSyntaxMessage(sender, context, context.getAddress().getParent());
+ return null;
+ }
+
+ public static void registerAsChild(ICommandAddress address) {
+ registerAsChild(address, "syntax");
+ }
+
+ public static void registerAsChild(ICommandAddress address, String main, String... aliases) {
+ ((ModifiableCommandAddress) address).addChild(new ChildCommandAddress(INSTANCE, main, aliases));
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/registration/BukkitCommand.java b/dicore3/command/src/main/java/io/dico/dicore/command/registration/BukkitCommand.java
index b5346d0..40de6ae 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/registration/BukkitCommand.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/registration/BukkitCommand.java
@@ -1,122 +1,122 @@
-package io.dico.dicore.command.registration;
-
-import io.dico.dicore.command.ICommandAddress;
-import io.dico.dicore.command.ICommandDispatcher;
-import org.bukkit.Location;
-import org.bukkit.command.Command;
-import org.bukkit.command.CommandSender;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.util.List;
-
-/**
- * This class extends the bukkit's command class.
- * Instances are injected into the command map.
- */
-public class BukkitCommand extends Command {
- private ICommandDispatcher dispatcher;
- private ICommandAddress origin;
-
- public BukkitCommand(ICommandAddress address) {
- super(validateTree(address).getNames().get(0), "", "", address.getNames().subList(1, address.getNames().size()));
- this.dispatcher = address.getDispatcherForTree();
- this.origin = address;
-
- setTimingsIfNecessary(this);
- }
-
- private static ICommandAddress validateTree(ICommandAddress tree) {
- if (!tree.hasParent()) {
- throw new IllegalArgumentException();
- }
- if (tree.getNames().isEmpty()) {
- throw new IllegalArgumentException();
- }
- return tree;
- }
-
- public ICommandAddress getOrigin() {
- return origin;
- }
-
- @Override
- public boolean execute(CommandSender sender, String label, String[] args) {
- if (!dispatcher.dispatchCommand(sender, label, args)) {
- //System.out.println("failed to dispatch command");
- // target command not found, send a message in the future TODO
- }
- return true;
- }
-
- @Override
- public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException {
- return this.tabComplete(sender, alias, args, null);
- }
-
- //@Override
- public List<String> tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException {
- return dispatcher.getTabCompletions(sender, alias, location, args);
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- BukkitCommand that = (BukkitCommand) o;
-
- return getName().equals(that.getName()) && dispatcher == that.dispatcher;
- }
-
- @Override
- public int hashCode() {
- return dispatcher.hashCode() | getName().hashCode();
- }
-
- private static void setTimingsIfNecessary(Command object) {
- // with paper spigot, the timings are not set by super constructor but by CommandMap.register(), which is not invoked for this system
- // I use reflection so that the project does not require paper spigot to build
- try {
- // public field
- Field field = Command.class.getDeclaredField("timings");
- if (field.get(object) != null) return;
- Class<?> clazz = Class.forName("co.aikar.timings.TimingsManager");
- // public method
- Method method = clazz.getDeclaredMethod("getCommandTiming", String.class, Command.class);
- Object timings = method.invoke(null, "", object);
- field.set(object, timings);
- } catch (Throwable ignored) {
- }
- }
-
- /*
- public static void registerToMap(ICommandAddress tree, Map<String, Command> map) {
- BukkitCommand command = new BukkitCommand(tree);
- Iterator<String> iterator = tree.getNames().iterator();
- map.put(iterator.next(), command);
- while (iterator.hasNext()) {
- map.putIfAbsent(iterator.next(), command);
- }
- }
-
- public static void unregisterFromMap(ICommandAddress tree, Map<String, Command> map) {
- map.values().remove(new BukkitCommand(tree));
- }
-
- public static void registerChildrenToMap(ICommandAddress tree, Map<String, Command> map) {
- for (Map.Entry<String, ? extends ICommandAddress> entry : tree.getChildren().entrySet()) {
- ICommandAddress child = entry.getValue();
- registerToMap(child, map);
- }
- }
-
- public static void unregisterChildenFromMap(ICommandAddress tree, Map<String, Command> map) {
- for (Map.Entry<String, ? extends ICommandAddress> entry : tree.getChildren().entrySet()) {
- ICommandAddress child = entry.getValue();
- unregisterFromMap(child, map);
- }
- }
- */
-
-}
+package io.dico.dicore.command.registration;
+
+import io.dico.dicore.command.ICommandAddress;
+import io.dico.dicore.command.ICommandDispatcher;
+import org.bukkit.Location;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.List;
+
+/**
+ * This class extends the bukkit's command class.
+ * Instances are injected into the command map.
+ */
+public class BukkitCommand extends Command {
+ private ICommandDispatcher dispatcher;
+ private ICommandAddress origin;
+
+ public BukkitCommand(ICommandAddress address) {
+ super(validateTree(address).getNames().get(0), "", "", address.getNames().subList(1, address.getNames().size()));
+ this.dispatcher = address.getDispatcherForTree();
+ this.origin = address;
+
+ setTimingsIfNecessary(this);
+ }
+
+ private static ICommandAddress validateTree(ICommandAddress tree) {
+ if (!tree.hasParent()) {
+ throw new IllegalArgumentException();
+ }
+ if (tree.getNames().isEmpty()) {
+ throw new IllegalArgumentException();
+ }
+ return tree;
+ }
+
+ public ICommandAddress getOrigin() {
+ return origin;
+ }
+
+ @Override
+ public boolean execute(CommandSender sender, String label, String[] args) {
+ if (!dispatcher.dispatchCommand(sender, label, args)) {
+ //System.out.println("failed to dispatch command");
+ // target command not found, send a message in the future TODO
+ }
+ return true;
+ }
+
+ @Override
+ public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException {
+ return this.tabComplete(sender, alias, args, null);
+ }
+
+ //@Override
+ public List<String> tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException {
+ return dispatcher.getTabCompletions(sender, alias, location, args);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ BukkitCommand that = (BukkitCommand) o;
+
+ return getName().equals(that.getName()) && dispatcher == that.dispatcher;
+ }
+
+ @Override
+ public int hashCode() {
+ return dispatcher.hashCode() | getName().hashCode();
+ }
+
+ private static void setTimingsIfNecessary(Command object) {
+ // with paper spigot, the timings are not set by super constructor but by CommandMap.register(), which is not invoked for this system
+ // I use reflection so that the project does not require paper spigot to build
+ try {
+ // public field
+ Field field = Command.class.getDeclaredField("timings");
+ if (field.get(object) != null) return;
+ Class<?> clazz = Class.forName("co.aikar.timings.TimingsManager");
+ // public method
+ Method method = clazz.getDeclaredMethod("getCommandTiming", String.class, Command.class);
+ Object timings = method.invoke(null, "", object);
+ field.set(object, timings);
+ } catch (Throwable ignored) {
+ }
+ }
+
+ /*
+ public static void registerToMap(ICommandAddress tree, Map<String, Command> map) {
+ BukkitCommand command = new BukkitCommand(tree);
+ Iterator<String> iterator = tree.getNames().iterator();
+ map.put(iterator.next(), command);
+ while (iterator.hasNext()) {
+ map.putIfAbsent(iterator.next(), command);
+ }
+ }
+
+ public static void unregisterFromMap(ICommandAddress tree, Map<String, Command> map) {
+ map.values().remove(new BukkitCommand(tree));
+ }
+
+ public static void registerChildrenToMap(ICommandAddress tree, Map<String, Command> map) {
+ for (Map.Entry<String, ? extends ICommandAddress> entry : tree.getChildren().entrySet()) {
+ ICommandAddress child = entry.getValue();
+ registerToMap(child, map);
+ }
+ }
+
+ public static void unregisterChildenFromMap(ICommandAddress tree, Map<String, Command> map) {
+ for (Map.Entry<String, ? extends ICommandAddress> entry : tree.getChildren().entrySet()) {
+ ICommandAddress child = entry.getValue();
+ unregisterFromMap(child, map);
+ }
+ }
+ */
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/registration/CommandMap.java b/dicore3/command/src/main/java/io/dico/dicore/command/registration/CommandMap.java
index 780ec66..2008b29 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/registration/CommandMap.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/registration/CommandMap.java
@@ -1,59 +1,59 @@
-package io.dico.dicore.command.registration;
-
-import io.dico.dicore.Reflection;
-import org.bukkit.Bukkit;
-import org.bukkit.command.Command;
-import org.bukkit.command.SimpleCommandMap;
-import org.bukkit.plugin.SimplePluginManager;
-
-import java.util.*;
-
-/**
- * Provides access to bukkit's {@code Map<String, org.bukkit.command.Command>} command map.
- */
-@SuppressWarnings("ConstantConditions")
-public class CommandMap {
- private static final Map<String, Command> commandMap = findCommandMap();
-
- private CommandMap() {
-
- }
-
- public static Map<String, Command> getCommandMap() {
- return Objects.requireNonNull(commandMap);
- }
-
- public static boolean isAvailable() {
- return commandMap != null;
- }
-
- public static Command get(String key) {
- return commandMap.get(key);
- }
-
- public static void put(String key, Command command) {
- commandMap.put(key, command);
- }
-
- public static Collection<String> replace(Command command, Command replacement) {
- List<String> result = new ArrayList<>();
- for (Map.Entry<String, Command> entry : commandMap.entrySet()) {
- if (entry.getValue() == command) {
- entry.setValue(replacement);
- result.add(entry.getKey());
- }
- }
- return result;
- }
-
- private static Map<String, Command> findCommandMap() {
- try {
- return Reflection.getFieldValue(SimpleCommandMap.class, "knownCommands",
- Reflection.getFieldValue(SimplePluginManager.class, "commandMap", Bukkit.getPluginManager()));
- } catch (Exception ex) {
- ex.printStackTrace();
- return null;
- }
- }
-
-}
+package io.dico.dicore.command.registration;
+
+import io.dico.dicore.Reflection;
+import org.bukkit.Bukkit;
+import org.bukkit.command.Command;
+import org.bukkit.command.SimpleCommandMap;
+import org.bukkit.plugin.SimplePluginManager;
+
+import java.util.*;
+
+/**
+ * Provides access to bukkit's {@code Map<String, org.bukkit.command.Command>} command map.
+ */
+@SuppressWarnings("ConstantConditions")
+public class CommandMap {
+ private static final Map<String, Command> commandMap = findCommandMap();
+
+ private CommandMap() {
+
+ }
+
+ public static Map<String, Command> getCommandMap() {
+ return Objects.requireNonNull(commandMap);
+ }
+
+ public static boolean isAvailable() {
+ return commandMap != null;
+ }
+
+ public static Command get(String key) {
+ return commandMap.get(key);
+ }
+
+ public static void put(String key, Command command) {
+ commandMap.put(key, command);
+ }
+
+ public static Collection<String> replace(Command command, Command replacement) {
+ List<String> result = new ArrayList<>();
+ for (Map.Entry<String, Command> entry : commandMap.entrySet()) {
+ if (entry.getValue() == command) {
+ entry.setValue(replacement);
+ result.add(entry.getKey());
+ }
+ }
+ return result;
+ }
+
+ private static Map<String, Command> findCommandMap() {
+ try {
+ return Reflection.getFieldValue(SimpleCommandMap.class, "knownCommands",
+ Reflection.getFieldValue(SimplePluginManager.class, "commandMap", Bukkit.getPluginManager()));
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ return null;
+ }
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/CommandParseException.java b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/CommandParseException.java
index 478c70c..0b7ce3c 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/CommandParseException.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/CommandParseException.java
@@ -1,27 +1,27 @@
-package io.dico.dicore.command.registration.reflect;
-
-/**
- * Thrown if an error occurs while 'parsing' a reflection command method
- * Other errors can be thrown too in there that may not be directly relevant to a parsing error.
- */
-public class CommandParseException extends Exception {
-
- public CommandParseException() {
- }
-
- public CommandParseException(String message) {
- super(message);
- }
-
- public CommandParseException(String message, Throwable cause) {
- super(message, cause);
- }
-
- public CommandParseException(Throwable cause) {
- super(cause);
- }
-
- public CommandParseException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
- super(message, cause, enableSuppression, writableStackTrace);
- }
-}
+package io.dico.dicore.command.registration.reflect;
+
+/**
+ * Thrown if an error occurs while 'parsing' a reflection command method
+ * Other errors can be thrown too in there that may not be directly relevant to a parsing error.
+ */
+public class CommandParseException extends Exception {
+
+ public CommandParseException() {
+ }
+
+ public CommandParseException(String message) {
+ super(message);
+ }
+
+ public CommandParseException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public CommandParseException(Throwable cause) {
+ super(cause);
+ }
+
+ public CommandParseException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ICommandInterceptor.java b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ICommandInterceptor.java
index 67b65e4..1e56b8a 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ICommandInterceptor.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ICommandInterceptor.java
@@ -1,36 +1,36 @@
-package io.dico.dicore.command.registration.reflect;
-
-import io.dico.dicore.command.ExecutionContext;
-
-import java.lang.reflect.Method;
-
-public interface ICommandInterceptor {
-
- /**
- * Get the receiver of the command, if applicable.
- * A command has a receiver if its first parameter implements {@link ICommandReceiver}
- * and its instance object implements this interface.
- *
- * @param context the context of execution
- * @param target the method of the command
- * @param cmdName the name of the command
- * @return the receiver
- */
- default ICommandReceiver getReceiver(ExecutionContext context, Method target, String cmdName) {
- return null;
- }
-
- /**
- * If applicable, get the coroutine context to use in suspend functions (Kotlin only).
- * The return type is object to avoid depending on the kotlin runtime.
- *
- * @param context the context of execution
- * @param target the method of the command
- * @param cmdName the name of the command
- * @return the coroutine context
- */
- default Object getCoroutineContext(ExecutionContext context, Method target, String cmdName) {
- return null;
- }
-
-}
+package io.dico.dicore.command.registration.reflect;
+
+import io.dico.dicore.command.ExecutionContext;
+
+import java.lang.reflect.Method;
+
+public interface ICommandInterceptor {
+
+ /**
+ * Get the receiver of the command, if applicable.
+ * A command has a receiver if its first parameter implements {@link ICommandReceiver}
+ * and its instance object implements this interface.
+ *
+ * @param context the context of execution
+ * @param target the method of the command
+ * @param cmdName the name of the command
+ * @return the receiver
+ */
+ default ICommandReceiver getReceiver(ExecutionContext context, Method target, String cmdName) {
+ return null;
+ }
+
+ /**
+ * If applicable, get the coroutine context to use in suspend functions (Kotlin only).
+ * The return type is object to avoid depending on the kotlin runtime.
+ *
+ * @param context the context of execution
+ * @param target the method of the command
+ * @param cmdName the name of the command
+ * @return the coroutine context
+ */
+ default Object getCoroutineContext(ExecutionContext context, Method target, String cmdName) {
+ return null;
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ICommandReceiver.java b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ICommandReceiver.java
index bdcb568..d0681b1 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ICommandReceiver.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ICommandReceiver.java
@@ -1,5 +1,5 @@
-package io.dico.dicore.command.registration.reflect;
-
-public interface ICommandReceiver {
-
-}
+package io.dico.dicore.command.registration.reflect;
+
+public interface ICommandReceiver {
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveCommand.java b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveCommand.java
index 2d7c333..34ea8de 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveCommand.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveCommand.java
@@ -1,187 +1,187 @@
-package io.dico.dicore.command.registration.reflect;
-
-import io.dico.dicore.command.*;
-import io.dico.dicore.command.annotation.Cmd;
-import io.dico.dicore.command.annotation.GenerateCommands;
-import io.dico.dicore.command.parameter.type.IParameterTypeSelector;
-import org.bukkit.command.CommandSender;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-
-public final class ReflectiveCommand extends Command {
- private static final int continuationMask = 1 << 3;
- private final Cmd cmdAnnotation;
- private final Method method;
- private final Object instance;
- private String[] parameterOrder;
-
- // hasContinuation | hasContext | hasSender | hasReceiver
- private final int flags;
-
- ReflectiveCommand(IParameterTypeSelector selector, Method method, Object instance) throws CommandParseException {
- if (!method.isAnnotationPresent(Cmd.class)) {
- throw new CommandParseException("No @Cmd present for the method " + method.toGenericString());
- }
- cmdAnnotation = method.getAnnotation(Cmd.class);
-
- java.lang.reflect.Parameter[] parameters = method.getParameters();
-
- if (!method.isAccessible()) try {
- method.setAccessible(true);
- } catch (Exception ex) {
- throw new CommandParseException("Failed to make method accessible");
- }
-
- if (!Modifier.isStatic(method.getModifiers())) {
- if (instance == null) {
- try {
- instance = method.getDeclaringClass().newInstance();
- } catch (Exception ex) {
- throw new CommandParseException("No instance given for instance method, and failed to create new instance", ex);
- }
- } else if (!method.getDeclaringClass().isInstance(instance)) {
- throw new CommandParseException("Given instance is not an instance of the method's declaring class");
- }
- }
-
- this.method = method;
- this.instance = instance;
- this.flags = ReflectiveRegistration.parseCommandAttributes(selector, method, this, parameters);
- }
-
- public Method getMethod() {
- return method;
- }
-
- public Object getInstance() {
- return instance;
- }
-
- public String getCmdName() { return cmdAnnotation.value(); }
-
- void setParameterOrder(String[] parameterOrder) {
- this.parameterOrder = parameterOrder;
- }
-
- ICommandAddress getAddress() {
- ChildCommandAddress result = new ChildCommandAddress();
- result.setCommand(this);
-
- Cmd cmd = cmdAnnotation;
- result.getNames().add(cmd.value());
- for (String alias : cmd.aliases()) {
- result.getNames().add(alias);
- }
- result.finalizeNames();
-
- GenerateCommands generateCommands = method.getAnnotation(GenerateCommands.class);
- if (generateCommands != null) {
- ReflectiveRegistration.generateCommands(result, generateCommands.value());
- }
-
- return result;
- }
-
- @Override
- public String execute(CommandSender sender, ExecutionContext context) throws CommandException {
- String[] parameterOrder = this.parameterOrder;
- int extraArgumentCount = Integer.bitCount(flags);
- int parameterStartIndex = Integer.bitCount(flags & ~continuationMask);
-
- Object[] args = new Object[parameterOrder.length + extraArgumentCount];
-
- int i = 0;
-
- int mask = 1;
- if ((flags & mask) != 0) {
- // Has receiver
- try {
- args[i++] = ((ICommandInterceptor) instance).getReceiver(context, method, getCmdName());
- } catch (Exception ex) {
- handleException(ex);
- return null; // unreachable
- }
- }
-
- mask <<= 1;
- if ((flags & mask) != 0) {
- // Has sender
- args[i++] = sender;
- }
-
- mask <<= 1;
- if ((flags & mask) != 0) {
- // Has context
- args[i++] = context;
- }
-
- mask <<= 1;
- if ((flags & mask) != 0) {
- // Has continuation
-
- extraArgumentCount--;
- }
-
- for (int n = args.length; i < n; i++) {
- args[i] = context.get(parameterOrder[i - extraArgumentCount]);
- }
-
- if ((flags & mask) != 0) {
- // Since it has continuation, call as coroutine
- return callAsCoroutine(context, args);
- }
-
- return callSynchronously(args);
- }
-
- private boolean isSuspendFunction() {
- try {
- return KotlinReflectiveRegistrationKt.isSuspendFunction(method);
- } catch (Throwable ex) {
- return false;
- }
- }
-
- public String callSynchronously(Object[] args) throws CommandException {
- try {
- return getResult(method.invoke(instance, args), null);
- } catch (Exception ex) {
- return getResult(null, ex);
- }
- }
-
- public static String getResult(Object returned, Exception ex) throws CommandException {
- if (ex != null) {
- handleException(ex);
- return null; // unreachable
- }
-
- if (returned instanceof String) {
- return (String) returned;
- }
- return null;
- }
-
- public static void handleException(Exception ex) throws CommandException {
- if (ex instanceof InvocationTargetException) {
- if (ex.getCause() instanceof CommandException) {
- throw (CommandException) ex.getCause();
- }
-
- ex.printStackTrace();
- throw new CommandException("An internal error occurred while executing this command.", ex);
- }
- if (ex instanceof CommandException) {
- throw (CommandException) ex;
- }
- ex.printStackTrace();
- throw new CommandException("An internal error occurred while executing this command.", ex);
- }
-
- private String callAsCoroutine(ExecutionContext context, Object[] args) {
- return KotlinReflectiveRegistrationKt.callAsCoroutine(this, (ICommandInterceptor) instance, context, args);
- }
-
-}
+package io.dico.dicore.command.registration.reflect;
+
+import io.dico.dicore.command.*;
+import io.dico.dicore.command.annotation.Cmd;
+import io.dico.dicore.command.annotation.GenerateCommands;
+import io.dico.dicore.command.parameter.type.IParameterTypeSelector;
+import org.bukkit.command.CommandSender;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+public final class ReflectiveCommand extends Command {
+ private static final int continuationMask = 1 << 3;
+ private final Cmd cmdAnnotation;
+ private final Method method;
+ private final Object instance;
+ private String[] parameterOrder;
+
+ // hasContinuation | hasContext | hasSender | hasReceiver
+ private final int flags;
+
+ ReflectiveCommand(IParameterTypeSelector selector, Method method, Object instance) throws CommandParseException {
+ if (!method.isAnnotationPresent(Cmd.class)) {
+ throw new CommandParseException("No @Cmd present for the method " + method.toGenericString());
+ }
+ cmdAnnotation = method.getAnnotation(Cmd.class);
+
+ java.lang.reflect.Parameter[] parameters = method.getParameters();
+
+ if (!method.isAccessible()) try {
+ method.setAccessible(true);
+ } catch (Exception ex) {
+ throw new CommandParseException("Failed to make method accessible");
+ }
+
+ if (!Modifier.isStatic(method.getModifiers())) {
+ if (instance == null) {
+ try {
+ instance = method.getDeclaringClass().newInstance();
+ } catch (Exception ex) {
+ throw new CommandParseException("No instance given for instance method, and failed to create new instance", ex);
+ }
+ } else if (!method.getDeclaringClass().isInstance(instance)) {
+ throw new CommandParseException("Given instance is not an instance of the method's declaring class");
+ }
+ }
+
+ this.method = method;
+ this.instance = instance;
+ this.flags = ReflectiveRegistration.parseCommandAttributes(selector, method, this, parameters);
+ }
+
+ public Method getMethod() {
+ return method;
+ }
+
+ public Object getInstance() {
+ return instance;
+ }
+
+ public String getCmdName() { return cmdAnnotation.value(); }
+
+ void setParameterOrder(String[] parameterOrder) {
+ this.parameterOrder = parameterOrder;
+ }
+
+ ICommandAddress getAddress() {
+ ChildCommandAddress result = new ChildCommandAddress();
+ result.setCommand(this);
+
+ Cmd cmd = cmdAnnotation;
+ result.getNames().add(cmd.value());
+ for (String alias : cmd.aliases()) {
+ result.getNames().add(alias);
+ }
+ result.finalizeNames();
+
+ GenerateCommands generateCommands = method.getAnnotation(GenerateCommands.class);
+ if (generateCommands != null) {
+ ReflectiveRegistration.generateCommands(result, generateCommands.value());
+ }
+
+ return result;
+ }
+
+ @Override
+ public String execute(CommandSender sender, ExecutionContext context) throws CommandException {
+ String[] parameterOrder = this.parameterOrder;
+ int extraArgumentCount = Integer.bitCount(flags);
+ int parameterStartIndex = Integer.bitCount(flags & ~continuationMask);
+
+ Object[] args = new Object[parameterOrder.length + extraArgumentCount];
+
+ int i = 0;
+
+ int mask = 1;
+ if ((flags & mask) != 0) {
+ // Has receiver
+ try {
+ args[i++] = ((ICommandInterceptor) instance).getReceiver(context, method, getCmdName());
+ } catch (Exception ex) {
+ handleException(ex);
+ return null; // unreachable
+ }
+ }
+
+ mask <<= 1;
+ if ((flags & mask) != 0) {
+ // Has sender
+ args[i++] = sender;
+ }
+
+ mask <<= 1;
+ if ((flags & mask) != 0) {
+ // Has context
+ args[i++] = context;
+ }
+
+ mask <<= 1;
+ if ((flags & mask) != 0) {
+ // Has continuation
+
+ extraArgumentCount--;
+ }
+
+ for (int n = args.length; i < n; i++) {
+ args[i] = context.get(parameterOrder[i - extraArgumentCount]);
+ }
+
+ if ((flags & mask) != 0) {
+ // Since it has continuation, call as coroutine
+ return callAsCoroutine(context, args);
+ }
+
+ return callSynchronously(args);
+ }
+
+ private boolean isSuspendFunction() {
+ try {
+ return KotlinReflectiveRegistrationKt.isSuspendFunction(method);
+ } catch (Throwable ex) {
+ return false;
+ }
+ }
+
+ public String callSynchronously(Object[] args) throws CommandException {
+ try {
+ return getResult(method.invoke(instance, args), null);
+ } catch (Exception ex) {
+ return getResult(null, ex);
+ }
+ }
+
+ public static String getResult(Object returned, Exception ex) throws CommandException {
+ if (ex != null) {
+ handleException(ex);
+ return null; // unreachable
+ }
+
+ if (returned instanceof String) {
+ return (String) returned;
+ }
+ return null;
+ }
+
+ public static void handleException(Exception ex) throws CommandException {
+ if (ex instanceof InvocationTargetException) {
+ if (ex.getCause() instanceof CommandException) {
+ throw (CommandException) ex.getCause();
+ }
+
+ ex.printStackTrace();
+ throw new CommandException("An internal error occurred while executing this command.", ex);
+ }
+ if (ex instanceof CommandException) {
+ throw (CommandException) ex;
+ }
+ ex.printStackTrace();
+ throw new CommandException("An internal error occurred while executing this command.", ex);
+ }
+
+ private String callAsCoroutine(ExecutionContext context, Object[] args) {
+ return KotlinReflectiveRegistrationKt.callAsCoroutine(this, (ICommandInterceptor) instance, context, args);
+ }
+
+}
diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveRegistration.java b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveRegistration.java
index 6b1965d..93ac0ee 100644
--- a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveRegistration.java
+++ b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveRegistration.java
@@ -1,415 +1,415 @@
-package io.dico.dicore.command.registration.reflect;
-
-import io.dico.dicore.command.*;
-import io.dico.dicore.command.annotation.*;
-import io.dico.dicore.command.annotation.GroupMatchedCommands.GroupEntry;
-import io.dico.dicore.command.parameter.Parameter;
-import io.dico.dicore.command.parameter.ParameterList;
-import io.dico.dicore.command.parameter.type.IParameterTypeSelector;
-import io.dico.dicore.command.parameter.type.MapBasedParameterTypeSelector;
-import io.dico.dicore.command.parameter.type.ParameterType;
-import io.dico.dicore.command.parameter.type.ParameterTypes;
-import io.dico.dicore.command.predef.PredefinedCommand;
-import org.bukkit.command.CommandSender;
-import org.bukkit.command.ConsoleCommandSender;
-import org.bukkit.entity.Player;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.*;
-import java.util.function.Consumer;
-import java.util.regex.Pattern;
-import java.util.regex.PatternSyntaxException;
-
-/**
- * Takes care of turning a reflection {@link Method} into a command and more.
- */
-public class ReflectiveRegistration {
- /**
- * This object provides names of the parameters.
- * Oddly, the AnnotationParanamer extensions require a 'fallback' paranamer to function properly without
- * requiring ALL parameters to have that flag. This is weird because it should just use the AdaptiveParanamer on an upper level to
- * determine the name of each individual flag. Oddly this isn't how it works, so the fallback works the same way as the AdaptiveParanamer does.
- * It's just linked instead of using an array for that part. Then we can use an AdaptiveParanamer for the latest fallback, to get bytecode names
- * or, finally, to get the Jvm-provided parameter names.
- */
- //private static final Paranamer paranamer = new CachingParanamer(new BytecodeReadingParanamer());
- @SuppressWarnings("StatementWithEmptyBody")
- private static String[] lookupParameterNames(Method method, java.lang.reflect.Parameter[] parameters, int start) {
- int n = parameters.length;
- String[] out = new String[n - start];
-
- //String[] bytecode;
- //try {
- // bytecode = paranamer.lookupParameterNames(method, false);
- //} catch (Exception ex) {
- // bytecode = new String[0];
- // System.err.println("ReflectiveRegistration.lookupParameterNames failed to read bytecode");
- // //ex.printStackTrace();
- //}
- //int bn = bytecode.length;
-
- for (int i = start; i < n; i++) {
- java.lang.reflect.Parameter parameter = parameters[i];
- Flag flag = parameter.getAnnotation(Flag.class);
- NamedArg namedArg = parameter.getAnnotation(NamedArg.class);
-
- boolean isFlag = flag != null;
- String name;
- if (namedArg != null && !(name = namedArg.value()).isEmpty()) {
- } else if (isFlag && !(name = flag.value()).isEmpty()) {
- //} else if (i < bn && (name = bytecode[i]) != null && !name.isEmpty()) {
- } else {
- name = parameter.getName();
- }
-
- if (isFlag) {
- name = '-' + name;
- } else {
- int idx = 0;
- while (name.startsWith("-", idx)) {
- idx++;
- }
- name = name.substring(idx);
- }
-
- out[i - start] = name;
- }
-
- return out;
- }
-
- public static void parseCommandGroup(ICommandAddress address, Class<?> clazz, Object instance) throws CommandParseException {
- parseCommandGroup(address, ParameterTypes.getSelector(), clazz, instance);
- }
-
- public static void parseCommandGroup(ICommandAddress address, IParameterTypeSelector selector, Class<?> clazz, Object instance) throws CommandParseException {
- boolean requireStatic = instance == null;
- if (!requireStatic && !clazz.isInstance(instance)) {
- throw new CommandParseException();
- }
-
- List<Method> methods = new LinkedList<>(Arrays.asList(clazz.getDeclaredMethods()));
-
- Iterator<Method> it = methods.iterator();
- for (Method method; it.hasNext(); ) {
- method = it.next();
-
- if (requireStatic && !Modifier.isStatic(method.getModifiers())) {
- it.remove();
- continue;
- }
-
- if (method.isAnnotationPresent(CmdParamType.class)) {
- it.remove();
-
- if (method.getReturnType() != ParameterType.class || method.getParameterCount() != 0) {
- throw new CommandParseException("Invalid CmdParamType method: must return ParameterType and take no arguments");
- }
-
- ParameterType<?, ?> type;
- try {
- Object inst = Modifier.isStatic(method.getModifiers()) ? null : instance;
- type = (ParameterType<?, ?>) method.invoke(inst);
- Objects.requireNonNull(type, "ParameterType returned is null");
- } catch (Exception ex) {
- throw new CommandParseException("Error occurred whilst getting ParameterType from CmdParamType method '" + method.toGenericString() + "'", ex);
- }
-
- if (selector == ParameterTypes.getSelector()) {
- selector = new MapBasedParameterTypeSelector(true);
- }
-
- selector.addType(method.getAnnotation(CmdParamType.class).infolessAlias(), type);
- }
- }
-
- GroupMatcherCache groupMatcherCache = new GroupMatcherCache(clazz, address);
- for (Method method : methods) {
- if (method.isAnnotationPresent(Cmd.class)) {
- ICommandAddress parsed = parseCommandMethod(selector, method, instance);
- groupMatcherCache.getGroupFor(method).addChild(parsed);
- }
- }
-
- }
-
- private static final class GroupMatcherCache {
- private ModifiableCommandAddress groupRootAddress;
- private GroupEntry[] matchEntries;
- private Pattern[] patterns;
- private ModifiableCommandAddress[] addresses;
-
- GroupMatcherCache(Class<?> clazz, ICommandAddress groupRootAddress) throws CommandParseException {
- this.groupRootAddress = (ModifiableCommandAddress) groupRootAddress;
-
- GroupMatchedCommands groupMatchedCommands = clazz.getAnnotation(GroupMatchedCommands.class);
- GroupEntry[] matchEntries = groupMatchedCommands == null ? new GroupEntry[0] : groupMatchedCommands.value();
-
- Pattern[] patterns = new Pattern[matchEntries.length];
- for (int i = 0; i < matchEntries.length; i++) {
- GroupEntry matchEntry = matchEntries[i];
- if (matchEntry.group().isEmpty() || matchEntry.regex().isEmpty()) {
- throw new CommandParseException("Empty group or regex in GroupMatchedCommands entry");
- }
- try {
- patterns[i] = Pattern.compile(matchEntry.regex());
- } catch (PatternSyntaxException ex) {
- throw new CommandParseException(ex);
- }
- }
-
- this.matchEntries = matchEntries;
- this.patterns = patterns;
- this.addresses = new ModifiableCommandAddress[this.matchEntries.length];
- }
-
- ModifiableCommandAddress getGroupFor(Method method) {
- String name = method.getName();
-
- GroupEntry[] matchEntries = this.matchEntries;
- Pattern[] patterns = this.patterns;
- ModifiableCommandAddress[] addresses = this.addresses;
-
- for (int i = 0; i < matchEntries.length; i++) {
- GroupEntry matchEntry = matchEntries[i];
- if (patterns[i].matcher(name).matches()) {
- if (addresses[i] == null) {
- ChildCommandAddress placeholder = new ChildCommandAddress();
- placeholder.setupAsPlaceholder(matchEntry.group(), matchEntry.groupAliases());
- addresses[i] = placeholder;
- groupRootAddress.addChild(placeholder);
- generateCommands(placeholder, matchEntry.generatedCommands());
- setDescription(placeholder, matchEntry.description(), matchEntry.shortDescription());
- }
- return addresses[i];
- }
- }
-
- return groupRootAddress;
- }
-
- }
-
- public static ICommandAddress parseCommandMethod(IParameterTypeSelector selector, Method method, Object instance) throws CommandParseException {
- return new ReflectiveCommand(selector, method, instance).getAddress();
- }
-
- static int parseCommandAttributes(IParameterTypeSelector selector, Method method, ReflectiveCommand command, java.lang.reflect.Parameter[] parameters) throws CommandParseException {
- ParameterList list = command.getParameterList();
-
- boolean hasReceiverParameter = false;
- boolean hasSenderParameter = false;
- boolean hasContextParameter = false;
- boolean hasContinuationParameter = false;
-
- int start = 0;
- int end = parameters.length;
-
- Class<?> senderParameterType = null;
-
- if (parameters.length > start
- && command.getInstance() instanceof ICommandInterceptor
- && ICommandReceiver.class.isAssignableFrom(parameters[start].getType())) {
- hasReceiverParameter = true;
- start++;
- }
-
- if (parameters.length > start && CommandSender.class.isAssignableFrom(senderParameterType = parameters[start].getType())) {
- hasSenderParameter = true;
- start++;
- }
-
- if (parameters.length > start && parameters[start].getType() == ExecutionContext.class) {
- hasContextParameter = true;
- start++;
- }
-
- if (parameters.length > start && parameters[end - 1].getType().getName().equals("kotlin.coroutines.Continuation")) {
- hasContinuationParameter = true;
- end--;
- }
-
- String[] parameterNames = lookupParameterNames(method, parameters, start);
- for (int i = start, n = end; i < n; i++) {
- Parameter<?, ?> parameter = parseParameter(selector, method, parameters[i], parameterNames[i - start]);
- list.addParameter(parameter);
- }
- command.setParameterOrder(hasContinuationParameter ? Arrays.copyOfRange(parameterNames, 0, parameterNames.length - 1) : parameterNames);
-
- RequirePermissions cmdPermissions = method.getAnnotation(RequirePermissions.class);
- if (cmdPermissions != null) {
- for (String permission : cmdPermissions.value()) {
- command.addContextFilter(IContextFilter.permission(permission));
- }
-
- if (cmdPermissions.inherit()) {
- command.addContextFilter(IContextFilter.INHERIT_PERMISSIONS);
- }
- } else {
- command.addContextFilter(IContextFilter.INHERIT_PERMISSIONS);
- }
-
- RequireParameters reqPar = method.getAnnotation(RequireParameters.class);
- if (reqPar != null) {
- list.setRequiredCount(reqPar.value() < 0 ? Integer.MAX_VALUE : reqPar.value());
- } else {
- list.setRequiredCount(list.getIndexedParameters().size());
- }
-
- /*
- PreprocessArgs preprocessArgs = method.getAnnotation(PreprocessArgs.class);
- if (preprocessArgs != null) {
- IArgumentPreProcessor preProcessor = IArgumentPreProcessor.mergeOnTokens(preprocessArgs.tokens(), preprocessArgs.escapeChar());
- list.setArgumentPreProcessor(preProcessor);
- }*/
-
- Desc desc = method.getAnnotation(Desc.class);
- if (desc != null) {
- String[] array = desc.value();
- if (array.length == 0) {
- command.setDescription(desc.shortVersion());
- } else {
- command.setDescription(array);
- }
- } else {
- command.setDescription();
- }
-
- if (hasSenderParameter && Player.class.isAssignableFrom(senderParameterType)) {
- command.addContextFilter(IContextFilter.PLAYER_ONLY);
- } else if (hasSenderParameter && ConsoleCommandSender.class.isAssignableFrom(senderParameterType)) {
- command.addContextFilter(IContextFilter.CONSOLE_ONLY);
- } else if (method.isAnnotationPresent(RequirePlayer.class)) {
- command.addContextFilter(IContextFilter.PLAYER_ONLY);
- } else if (method.isAnnotationPresent(RequireConsole.class)) {
- command.addContextFilter(IContextFilter.CONSOLE_ONLY);
- }
-
- list.setRepeatFinalParameter(parameters.length > start && parameters[parameters.length - 1].isVarArgs());
- list.setFinalParameterMayBeFlag(true);
-
- int flags = 0;
- if (hasContinuationParameter) flags |= 1;
- flags <<= 1;
- if (hasContextParameter) flags |= 1;
- flags <<= 1;
- if (hasSenderParameter) flags |= 1;
- flags <<= 1;
- if (hasReceiverParameter) flags |= 1;
- return flags;
- }
-
- public static int parseCommandAttributes(IParameterTypeSelector selector, Method method, ReflectiveCommand command) throws CommandParseException {
- return parseCommandAttributes(selector, method, command, method.getParameters());
- }
-
- public static Parameter<?, ?> parseParameter(IParameterTypeSelector selector, Method method, java.lang.reflect.Parameter parameter, String name) throws CommandParseException {
- Class<?> type = parameter.getType();
- if (parameter.isVarArgs()) {
- type = type.getComponentType();
- }
-
- Annotation[] annotations = parameter.getAnnotations();
- Flag flag = null;
- Annotation typeAnnotation = null;
- Desc desc = null;
-
- for (Annotation annotation : annotations) {
- //noinspection StatementWithEmptyBody
- if (annotation instanceof NamedArg) {
- // do nothing
- } else if (annotation instanceof Flag) {
- if (flag != null) {
- throw new CommandParseException("Multiple flags for the same parameter");
- }
- flag = (Flag) annotation;
- } else if (annotation instanceof Desc) {
- if (desc != null) {
- throw new CommandParseException("Multiple descriptions for the same parameter");
- }
- desc = (Desc) annotation;
- } else {
- if (typeAnnotation != null) {
- throw new CommandParseException("Multiple parameter type annotations for the same parameter");
- }
- typeAnnotation = annotation;
- }
- }
-
- if (flag == null && name.startsWith("-")) {
- throw new CommandParseException("Non-flag parameter's name starts with -");
- } else if (flag != null && !name.startsWith("-")) {
- throw new CommandParseException("Flag parameter's name doesn't start with -");
- }
-
- ParameterType<Object, Object> parameterType = selector.selectAny(type, typeAnnotation == null ? null : typeAnnotation.getClass());
- if (parameterType == null) {
- throw new CommandParseException("IParameter type not found for parameter " + name + " in method " + method.toString());
- }
-
- Object parameterInfo;
- if (typeAnnotation == null) {
- parameterInfo = null;
- } else try {
- parameterInfo = parameterType.getParameterConfig() == null ? null : parameterType.getParameterConfig().getParameterInfo(typeAnnotation);
- } catch (Exception ex) {
- throw new CommandParseException("Invalid parameter config", ex);
- }
-
- String descString = desc == null ? null : CommandAnnotationUtils.getShortDescription(desc);
-
- try {
- //noinspection unchecked
- String flagPermission = flag == null || flag.permission().isEmpty() ? null : flag.permission();
- return new Parameter<>(name, descString, parameterType, parameterInfo, type.isPrimitive(), name.startsWith("-"), flagPermission);
- } catch (Exception ex) {
- throw new CommandParseException("Invalid parameter", ex);
- }
- }
-
- public static void generateCommands(ICommandAddress address, String[] input) {
- for (String value : input) {
- Consumer<ICommandAddress> consumer = PredefinedCommand.getPredefinedCommandGenerator(value);
- if (consumer == null) {
- System.out.println("[Command Warning] generated command '" + value + "' could not be found");
- } else {
- consumer.accept(address);
- }
- }
- }
-
- /*
- Desired format
-
- @Cmd({"tp", "tpto"})
- @RequirePermissions("teleport.self")
- public (static) String|void onCommand(Player sender, Player target, @Flag("force", permission = "teleport.self.force") boolean force) {
- Validate.isTrue(force || !hasTpToggledOff(target), "Target has teleportation disabled. Use -force to ignore");
- sender.teleport(target);
- //return
- }
-
- parser needs to:
- - see the @Cmd and create a CommandTree for it
- - see that it must be a Player executing the command
- - add an indexed IParameter for a Player type
- - add a flag parameter named force, that consumes no arguments.
- - see that setting the force flag requires a permission
- */
-
- private static void setDescription(ICommandAddress address, String[] array, String shortVersion) {
- if (!address.hasCommand()) {
- return;
- }
-
- if (array.length == 0) {
- address.getCommand().setDescription(shortVersion);
- } else {
- address.getCommand().setDescription(array);
- }
-
- }
-
-}
+package io.dico.dicore.command.registration.reflect;
+
+import io.dico.dicore.command.*;
+import io.dico.dicore.command.annotation.*;
+import io.dico.dicore.command.annotation.GroupMatchedCommands.GroupEntry;
+import io.dico.dicore.command.parameter.Parameter;
+import io.dico.dicore.command.parameter.ParameterList;
+import io.dico.dicore.command.parameter.type.IParameterTypeSelector;
+import io.dico.dicore.command.parameter.type.MapBasedParameterTypeSelector;
+import io.dico.dicore.command.parameter.type.ParameterType;
+import io.dico.dicore.command.parameter.type.ParameterTypes;
+import io.dico.dicore.command.predef.PredefinedCommand;
+import org.bukkit.command.CommandSender;
+import org.bukkit.command.ConsoleCommandSender;
+import org.bukkit.entity.Player;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.*;
+import java.util.function.Consumer;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+/**
+ * Takes care of turning a reflection {@link Method} into a command and more.
+ */
+public class ReflectiveRegistration {
+ /**
+ * This object provides names of the parameters.
+ * Oddly, the AnnotationParanamer extensions require a 'fallback' paranamer to function properly without
+ * requiring ALL parameters to have that flag. This is weird because it should just use the AdaptiveParanamer on an upper level to
+ * determine the name of each individual flag. Oddly this isn't how it works, so the fallback works the same way as the AdaptiveParanamer does.
+ * It's just linked instead of using an array for that part. Then we can use an AdaptiveParanamer for the latest fallback, to get bytecode names
+ * or, finally, to get the Jvm-provided parameter names.
+ */
+ //private static final Paranamer paranamer = new CachingParanamer(new BytecodeReadingParanamer());
+ @SuppressWarnings("StatementWithEmptyBody")
+ private static String[] lookupParameterNames(Method method, java.lang.reflect.Parameter[] parameters, int start) {
+ int n = parameters.length;
+ String[] out = new String[n - start];
+
+ //String[] bytecode;
+ //try {
+ // bytecode = paranamer.lookupParameterNames(method, false);
+ //} catch (Exception ex) {
+ // bytecode = new String[0];
+ // System.err.println("ReflectiveRegistration.lookupParameterNames failed to read bytecode");
+ // //ex.printStackTrace();
+ //}
+ //int bn = bytecode.length;
+
+ for (int i = start; i < n; i++) {
+ java.lang.reflect.Parameter parameter = parameters[i];
+ Flag flag = parameter.getAnnotation(Flag.class);
+ NamedArg namedArg = parameter.getAnnotation(NamedArg.class);
+
+ boolean isFlag = flag != null;
+ String name;
+ if (namedArg != null && !(name = namedArg.value()).isEmpty()) {
+ } else if (isFlag && !(name = flag.value()).isEmpty()) {
+ //} else if (i < bn && (name = bytecode[i]) != null && !name.isEmpty()) {
+ } else {
+ name = parameter.getName();
+ }
+
+ if (isFlag) {
+ name = '-' + name;
+ } else {
+ int idx = 0;
+ while (name.startsWith("-", idx)) {
+ idx++;
+ }
+ name = name.substring(idx);
+ }
+
+ out[i - start] = name;
+ }
+
+ return out;
+ }
+
+ public static void parseCommandGroup(ICommandAddress address, Class<?> clazz, Object instance) throws CommandParseException {
+ parseCommandGroup(address, ParameterTypes.getSelector(), clazz, instance);
+ }
+
+ public static void parseCommandGroup(ICommandAddress address, IParameterTypeSelector selector, Class<?> clazz, Object instance) throws CommandParseException {
+ boolean requireStatic = instance == null;
+ if (!requireStatic && !clazz.isInstance(instance)) {
+ throw new CommandParseException();
+ }
+
+ List<Method> methods = new LinkedList<>(Arrays.asList(clazz.getDeclaredMethods()));
+
+ Iterator<Method> it = methods.iterator();
+ for (Method method; it.hasNext(); ) {
+ method = it.next();
+
+ if (requireStatic && !Modifier.isStatic(method.getModifiers())) {
+ it.remove();
+ continue;
+ }
+
+ if (method.isAnnotationPresent(CmdParamType.class)) {
+ it.remove();
+
+ if (method.getReturnType() != ParameterType.class || method.getParameterCount() != 0) {
+ throw new CommandParseException("Invalid CmdParamType method: must return ParameterType and take no arguments");
+ }
+
+ ParameterType<?, ?> type;
+ try {
+ Object inst = Modifier.isStatic(method.getModifiers()) ? null : instance;
+ type = (ParameterType<?, ?>) method.invoke(inst);
+ Objects.requireNonNull(type, "ParameterType returned is null");
+ } catch (Exception ex) {
+ throw new CommandParseException("Error occurred whilst getting ParameterType from CmdParamType method '" + method.toGenericString() + "'", ex);
+ }
+
+ if (selector == ParameterTypes.getSelector()) {
+ selector = new MapBasedParameterTypeSelector(true);
+ }
+
+ selector.addType(method.getAnnotation(CmdParamType.class).infolessAlias(), type);
+ }
+ }
+
+ GroupMatcherCache groupMatcherCache = new GroupMatcherCache(clazz, address);
+ for (Method method : methods) {
+ if (method.isAnnotationPresent(Cmd.class)) {
+ ICommandAddress parsed = parseCommandMethod(selector, method, instance);
+ groupMatcherCache.getGroupFor(method).addChild(parsed);
+ }
+ }
+
+ }
+
+ private static final class GroupMatcherCache {
+ private ModifiableCommandAddress groupRootAddress;
+ private GroupEntry[] matchEntries;
+ private Pattern[] patterns;
+ private ModifiableCommandAddress[] addresses;
+
+ GroupMatcherCache(Class<?> clazz, ICommandAddress groupRootAddress) throws CommandParseException {
+ this.groupRootAddress = (ModifiableCommandAddress) groupRootAddress;
+
+ GroupMatchedCommands groupMatchedCommands = clazz.getAnnotation(GroupMatchedCommands.class);
+ GroupEntry[] matchEntries = groupMatchedCommands == null ? new GroupEntry[0] : groupMatchedCommands.value();
+
+ Pattern[] patterns = new Pattern[matchEntries.length];
+ for (int i = 0; i < matchEntries.length; i++) {
+ GroupEntry matchEntry = matchEntries[i];
+ if (matchEntry.group().isEmpty() || matchEntry.regex().isEmpty()) {
+ throw new CommandParseException("Empty group or regex in GroupMatchedCommands entry");
+ }
+ try {
+ patterns[i] = Pattern.compile(matchEntry.regex());
+ } catch (PatternSyntaxException ex) {
+ throw new CommandParseException(ex);
+ }
+ }
+
+ this.matchEntries = matchEntries;
+ this.patterns = patterns;
+ this.addresses = new ModifiableCommandAddress[this.matchEntries.length];
+ }
+
+ ModifiableCommandAddress getGroupFor(Method method) {
+ String name = method.getName();
+
+ GroupEntry[] matchEntries = this.matchEntries;
+ Pattern[] patterns = this.patterns;
+ ModifiableCommandAddress[] addresses = this.addresses;
+
+ for (int i = 0; i < matchEntries.length; i++) {
+ GroupEntry matchEntry = matchEntries[i];
+ if (patterns[i].matcher(name).matches()) {
+ if (addresses[i] == null) {
+ ChildCommandAddress placeholder = new ChildCommandAddress();
+ placeholder.setupAsPlaceholder(matchEntry.group(), matchEntry.groupAliases());
+ addresses[i] = placeholder;
+ groupRootAddress.addChild(placeholder);
+ generateCommands(placeholder, matchEntry.generatedCommands());
+ setDescription(placeholder, matchEntry.description(), matchEntry.shortDescription());
+ }
+ return addresses[i];
+ }
+ }
+
+ return groupRootAddress;
+ }
+
+ }
+
+ public static ICommandAddress parseCommandMethod(IParameterTypeSelector selector, Method method, Object instance) throws CommandParseException {
+ return new ReflectiveCommand(selector, method, instance).getAddress();
+ }
+
+ static int parseCommandAttributes(IParameterTypeSelector selector, Method method, ReflectiveCommand command, java.lang.reflect.Parameter[] parameters) throws CommandParseException {
+ ParameterList list = command.getParameterList();
+
+ boolean hasReceiverParameter = false;
+ boolean hasSenderParameter = false;
+ boolean hasContextParameter = false;
+ boolean hasContinuationParameter = false;
+
+ int start = 0;
+ int end = parameters.length;
+
+ Class<?> senderParameterType = null;
+
+ if (parameters.length > start
+ && command.getInstance() instanceof ICommandInterceptor
+ && ICommandReceiver.class.isAssignableFrom(parameters[start].getType())) {
+ hasReceiverParameter = true;
+ start++;
+ }
+
+ if (parameters.length > start && CommandSender.class.isAssignableFrom(senderParameterType = parameters[start].getType())) {
+ hasSenderParameter = true;
+ start++;
+ }
+
+ if (parameters.length > start && parameters[start].getType() == ExecutionContext.class) {
+ hasContextParameter = true;
+ start++;
+ }
+
+ if (parameters.length > start && parameters[end - 1].getType().getName().equals("kotlin.coroutines.Continuation")) {
+ hasContinuationParameter = true;
+ end--;
+ }
+
+ String[] parameterNames = lookupParameterNames(method, parameters, start);
+ for (int i = start, n = end; i < n; i++) {
+ Parameter<?, ?> parameter = parseParameter(selector, method, parameters[i], parameterNames[i - start]);
+ list.addParameter(parameter);
+ }
+ command.setParameterOrder(hasContinuationParameter ? Arrays.copyOfRange(parameterNames, 0, parameterNames.length - 1) : parameterNames);
+
+ RequirePermissions cmdPermissions = method.getAnnotation(RequirePermissions.class);
+ if (cmdPermissions != null) {
+ for (String permission : cmdPermissions.value()) {
+ command.addContextFilter(IContextFilter.permission(permission));
+ }
+
+ if (cmdPermissions.inherit()) {
+ command.addContextFilter(IContextFilter.INHERIT_PERMISSIONS);
+ }
+ } else {
+ command.addContextFilter(IContextFilter.INHERIT_PERMISSIONS);
+ }
+
+ RequireParameters reqPar = method.getAnnotation(RequireParameters.class);
+ if (reqPar != null) {
+ list.setRequiredCount(reqPar.value() < 0 ? Integer.MAX_VALUE : reqPar.value());
+ } else {
+ list.setRequiredCount(list.getIndexedParameters().size());
+ }
+
+ /*
+ PreprocessArgs preprocessArgs = method.getAnnotation(PreprocessArgs.class);
+ if (preprocessArgs != null) {
+ IArgumentPreProcessor preProcessor = IArgumentPreProcessor.mergeOnTokens(preprocessArgs.tokens(), preprocessArgs.escapeChar());
+ list.setArgumentPreProcessor(preProcessor);
+ }*/
+
+ Desc desc = method.getAnnotation(Desc.class);
+ if (desc != null) {
+ String[] array = desc.value();
+ if (array.length == 0) {
+ command.setDescription(desc.shortVersion());
+ } else {
+ command.setDescription(array);
+ }
+ } else {
+ command.setDescription();
+ }
+
+ if (hasSenderParameter && Player.class.isAssignableFrom(senderParameterType)) {
+ command.addContextFilter(IContextFilter.PLAYER_ONLY);
+ } else if (hasSenderParameter && ConsoleCommandSender.class.isAssignableFrom(senderParameterType)) {
+ command.addContextFilter(IContextFilter.CONSOLE_ONLY);
+ } else if (method.isAnnotationPresent(RequirePlayer.class)) {
+ command.addContextFilter(IContextFilter.PLAYER_ONLY);
+ } else if (method.isAnnotationPresent(RequireConsole.class)) {
+ command.addContextFilter(IContextFilter.CONSOLE_ONLY);
+ }
+
+ list.setRepeatFinalParameter(parameters.length > start && parameters[parameters.length - 1].isVarArgs());
+ list.setFinalParameterMayBeFlag(true);
+
+ int flags = 0;
+ if (hasContinuationParameter) flags |= 1;
+ flags <<= 1;
+ if (hasContextParameter) flags |= 1;
+ flags <<= 1;
+ if (hasSenderParameter) flags |= 1;
+ flags <<= 1;
+ if (hasReceiverParameter) flags |= 1;
+ return flags;
+ }
+
+ public static int parseCommandAttributes(IParameterTypeSelector selector, Method method, ReflectiveCommand command) throws CommandParseException {
+ return parseCommandAttributes(selector, method, command, method.getParameters());
+ }
+
+ public static Parameter<?, ?> parseParameter(IParameterTypeSelector selector, Method method, java.lang.reflect.Parameter parameter, String name) throws CommandParseException {
+ Class<?> type = parameter.getType();
+ if (parameter.isVarArgs()) {
+ type = type.getComponentType();
+ }
+
+ Annotation[] annotations = parameter.getAnnotations();
+ Flag flag = null;
+ Annotation typeAnnotation = null;
+ Desc desc = null;
+
+ for (Annotation annotation : annotations) {
+ //noinspection StatementWithEmptyBody
+ if (annotation instanceof NamedArg) {
+ // do nothing
+ } else if (annotation instanceof Flag) {
+ if (flag != null) {
+ throw new CommandParseException("Multiple flags for the same parameter");
+ }
+ flag = (Flag) annotation;
+ } else if (annotation instanceof Desc) {
+ if (desc != null) {
+ throw new CommandParseException("Multiple descriptions for the same parameter");
+ }
+ desc = (Desc) annotation;
+ } else {
+ if (typeAnnotation != null) {
+ throw new CommandParseException("Multiple parameter type annotations for the same parameter");
+ }
+ typeAnnotation = annotation;
+ }
+ }
+
+ if (flag == null && name.startsWith("-")) {
+ throw new CommandParseException("Non-flag parameter's name starts with -");
+ } else if (flag != null && !name.startsWith("-")) {
+ throw new CommandParseException("Flag parameter's name doesn't start with -");
+ }
+
+ ParameterType<Object, Object> parameterType = selector.selectAny(type, typeAnnotation == null ? null : typeAnnotation.getClass());
+ if (parameterType == null) {
+ throw new CommandParseException("IParameter type not found for parameter " + name + " in method " + method.toString());
+ }
+
+ Object parameterInfo;
+ if (typeAnnotation == null) {
+ parameterInfo = null;
+ } else try {
+ parameterInfo = parameterType.getParameterConfig() == null ? null : parameterType.getParameterConfig().getParameterInfo(typeAnnotation);
+ } catch (Exception ex) {
+ throw new CommandParseException("Invalid parameter config", ex);
+ }
+
+ String descString = desc == null ? null : CommandAnnotationUtils.getShortDescription(desc);
+
+ try {
+ //noinspection unchecked
+ String flagPermission = flag == null || flag.permission().isEmpty() ? null : flag.permission();
+ return new Parameter<>(name, descString, parameterType, parameterInfo, type.isPrimitive(), name.startsWith("-"), flagPermission);
+ } catch (Exception ex) {
+ throw new CommandParseException("Invalid parameter", ex);
+ }
+ }
+
+ public static void generateCommands(ICommandAddress address, String[] input) {
+ for (String value : input) {
+ Consumer<ICommandAddress> consumer = PredefinedCommand.getPredefinedCommandGenerator(value);
+ if (consumer == null) {
+ System.out.println("[Command Warning] generated command '" + value + "' could not be found");
+ } else {
+ consumer.accept(address);
+ }
+ }
+ }
+
+ /*
+ Desired format
+
+ @Cmd({"tp", "tpto"})
+ @RequirePermissions("teleport.self")
+ public (static) String|void onCommand(Player sender, Player target, @Flag("force", permission = "teleport.self.force") boolean force) {
+ Validate.isTrue(force || !hasTpToggledOff(target), "Target has teleportation disabled. Use -force to ignore");
+ sender.teleport(target);
+ //return
+ }
+
+ parser needs to:
+ - see the @Cmd and create a CommandTree for it
+ - see that it must be a Player executing the command
+ - add an indexed IParameter for a Player type
+ - add a flag parameter named force, that consumes no arguments.
+ - see that setting the force flag requires a permission
+ */
+
+ private static void setDescription(ICommandAddress address, String[] array, String shortVersion) {
+ if (!address.hasCommand()) {
+ return;
+ }
+
+ if (array.length == 0) {
+ address.getCommand().setDescription(shortVersion);
+ } else {
+ address.getCommand().setDescription(array);
+ }
+
+ }
+
+}
diff --git a/dicore3/command/src/main/kotlin/io/dico/dicore/command/registration/reflect/KotlinReflectiveRegistration.kt b/dicore3/command/src/main/kotlin/io/dico/dicore/command/registration/reflect/KotlinReflectiveRegistration.kt
index 2ef6e39..1a6692b 100644
--- a/dicore3/command/src/main/kotlin/io/dico/dicore/command/registration/reflect/KotlinReflectiveRegistration.kt
+++ b/dicore3/command/src/main/kotlin/io/dico/dicore/command/registration/reflect/KotlinReflectiveRegistration.kt
@@ -1,66 +1,66 @@
-package io.dico.dicore.command.registration.reflect
-
-import io.dico.dicore.command.*
-import kotlinx.coroutines.CoroutineStart.UNDISPATCHED
-import kotlinx.coroutines.Deferred
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.async
-import java.lang.reflect.Method
-import java.util.concurrent.CancellationException
-import kotlin.coroutines.CoroutineContext
-import kotlin.coroutines.intrinsics.intercepted
-import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn
-import kotlin.reflect.jvm.kotlinFunction
-
-fun isSuspendFunction(method: Method): Boolean {
- val func = method.kotlinFunction ?: return false
- return func.isSuspend
-}
-
-fun callAsCoroutine(
- command: ReflectiveCommand,
- factory: ICommandInterceptor,
- context: ExecutionContext,
- args: Array<Any?>
-): String? {
- val coroutineContext = factory.getCoroutineContext(context, command.method, command.cmdName) as CoroutineContext
-
- // UNDISPATCHED causes the handler to run until the first suspension point on the current thread,
- // meaning command handlers that don't have suspension points will run completely synchronously.
- // Tasks that take time to compute should suspend the coroutine and resume on another thread.
- val job = GlobalScope.async(context = coroutineContext, start = UNDISPATCHED) {
- suspendCoroutineUninterceptedOrReturn<Any?> { cont ->
- command.method.invoke(command.instance, *args, cont.intercepted())
- }
- }
-
- if (job.isCompleted) {
- return job.getResult()
- }
-
- job.invokeOnCompletion {
- val chatHandler = context.address.chatHandler
- try {
- val result = job.getResult()
- chatHandler.sendMessage(context.sender, EMessageType.RESULT, result)
- } catch (ex: Throwable) {
- chatHandler.handleException(context.sender, context, ex)
- }
- }
-
- return null
-}
-
-@Throws(CommandException::class)
-private fun Deferred<Any?>.getResult(): String? {
- getCompletionExceptionOrNull()?.let { ex ->
- if (ex is CancellationException) {
- System.err.println("An asynchronously dispatched command was cancelled unexpectedly")
- ex.printStackTrace()
- throw CommandException("The command was cancelled unexpectedly (see console)")
- }
- if (ex is Exception) return ReflectiveCommand.getResult(null, ex)
- throw ex
- }
- return ReflectiveCommand.getResult(getCompleted(), null)
-}
+package io.dico.dicore.command.registration.reflect
+
+import io.dico.dicore.command.*
+import kotlinx.coroutines.CoroutineStart.UNDISPATCHED
+import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.async
+import java.lang.reflect.Method
+import java.util.concurrent.CancellationException
+import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.intrinsics.intercepted
+import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn
+import kotlin.reflect.jvm.kotlinFunction
+
+fun isSuspendFunction(method: Method): Boolean {
+ val func = method.kotlinFunction ?: return false
+ return func.isSuspend
+}
+
+fun callAsCoroutine(
+ command: ReflectiveCommand,
+ factory: ICommandInterceptor,
+ context: ExecutionContext,
+ args: Array<Any?>
+): String? {
+ val coroutineContext = factory.getCoroutineContext(context, command.method, command.cmdName) as CoroutineContext
+
+ // UNDISPATCHED causes the handler to run until the first suspension point on the current thread,
+ // meaning command handlers that don't have suspension points will run completely synchronously.
+ // Tasks that take time to compute should suspend the coroutine and resume on another thread.
+ val job = GlobalScope.async(context = coroutineContext, start = UNDISPATCHED) {
+ suspendCoroutineUninterceptedOrReturn<Any?> { cont ->
+ command.method.invoke(command.instance, *args, cont.intercepted())
+ }
+ }
+
+ if (job.isCompleted) {
+ return job.getResult()
+ }
+
+ job.invokeOnCompletion {
+ val chatHandler = context.address.chatHandler
+ try {
+ val result = job.getResult()
+ chatHandler.sendMessage(context.sender, EMessageType.RESULT, result)
+ } catch (ex: Throwable) {
+ chatHandler.handleException(context.sender, context, ex)
+ }
+ }
+
+ return null
+}
+
+@Throws(CommandException::class)
+private fun Deferred<Any?>.getResult(): String? {
+ getCompletionExceptionOrNull()?.let { ex ->
+ if (ex is CancellationException) {
+ System.err.println("An asynchronously dispatched command was cancelled unexpectedly")
+ ex.printStackTrace()
+ throw CommandException("The command was cancelled unexpectedly (see console)")
+ }
+ if (ex is Exception) return ReflectiveCommand.getResult(null, ex)
+ throw ex
+ }
+ return ReflectiveCommand.getResult(getCompleted(), null)
+}
diff --git a/dicore3/command/src/test/java/io/dico/dicore/command/example/ParameterInfoObjectExample.java b/dicore3/command/src/test/java/io/dico/dicore/command/example/ParameterInfoObjectExample.java
index 1cd4e23..c31b96f 100644
--- a/dicore3/command/src/test/java/io/dico/dicore/command/example/ParameterInfoObjectExample.java
+++ b/dicore3/command/src/test/java/io/dico/dicore/command/example/ParameterInfoObjectExample.java
@@ -1,73 +1,73 @@
-package io.dico.dicore.command.example;
-
-import io.dico.dicore.command.CommandBuilder;
-import io.dico.dicore.command.CommandException;
-import io.dico.dicore.command.Validate;
-import io.dico.dicore.command.annotation.Cmd;
-import io.dico.dicore.command.parameter.ArgumentBuffer;
-import io.dico.dicore.command.parameter.Parameter;
-import io.dico.dicore.command.parameter.type.ParameterConfig;
-import io.dico.dicore.command.parameter.type.ParameterType;
-import org.bukkit.command.CommandSender;
-
-public class ParameterInfoObjectExample {
-
- private @interface ParentPermission {
- String value();
- }
-
- static class MyInfoObject {
- public static final ParameterConfig<ParentPermission, MyInfoObject> config = new ParameterConfig<ParentPermission, MyInfoObject>() {
- @Override
- protected MyInfoObject toParameterInfo(ParentPermission annotation) {
- return new MyInfoObject(annotation.value());
- }
- };
-
- private String permissionParent;
-
- MyInfoObject(String permissionParent) {
- this.permissionParent = permissionParent;
- }
-
- public String getPermissionParent() {
- return permissionParent;
- }
- }
-
- static class MyParameterType extends ParameterType<String, MyInfoObject> {
-
- public MyParameterType() {
- super(String.class, MyInfoObject.config);
- }
-
- @Override
- public String parse(Parameter<String, MyInfoObject> parameter, CommandSender sender, ArgumentBuffer buffer) throws CommandException {
- String value = buffer.next();
-
- MyInfoObject mio = parameter.getParamInfo();
- if (mio != null) {
- String permission = mio.permissionParent + "." + value;
- Validate.isAuthorized(sender, permission);
- }
-
- return value;
- }
- }
-
- static class MyCommands {
-
- @Cmd("test")
- Object cmdTest(@ParentPermission("test.permission") String value) {
- return "You have permission to use the argument '" + value + "'!";
- }
-
- }
-
- static void main(String[] args) {
- new CommandBuilder()
- .addParameterType(false, new MyParameterType())
- .registerCommands(new MyCommands());
- }
-
-}
+package io.dico.dicore.command.example;
+
+import io.dico.dicore.command.CommandBuilder;
+import io.dico.dicore.command.CommandException;
+import io.dico.dicore.command.Validate;
+import io.dico.dicore.command.annotation.Cmd;
+import io.dico.dicore.command.parameter.ArgumentBuffer;
+import io.dico.dicore.command.parameter.Parameter;
+import io.dico.dicore.command.parameter.type.ParameterConfig;
+import io.dico.dicore.command.parameter.type.ParameterType;
+import org.bukkit.command.CommandSender;
+
+public class ParameterInfoObjectExample {
+
+ private @interface ParentPermission {
+ String value();
+ }
+
+ static class MyInfoObject {
+ public static final ParameterConfig<ParentPermission, MyInfoObject> config = new ParameterConfig<ParentPermission, MyInfoObject>() {
+ @Override
+ protected MyInfoObject toParameterInfo(ParentPermission annotation) {
+ return new MyInfoObject(annotation.value());
+ }
+ };
+
+ private String permissionParent;
+
+ MyInfoObject(String permissionParent) {
+ this.permissionParent = permissionParent;
+ }
+
+ public String getPermissionParent() {
+ return permissionParent;
+ }
+ }
+
+ static class MyParameterType extends ParameterType<String, MyInfoObject> {
+
+ public MyParameterType() {
+ super(String.class, MyInfoObject.config);
+ }
+
+ @Override
+ public String parse(Parameter<String, MyInfoObject> parameter, CommandSender sender, ArgumentBuffer buffer) throws CommandException {
+ String value = buffer.next();
+
+ MyInfoObject mio = parameter.getParamInfo();
+ if (mio != null) {
+ String permission = mio.permissionParent + "." + value;
+ Validate.isAuthorized(sender, permission);
+ }
+
+ return value;
+ }
+ }
+
+ static class MyCommands {
+
+ @Cmd("test")
+ Object cmdTest(@ParentPermission("test.permission") String value) {
+ return "You have permission to use the argument '" + value + "'!";
+ }
+
+ }
+
+ static void main(String[] args) {
+ new CommandBuilder()
+ .addParameterType(false, new MyParameterType())
+ .registerCommands(new MyCommands());
+ }
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/BitModifier.java b/dicore3/core/src/main/java/io/dico/dicore/BitModifier.java
index e4facb1..ae25123 100644
--- a/dicore3/core/src/main/java/io/dico/dicore/BitModifier.java
+++ b/dicore3/core/src/main/java/io/dico/dicore/BitModifier.java
@@ -1,182 +1,182 @@
-package io.dico.dicore;
-
-public interface BitModifier {
-
- long mask();
-
- long get(long x);
-
- long set(long x, long value);
-
- default int lowerBound() {
- return Long.numberOfTrailingZeros(mask());
- }
-
- default int upperBound() {
- return 64 - Long.numberOfLeadingZeros(mask());
- }
-
- default int bitCount() {
- return Long.bitCount(mask());
- }
-
- default boolean getBoolean(long x) {
- return get(x) == 1;
- }
-
- default long setBoolean(long x, boolean value) {
- return set(x, value ? 1 : 0);
- }
-
- default int getInt(long x) {
- return (int) (get(x) & 0xFFFFFFFFL);
- }
-
- default long setInt(long x, int value) {
- return set(x, value & 0xFFFFFFFFL);
- }
-
- default short getShort(long x) {
- return (short) (get(x) & 0xFFFFL);
- }
-
- default long setShort(long x, int value) {
- return set(x, value & 0xFFFFL);
- }
-
- default byte getByte(long x) {
- return (byte) (get(x) & 0xFFL);
- }
-
- default long setByte(long x, int value) {
- return set(x, value & 0xFFL);
- }
-
- final class OfSingle implements BitModifier {
- private final long mask;
-
- public OfSingle(int bit) {
- if (bit < 0 || bit >= 64) {
- throw new IndexOutOfBoundsException();
- }
- this.mask = 1L << bit;
- }
-
- @Override
- public int bitCount() {
- return 1;
- }
-
- @Override
- public long mask() {
- return mask;
- }
-
- public boolean getBoolean(long x) {
- return (x & mask) != 0;
- }
-
- public long setBoolean(long x, boolean value) {
- return value ? (x | mask) : (x & ~mask);
- }
-
- @Override
- public long get(long x) {
- return getBoolean(x) ? 1 : 0;
- }
-
- @Override
- public long set(long x, long value) {
- if (value < 0 || value > 1) {
- throw new IllegalArgumentException();
- }
- return setBoolean(x, value == 1);
- }
- }
-
- final class OfMultiple implements BitModifier {
- private final int lowerBound;
- private final int bitCount;
- private final long mask;
-
- public OfMultiple(int lowerBound, int bitCount) {
- int upperBound = lowerBound + bitCount;
- if (lowerBound < 0 || lowerBound >= 64 || upperBound < 1 || upperBound > 64 || upperBound < lowerBound) {
- throw new IndexOutOfBoundsException();
- }
- this.lowerBound = lowerBound;
- this.bitCount = bitCount;
- this.mask = (Long.MIN_VALUE >> (bitCount - 1)) >>> (64 - bitCount - lowerBound);
- }
-
- @Override
- public int lowerBound() {
- return lowerBound;
- }
-
- @Override
- public int bitCount() {
- return bitCount;
- }
-
- @Override
- public int upperBound() {
- return lowerBound + bitCount;
- }
-
- @Override
- public long mask() {
- return mask;
- }
-
- @Override
- public long get(long x) {
- return (x & mask) >>> lowerBound;
- }
-
- @Override
- public long set(long x, long value) {
- return (x & ~mask) | ((value << lowerBound) & mask);
- }
-
- }
-
- class Builder {
- int currentIndex;
-
- public int getCurrentIndex() {
- return currentIndex;
- }
-
- public int getRemaining() {
- return 64 - currentIndex;
- }
-
- public OfSingle single() {
- checkAvailable(1);
- return new OfSingle(currentIndex++);
- }
-
- public BitModifier size(int size) {
- if (size == 1) {
- return single();
- }
- checkAvailable(size);
- BitModifier result = new OfMultiple(currentIndex, size);
- currentIndex += size;
- return result;
- }
-
- public BitModifier remaining() {
- return size(getRemaining());
- }
-
- private void checkAvailable(int size) {
- if (size <= 0 || currentIndex + size > 64) {
- throw new IllegalStateException("Exceeding bit count of a long");
- }
- }
-
- }
-
-}
+package io.dico.dicore;
+
+public interface BitModifier {
+
+ long mask();
+
+ long get(long x);
+
+ long set(long x, long value);
+
+ default int lowerBound() {
+ return Long.numberOfTrailingZeros(mask());
+ }
+
+ default int upperBound() {
+ return 64 - Long.numberOfLeadingZeros(mask());
+ }
+
+ default int bitCount() {
+ return Long.bitCount(mask());
+ }
+
+ default boolean getBoolean(long x) {
+ return get(x) == 1;
+ }
+
+ default long setBoolean(long x, boolean value) {
+ return set(x, value ? 1 : 0);
+ }
+
+ default int getInt(long x) {
+ return (int) (get(x) & 0xFFFFFFFFL);
+ }
+
+ default long setInt(long x, int value) {
+ return set(x, value & 0xFFFFFFFFL);
+ }
+
+ default short getShort(long x) {
+ return (short) (get(x) & 0xFFFFL);
+ }
+
+ default long setShort(long x, int value) {
+ return set(x, value & 0xFFFFL);
+ }
+
+ default byte getByte(long x) {
+ return (byte) (get(x) & 0xFFL);
+ }
+
+ default long setByte(long x, int value) {
+ return set(x, value & 0xFFL);
+ }
+
+ final class OfSingle implements BitModifier {
+ private final long mask;
+
+ public OfSingle(int bit) {
+ if (bit < 0 || bit >= 64) {
+ throw new IndexOutOfBoundsException();
+ }
+ this.mask = 1L << bit;
+ }
+
+ @Override
+ public int bitCount() {
+ return 1;
+ }
+
+ @Override
+ public long mask() {
+ return mask;
+ }
+
+ public boolean getBoolean(long x) {
+ return (x & mask) != 0;
+ }
+
+ public long setBoolean(long x, boolean value) {
+ return value ? (x | mask) : (x & ~mask);
+ }
+
+ @Override
+ public long get(long x) {
+ return getBoolean(x) ? 1 : 0;
+ }
+
+ @Override
+ public long set(long x, long value) {
+ if (value < 0 || value > 1) {
+ throw new IllegalArgumentException();
+ }
+ return setBoolean(x, value == 1);
+ }
+ }
+
+ final class OfMultiple implements BitModifier {
+ private final int lowerBound;
+ private final int bitCount;
+ private final long mask;
+
+ public OfMultiple(int lowerBound, int bitCount) {
+ int upperBound = lowerBound + bitCount;
+ if (lowerBound < 0 || lowerBound >= 64 || upperBound < 1 || upperBound > 64 || upperBound < lowerBound) {
+ throw new IndexOutOfBoundsException();
+ }
+ this.lowerBound = lowerBound;
+ this.bitCount = bitCount;
+ this.mask = (Long.MIN_VALUE >> (bitCount - 1)) >>> (64 - bitCount - lowerBound);
+ }
+
+ @Override
+ public int lowerBound() {
+ return lowerBound;
+ }
+
+ @Override
+ public int bitCount() {
+ return bitCount;
+ }
+
+ @Override
+ public int upperBound() {
+ return lowerBound + bitCount;
+ }
+
+ @Override
+ public long mask() {
+ return mask;
+ }
+
+ @Override
+ public long get(long x) {
+ return (x & mask) >>> lowerBound;
+ }
+
+ @Override
+ public long set(long x, long value) {
+ return (x & ~mask) | ((value << lowerBound) & mask);
+ }
+
+ }
+
+ class Builder {
+ int currentIndex;
+
+ public int getCurrentIndex() {
+ return currentIndex;
+ }
+
+ public int getRemaining() {
+ return 64 - currentIndex;
+ }
+
+ public OfSingle single() {
+ checkAvailable(1);
+ return new OfSingle(currentIndex++);
+ }
+
+ public BitModifier size(int size) {
+ if (size == 1) {
+ return single();
+ }
+ checkAvailable(size);
+ BitModifier result = new OfMultiple(currentIndex, size);
+ currentIndex += size;
+ return result;
+ }
+
+ public BitModifier remaining() {
+ return size(getRemaining());
+ }
+
+ private void checkAvailable(int size) {
+ if (size <= 0 || currentIndex + size > 64) {
+ throw new IllegalStateException("Exceeding bit count of a long");
+ }
+ }
+
+ }
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/Formatting.java b/dicore3/core/src/main/java/io/dico/dicore/Formatting.java
index 9fdfe0e..40afb9b 100644
--- a/dicore3/core/src/main/java/io/dico/dicore/Formatting.java
+++ b/dicore3/core/src/main/java/io/dico/dicore/Formatting.java
@@ -1,295 +1,295 @@
-/*
- * Copyright (c) 2017 ProjectOreville
- *
- * All rights reserved.
- *
- * Author(s):
- * Dico Karssiens
- */
-
-package io.dico.dicore;
-
-import org.jetbrains.annotations.NotNull;
-
-public final class Formatting implements CharSequence {
- public static final char FORMAT_CHAR = '\u00a7';
- private static final String CACHED_CHARS = "0123456789abcdefklmnor";
- private static final Formatting[] singleCharInstances = new Formatting[CACHED_CHARS.length()];
-
- @NotNull
- public static final Formatting
- BLACK = from('0'),
- DARK_BLUE = from('1'),
- DARL_GREEN = from('2'),
- CYAN = from('3'),
- DARK_RED = from('4'),
- PURPLE = from('5'),
- ORANGE = from('6'),
- GRAY = from('7'),
- DARK_GRAY = from('8'),
- BLUE = from('9'),
- GREEN = from('a'),
- AQUA = from('b'),
- RED = from('c'),
- PINK = from('d'),
- YELLOW = from('e'),
- WHITE = from('f'),
- BOLD = from('l'),
- STRIKETHROUGH = from('m'),
- UNDERLINE = from('n'),
- ITALIC = from('o'),
- MAGIC = from('k'),
- RESET = from('r'),
- EMPTY = from('\0');
-
- public static String stripAll(String value) {
- return stripAll(FORMAT_CHAR, value);
- }
-
- public static String stripAll(char alternateChar, String value) {
- int index = value.indexOf(alternateChar);
- int max;
- if (index == -1 || index == (max = value.length() - 1)) {
- return value;
- }
-
- StringBuilder result = new StringBuilder();
- int from = 0;
- do {
- if (isRecognizedChar(value.charAt(index + 1))) {
- result.append(value, from, index);
- from = index + 2;
- } else {
- result.append(value, from, from = index + 2);
- }
-
- index = value.indexOf(alternateChar, index + 1);
- } while (index != -1 && index != max && from <= max);
-
- if (from <= max) {
- result.append(value, from, value.length());
- }
- return result.toString();
- }
-
- public static String stripFirst(String value) {
- return stripFirst(FORMAT_CHAR, value);
- }
-
- public static String stripFirst(char alternateChar, String value) {
- int index = value.indexOf(alternateChar);
- int max;
- if (index == -1 || index == (max = value.length() - 1)) {
- return value;
- }
-
- StringBuilder result = new StringBuilder(value.length());
- int from = 0;
- if (isRecognizedChar(value.charAt(index + 1))) {
- result.append(value, from, index);
- from = index + 2;
- } else {
- result.append(value, from, from = index + 2);
- }
-
- if (from < max) {
- result.append(value, from, value.length());
- }
- return result.toString();
- }
-
- public static Formatting from(char c) {
- if (isRecognizedChar(c)) {
- c = Character.toLowerCase(c);
- int index = CACHED_CHARS.indexOf(c);
- if (index == -1) return EMPTY;
-
- Formatting res = singleCharInstances[index];
- if (res == null) {
- singleCharInstances[index] = res = new Formatting(c);
- }
- return res;
- }
- return EMPTY;
- }
-
- public static Formatting from(String chars) {
- return chars.length() == 1 ? from(chars.charAt(0)) : getFormats(chars, '\0');
- }
-
- public static Formatting getFormats(String input) {
- return getFormats(input, FORMAT_CHAR);
- }
-
- public static Formatting getFormats(String input, char formatChar) {
- return getFormats(input, 0, input.length(), formatChar);
- }
-
- public static Formatting getFormats(String input, int start, int end, char formatChar) {
- if ((start < 0) || (start > end) || (end > input.length())) {
- throw new IndexOutOfBoundsException("start " + start + ", end " + end + ", input.length() " + input.length());
- }
-
- boolean needsFormatChar = formatChar != '\0';
- char[] formats = new char[6];
- // just make sure it's not the same as formatChar
- char previous = (char) (formatChar + 1);
-
- for (int i = start; i < end; i++) {
- char c = input.charAt(i);
-
- if (previous == formatChar || !needsFormatChar) {
- if (isColourChar(c) || isResetChar(c)) {
- formats = new char[6];
- formats[0] = Character.toLowerCase(c);
- } else if (isFormatChar(c)) {
- char format = Character.toLowerCase(c);
- for (int j = 0; j < 6; j++) {
- if (formats[j] == '\0') {
- formats[j] = format;
- break;
- } else if (formats[j] == format) {
- break;
- }
- }
- }
- }
-
- previous = c;
- }
-
- return formats[1] == '\0' ? from(formats[0]) : new Formatting(formats);
- }
-
- public static String translate(String input) {
- return translateChars('&', input);
- }
-
- public static String translateChars(char alternateChar, String input) {
- return translateFormat(alternateChar, FORMAT_CHAR, input);
- }
-
- public static String revert(String input) {
- return revertChars('&', input);
- }
-
- public static String revertChars(char alternateChar, String input) {
- return translateFormat(FORMAT_CHAR, alternateChar, input);
- }
-
- public static String translateFormat(char fromChar, char toChar, String input) {
- if (input == null) {
- return null;
- }
- int n = input.length();
- if (n < 2) {
- return input;
- }
- char[] result = null;
- char previous = input.charAt(0);
- for (int i = 1; i < n; i++) {
- char c = input.charAt(i);
- if (previous == fromChar && isRecognizedChar(c)) {
- if (result == null) {
- result = input.toCharArray();
- }
- result[i - 1] = toChar;
- }
- previous = c;
- }
- return result == null ? input : String.valueOf(result);
- }
-
- public static void translate(StringBuilder input) {
- translateChars('&', input);
- }
-
- public static void translateChars(char alternateChar, StringBuilder input) {
- translateFormat(alternateChar, FORMAT_CHAR, input);
- }
-
- public static void revert(StringBuilder input) {
- revertChars('&', input);
- }
-
- public static void revertChars(char alternateChar, StringBuilder input) {
- translateFormat(FORMAT_CHAR, alternateChar, input);
- }
-
- public static void translateFormat(char fromChar, char toChar, StringBuilder input) {
- if (input == null) {
- return;
- }
- int n = input.length();
- if (n < 2) {
- return;
- }
- char previous = input.charAt(0);
- for (int i = 1; i < n; i++) {
- char c = input.charAt(i);
- if (previous == fromChar && isRecognizedChar(c)) {
- input.setCharAt(i -1, toChar);
- }
- previous = c;
- }
- }
-
- private static boolean isRecognizedChar(char c) {
- return isColourChar(c) || isFormatChar(c) || isResetChar(c);
- }
-
- private static boolean isColourChar(char c) {
- return "0123456789abcdefABCDEF".indexOf(c) >= 0;
- }
-
- private static boolean isResetChar(char c) {
- return c == 'r' || c == 'R';
- }
-
- private static boolean isFormatChar(char c) {
- return "klmnoKLMNO".indexOf(c) >= 0;
- }
-
- private final String format;
-
- private Formatting(char[] formats) {
- StringBuilder format = new StringBuilder(12);
- for (char c : formats) {
- if (c != '\0') {
- format.append(FORMAT_CHAR).append(c);
- } else {
- break;
- }
- }
- this.format = format.toString();
- }
-
- private Formatting(char c) {
- this.format = (c != '\0') ? String.valueOf(new char[]{FORMAT_CHAR, c}) : "";
- }
-
- @Override
- public int length() {
- return format.length();
- }
-
- @Override
- public char charAt(int index) {
- return format.charAt(index);
- }
-
- @Override
- public String subSequence(int start, int end) {
- return format.substring(start, end);
- }
-
- @Override
- public String toString() {
- return format;
- }
-
- public String toString(char formatChar) {
- return format.replace(FORMAT_CHAR, formatChar);
- }
-
+/*
+ * Copyright (c) 2017 ProjectOreville
+ *
+ * All rights reserved.
+ *
+ * Author(s):
+ * Dico Karssiens
+ */
+
+package io.dico.dicore;
+
+import org.jetbrains.annotations.NotNull;
+
+public final class Formatting implements CharSequence {
+ public static final char FORMAT_CHAR = '\u00a7';
+ private static final String CACHED_CHARS = "0123456789abcdefklmnor";
+ private static final Formatting[] singleCharInstances = new Formatting[CACHED_CHARS.length()];
+
+ @NotNull
+ public static final Formatting
+ BLACK = from('0'),
+ DARK_BLUE = from('1'),
+ DARL_GREEN = from('2'),
+ CYAN = from('3'),
+ DARK_RED = from('4'),
+ PURPLE = from('5'),
+ ORANGE = from('6'),
+ GRAY = from('7'),
+ DARK_GRAY = from('8'),
+ BLUE = from('9'),
+ GREEN = from('a'),
+ AQUA = from('b'),
+ RED = from('c'),
+ PINK = from('d'),
+ YELLOW = from('e'),
+ WHITE = from('f'),
+ BOLD = from('l'),
+ STRIKETHROUGH = from('m'),
+ UNDERLINE = from('n'),
+ ITALIC = from('o'),
+ MAGIC = from('k'),
+ RESET = from('r'),
+ EMPTY = from('\0');
+
+ public static String stripAll(String value) {
+ return stripAll(FORMAT_CHAR, value);
+ }
+
+ public static String stripAll(char alternateChar, String value) {
+ int index = value.indexOf(alternateChar);
+ int max;
+ if (index == -1 || index == (max = value.length() - 1)) {
+ return value;
+ }
+
+ StringBuilder result = new StringBuilder();
+ int from = 0;
+ do {
+ if (isRecognizedChar(value.charAt(index + 1))) {
+ result.append(value, from, index);
+ from = index + 2;
+ } else {
+ result.append(value, from, from = index + 2);
+ }
+
+ index = value.indexOf(alternateChar, index + 1);
+ } while (index != -1 && index != max && from <= max);
+
+ if (from <= max) {
+ result.append(value, from, value.length());
+ }
+ return result.toString();
+ }
+
+ public static String stripFirst(String value) {
+ return stripFirst(FORMAT_CHAR, value);
+ }
+
+ public static String stripFirst(char alternateChar, String value) {
+ int index = value.indexOf(alternateChar);
+ int max;
+ if (index == -1 || index == (max = value.length() - 1)) {
+ return value;
+ }
+
+ StringBuilder result = new StringBuilder(value.length());
+ int from = 0;
+ if (isRecognizedChar(value.charAt(index + 1))) {
+ result.append(value, from, index);
+ from = index + 2;
+ } else {
+ result.append(value, from, from = index + 2);
+ }
+
+ if (from < max) {
+ result.append(value, from, value.length());
+ }
+ return result.toString();
+ }
+
+ public static Formatting from(char c) {
+ if (isRecognizedChar(c)) {
+ c = Character.toLowerCase(c);
+ int index = CACHED_CHARS.indexOf(c);
+ if (index == -1) return EMPTY;
+
+ Formatting res = singleCharInstances[index];
+ if (res == null) {
+ singleCharInstances[index] = res = new Formatting(c);
+ }
+ return res;
+ }
+ return EMPTY;
+ }
+
+ public static Formatting from(String chars) {
+ return chars.length() == 1 ? from(chars.charAt(0)) : getFormats(chars, '\0');
+ }
+
+ public static Formatting getFormats(String input) {
+ return getFormats(input, FORMAT_CHAR);
+ }
+
+ public static Formatting getFormats(String input, char formatChar) {
+ return getFormats(input, 0, input.length(), formatChar);
+ }
+
+ public static Formatting getFormats(String input, int start, int end, char formatChar) {
+ if ((start < 0) || (start > end) || (end > input.length())) {
+ throw new IndexOutOfBoundsException("start " + start + ", end " + end + ", input.length() " + input.length());
+ }
+
+ boolean needsFormatChar = formatChar != '\0';
+ char[] formats = new char[6];
+ // just make sure it's not the same as formatChar
+ char previous = (char) (formatChar + 1);
+
+ for (int i = start; i < end; i++) {
+ char c = input.charAt(i);
+
+ if (previous == formatChar || !needsFormatChar) {
+ if (isColourChar(c) || isResetChar(c)) {
+ formats = new char[6];
+ formats[0] = Character.toLowerCase(c);
+ } else if (isFormatChar(c)) {
+ char format = Character.toLowerCase(c);
+ for (int j = 0; j < 6; j++) {
+ if (formats[j] == '\0') {
+ formats[j] = format;
+ break;
+ } else if (formats[j] == format) {
+ break;
+ }
+ }
+ }
+ }
+
+ previous = c;
+ }
+
+ return formats[1] == '\0' ? from(formats[0]) : new Formatting(formats);
+ }
+
+ public static String translate(String input) {
+ return translateChars('&', input);
+ }
+
+ public static String translateChars(char alternateChar, String input) {
+ return translateFormat(alternateChar, FORMAT_CHAR, input);
+ }
+
+ public static String revert(String input) {
+ return revertChars('&', input);
+ }
+
+ public static String revertChars(char alternateChar, String input) {
+ return translateFormat(FORMAT_CHAR, alternateChar, input);
+ }
+
+ public static String translateFormat(char fromChar, char toChar, String input) {
+ if (input == null) {
+ return null;
+ }
+ int n = input.length();
+ if (n < 2) {
+ return input;
+ }
+ char[] result = null;
+ char previous = input.charAt(0);
+ for (int i = 1; i < n; i++) {
+ char c = input.charAt(i);
+ if (previous == fromChar && isRecognizedChar(c)) {
+ if (result == null) {
+ result = input.toCharArray();
+ }
+ result[i - 1] = toChar;
+ }
+ previous = c;
+ }
+ return result == null ? input : String.valueOf(result);
+ }
+
+ public static void translate(StringBuilder input) {
+ translateChars('&', input);
+ }
+
+ public static void translateChars(char alternateChar, StringBuilder input) {
+ translateFormat(alternateChar, FORMAT_CHAR, input);
+ }
+
+ public static void revert(StringBuilder input) {
+ revertChars('&', input);
+ }
+
+ public static void revertChars(char alternateChar, StringBuilder input) {
+ translateFormat(FORMAT_CHAR, alternateChar, input);
+ }
+
+ public static void translateFormat(char fromChar, char toChar, StringBuilder input) {
+ if (input == null) {
+ return;
+ }
+ int n = input.length();
+ if (n < 2) {
+ return;
+ }
+ char previous = input.charAt(0);
+ for (int i = 1; i < n; i++) {
+ char c = input.charAt(i);
+ if (previous == fromChar && isRecognizedChar(c)) {
+ input.setCharAt(i -1, toChar);
+ }
+ previous = c;
+ }
+ }
+
+ private static boolean isRecognizedChar(char c) {
+ return isColourChar(c) || isFormatChar(c) || isResetChar(c);
+ }
+
+ private static boolean isColourChar(char c) {
+ return "0123456789abcdefABCDEF".indexOf(c) >= 0;
+ }
+
+ private static boolean isResetChar(char c) {
+ return c == 'r' || c == 'R';
+ }
+
+ private static boolean isFormatChar(char c) {
+ return "klmnoKLMNO".indexOf(c) >= 0;
+ }
+
+ private final String format;
+
+ private Formatting(char[] formats) {
+ StringBuilder format = new StringBuilder(12);
+ for (char c : formats) {
+ if (c != '\0') {
+ format.append(FORMAT_CHAR).append(c);
+ } else {
+ break;
+ }
+ }
+ this.format = format.toString();
+ }
+
+ private Formatting(char c) {
+ this.format = (c != '\0') ? String.valueOf(new char[]{FORMAT_CHAR, c}) : "";
+ }
+
+ @Override
+ public int length() {
+ return format.length();
+ }
+
+ @Override
+ public char charAt(int index) {
+ return format.charAt(index);
+ }
+
+ @Override
+ public String subSequence(int start, int end) {
+ return format.substring(start, end);
+ }
+
+ @Override
+ public String toString() {
+ return format;
+ }
+
+ public String toString(char formatChar) {
+ return format.replace(FORMAT_CHAR, formatChar);
+ }
+
} \ No newline at end of file
diff --git a/dicore3/core/src/main/java/io/dico/dicore/InterfaceChain.java b/dicore3/core/src/main/java/io/dico/dicore/InterfaceChain.java
index b59979a..f92da6b 100644
--- a/dicore3/core/src/main/java/io/dico/dicore/InterfaceChain.java
+++ b/dicore3/core/src/main/java/io/dico/dicore/InterfaceChain.java
@@ -1,173 +1,173 @@
-package io.dico.dicore;
-
-/**
- * A chainable object
- * <p>
- * It is not possible to declare another upper bound for type parameter Subtype.
- * However, it is required that it also extends the type parameter Element.
- *
- * @param <Subtype> the interface that is chainable
- * @param <Element> the element of the chain, this is a supertype of the subtype
- */
-@SuppressWarnings("unchecked")
-public interface InterfaceChain<Element, Subtype extends InterfaceChain<Element, Subtype>> {
-
- /**
- * returns the empty InterfaceChain instance.
- *
- * @return the empty InterfaceChain instance
- */
- Subtype getEmptyInstance();
-
- /**
- * returns a InterfaceChain with the last added element detached.
- * <p>
- * if this InterfaceChain is the empty instance, the empty instance is returned.
- *
- * @return a InterfaceChain with the last added element detached
- * @implNote for the purpose of lambdas, the default implementation also returns the empty instance.
- */
- default Subtype withoutLastNode() {
- return getEmptyInstance();
- }
-
- /**
- * returns the element that was inserted as the last element
- * <p>
- * For instance, calling this method on the result of calling {@link InterfaceChain#withElement(Object)}
- * would return the given element.
- *
- * @return the element that was inserted as the last element
- * @implNote for the purpose of lambdas, the default implementation returns this object,
- * which is required to implement the Element type parameter.
- */
- default Element getDelegateOfLastNode() {
- //noinspection unchecked
- return (Element) this;
- }
-
- /**
- * @return The number of elements chained from this InterfaceChain.
- * @implNote for the purpose of lambdas, the default implementation returns 1.
- */
- default int getElementCount() {
- return 1;
- }
-
- /**
- * Get a new InterfaceChain that includes the given Element.
- * <p>
- * The default implementation of the Subtype should look like this:
- * <pre> {@code
- * if (element == null) {
- * return this;
- * }
- *
- * int count = getElementCount() + 1;
- * return new Subtype() {
- * \@Override
- * public void exampleElementMethod() {
- * try {
- * Subtype.this.exampleElementMethod();
- * } finally {
- * element.exampleElementMethod();
- * }
- * }
- *
- * \@Override
- * public Subtype withoutLastNode() {
- * return Subtype.this;
- * }
- *
- * \@Override
- * public Element getDelegateOfLastNode() {
- * return element;
- * }
- *
- * \@Override
- * public int getElementCount() {
- * return count;
- * }
- * };
- * }
- * </pre>
- *
- * @param element A new element to insert at the end of this InterfaceChain.
- * @return a new InterfaceChain that includes the given Element.
- */
- Subtype withElement(Element element);
-
- /**
- * Append each of the elements to this InterfaceChain
- *
- * @param elements the elements to append
- * @return a new InterfaceChain with the elements appended
- */
- default Subtype withElements(Element... elements) {
- Subtype result = (Subtype) this;
- for (Element element : elements) {
- result = result.withElement(element);
- }
- return result;
- }
-
- /*
- Example Subtypes implementation
-
- public class Subtypes {
-
- private Subtypes() {
-
- }
-
- private static final Subtype empty = new Subtype() {
- @Override
- public void exampleElementMethod() {
-
- }
-
- @Override
- public Subtype withElement(Element other) {
- return Subtypes.singleton(other);
- }
-
- @Override
- public int getElementCount() {
- return 0;
- }
-
- @Override
- public Element getDelegateOfLastNode() {
- return null;
- }
- };
-
- public static Subtype empty() {
- return empty;
- }
-
- public static Subtype singleton(Element element) {
- if (element instanceof Subtype) {
- return (Subtype) element;
- }
- if (element == null) {
- return empty();
- }
- return new Subtype() {
- @Override
- public void exampleElementMethod() {
- element.exampleElementMethod();
- }
-
- @Override
- public Element getDelegateOfLastNode() {
- return element;
- }
- };
- }
-
- }
-
- */
-
-}
+package io.dico.dicore;
+
+/**
+ * A chainable object
+ * <p>
+ * It is not possible to declare another upper bound for type parameter Subtype.
+ * However, it is required that it also extends the type parameter Element.
+ *
+ * @param <Subtype> the interface that is chainable
+ * @param <Element> the element of the chain, this is a supertype of the subtype
+ */
+@SuppressWarnings("unchecked")
+public interface InterfaceChain<Element, Subtype extends InterfaceChain<Element, Subtype>> {
+
+ /**
+ * returns the empty InterfaceChain instance.
+ *
+ * @return the empty InterfaceChain instance
+ */
+ Subtype getEmptyInstance();
+
+ /**
+ * returns a InterfaceChain with the last added element detached.
+ * <p>
+ * if this InterfaceChain is the empty instance, the empty instance is returned.
+ *
+ * @return a InterfaceChain with the last added element detached
+ * @implNote for the purpose of lambdas, the default implementation also returns the empty instance.
+ */
+ default Subtype withoutLastNode() {
+ return getEmptyInstance();
+ }
+
+ /**
+ * returns the element that was inserted as the last element
+ * <p>
+ * For instance, calling this method on the result of calling {@link InterfaceChain#withElement(Object)}
+ * would return the given element.
+ *
+ * @return the element that was inserted as the last element
+ * @implNote for the purpose of lambdas, the default implementation returns this object,
+ * which is required to implement the Element type parameter.
+ */
+ default Element getDelegateOfLastNode() {
+ //noinspection unchecked
+ return (Element) this;
+ }
+
+ /**
+ * @return The number of elements chained from this InterfaceChain.
+ * @implNote for the purpose of lambdas, the default implementation returns 1.
+ */
+ default int getElementCount() {
+ return 1;
+ }
+
+ /**
+ * Get a new InterfaceChain that includes the given Element.
+ * <p>
+ * The default implementation of the Subtype should look like this:
+ * <pre> {@code
+ * if (element == null) {
+ * return this;
+ * }
+ *
+ * int count = getElementCount() + 1;
+ * return new Subtype() {
+ * \@Override
+ * public void exampleElementMethod() {
+ * try {
+ * Subtype.this.exampleElementMethod();
+ * } finally {
+ * element.exampleElementMethod();
+ * }
+ * }
+ *
+ * \@Override
+ * public Subtype withoutLastNode() {
+ * return Subtype.this;
+ * }
+ *
+ * \@Override
+ * public Element getDelegateOfLastNode() {
+ * return element;
+ * }
+ *
+ * \@Override
+ * public int getElementCount() {
+ * return count;
+ * }
+ * };
+ * }
+ * </pre>
+ *
+ * @param element A new element to insert at the end of this InterfaceChain.
+ * @return a new InterfaceChain that includes the given Element.
+ */
+ Subtype withElement(Element element);
+
+ /**
+ * Append each of the elements to this InterfaceChain
+ *
+ * @param elements the elements to append
+ * @return a new InterfaceChain with the elements appended
+ */
+ default Subtype withElements(Element... elements) {
+ Subtype result = (Subtype) this;
+ for (Element element : elements) {
+ result = result.withElement(element);
+ }
+ return result;
+ }
+
+ /*
+ Example Subtypes implementation
+
+ public class Subtypes {
+
+ private Subtypes() {
+
+ }
+
+ private static final Subtype empty = new Subtype() {
+ @Override
+ public void exampleElementMethod() {
+
+ }
+
+ @Override
+ public Subtype withElement(Element other) {
+ return Subtypes.singleton(other);
+ }
+
+ @Override
+ public int getElementCount() {
+ return 0;
+ }
+
+ @Override
+ public Element getDelegateOfLastNode() {
+ return null;
+ }
+ };
+
+ public static Subtype empty() {
+ return empty;
+ }
+
+ public static Subtype singleton(Element element) {
+ if (element instanceof Subtype) {
+ return (Subtype) element;
+ }
+ if (element == null) {
+ return empty();
+ }
+ return new Subtype() {
+ @Override
+ public void exampleElementMethod() {
+ element.exampleElementMethod();
+ }
+
+ @Override
+ public Element getDelegateOfLastNode() {
+ return element;
+ }
+ };
+ }
+
+ }
+
+ */
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/InventoryEventUtil.java b/dicore3/core/src/main/java/io/dico/dicore/InventoryEventUtil.java
index 5c19848..3be2179 100644
--- a/dicore3/core/src/main/java/io/dico/dicore/InventoryEventUtil.java
+++ b/dicore3/core/src/main/java/io/dico/dicore/InventoryEventUtil.java
@@ -1,120 +1,120 @@
-package io.dico.dicore;
-
-import org.bukkit.Material;
-import org.bukkit.event.inventory.InventoryClickEvent;
-import org.bukkit.inventory.Inventory;
-import org.bukkit.inventory.ItemStack;
-import org.bukkit.inventory.PlayerInventory;
-
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-public class InventoryEventUtil {
-
- private InventoryEventUtil() {
-
- }
-
- public static ItemStack getNewItem(InventoryClickEvent event) {
- Inventory clicked = event.getInventory();
- switch (event.getAction()) {
- case SWAP_WITH_CURSOR:
- case PLACE_ALL:
- return event.getCursor();
- case PICKUP_ALL:
- case HOTBAR_MOVE_AND_READD:
- case MOVE_TO_OTHER_INVENTORY:
- case DROP_ALL_SLOT:
- case COLLECT_TO_CURSOR:
- return null;
- case PICKUP_HALF:
- case PICKUP_SOME:
- ItemStack item = clicked.getItem(event.getSlot()).clone();
- item.setAmount(item.getAmount() / 2);
- return item;
- case PICKUP_ONE:
- case DROP_ONE_SLOT:
- item = clicked.getItem(event.getSlot()).clone();
- item.setAmount(Math.max(0, item.getAmount() - 1));
- return item;
- case PLACE_ONE:
- item = event.getView().getCursor().clone();
- item.setAmount(1);
- return item;
- case PLACE_SOME:
- item = event.getView().getCursor().clone();
- item.setAmount(item.getAmount() / 2);
- return item;
- case HOTBAR_SWAP:
- return event.getView().getBottomInventory().getItem(event.getHotbarButton());
- default:
- return clicked.getItem(event.getSlot());
- }
- }
-
-
- public static Map<Integer, ItemStack> deduceChangesIfItemAdded(Inventory inventory, ItemStack added, boolean computeNewItem) {
- int addedAmount = added.getAmount();
- Map<Integer, ItemStack> rv = Collections.emptyMap();
-
- for (int n = inventory.getSize(), i = 0; i < n; i++) {
- if (addedAmount <= 0) break;
-
- ItemStack current = inventory.getItem(i);
- if (current == null || current.getType() == Material.AIR || current.isSimilar(added)) {
- int count = current == null ? 0 : current.getAmount();
- int max = (current == null ? added : current).getType().getMaxStackSize();
- if (count < max) {
- int diff = max - count;
- if (diff > addedAmount) {
- diff = addedAmount;
- }
- addedAmount -= diff;
-
- if (rv.isEmpty()) rv = new LinkedHashMap<>();
-
- if (computeNewItem) {
- current = (current == null ? added : current).clone();
- current.setAmount(count + diff);
- rv.put(i, current);
- } else {
- rv.put(i, null);
- }
- }
- }
- }
-
- return rv;
- }
-
-
- public static ItemStack getNewHeldItemIfPickedUp(PlayerInventory inventory, ItemStack added) {
- int heldItemSlot = inventory.getHeldItemSlot();
- ItemStack heldItem = inventory.getItem(heldItemSlot);
-
- if (SpigotUtil.isItemPresent(heldItem) && !added.isSimilar(heldItem)) {
- return null;
- }
-
- int amt = added.getAmount();
- for (int i = 0; i < heldItemSlot; i++) {
- ItemStack item = inventory.getItem(i);
- if (!SpigotUtil.isItemPresent(item)) {
- return null;
- }
- if (item.isSimilar(item)) {
- amt -= Math.max(0, item.getMaxStackSize() - item.getAmount());
- if (amt <= 0) {
- return null;
- }
- }
- }
-
- added = added.clone();
- added.setAmount(amt + Math.max(0, heldItem == null ? 0 : heldItem.getAmount()));
- return added;
- }
-
-
-}
+package io.dico.dicore;
+
+import org.bukkit.Material;
+import org.bukkit.event.inventory.InventoryClickEvent;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.PlayerInventory;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class InventoryEventUtil {
+
+ private InventoryEventUtil() {
+
+ }
+
+ public static ItemStack getNewItem(InventoryClickEvent event) {
+ Inventory clicked = event.getInventory();
+ switch (event.getAction()) {
+ case SWAP_WITH_CURSOR:
+ case PLACE_ALL:
+ return event.getCursor();
+ case PICKUP_ALL:
+ case HOTBAR_MOVE_AND_READD:
+ case MOVE_TO_OTHER_INVENTORY:
+ case DROP_ALL_SLOT:
+ case COLLECT_TO_CURSOR:
+ return null;
+ case PICKUP_HALF:
+ case PICKUP_SOME:
+ ItemStack item = clicked.getItem(event.getSlot()).clone();
+ item.setAmount(item.getAmount() / 2);
+ return item;
+ case PICKUP_ONE:
+ case DROP_ONE_SLOT:
+ item = clicked.getItem(event.getSlot()).clone();
+ item.setAmount(Math.max(0, item.getAmount() - 1));
+ return item;
+ case PLACE_ONE:
+ item = event.getView().getCursor().clone();
+ item.setAmount(1);
+ return item;
+ case PLACE_SOME:
+ item = event.getView().getCursor().clone();
+ item.setAmount(item.getAmount() / 2);
+ return item;
+ case HOTBAR_SWAP:
+ return event.getView().getBottomInventory().getItem(event.getHotbarButton());
+ default:
+ return clicked.getItem(event.getSlot());
+ }
+ }
+
+
+ public static Map<Integer, ItemStack> deduceChangesIfItemAdded(Inventory inventory, ItemStack added, boolean computeNewItem) {
+ int addedAmount = added.getAmount();
+ Map<Integer, ItemStack> rv = Collections.emptyMap();
+
+ for (int n = inventory.getSize(), i = 0; i < n; i++) {
+ if (addedAmount <= 0) break;
+
+ ItemStack current = inventory.getItem(i);
+ if (current == null || current.getType() == Material.AIR || current.isSimilar(added)) {
+ int count = current == null ? 0 : current.getAmount();
+ int max = (current == null ? added : current).getType().getMaxStackSize();
+ if (count < max) {
+ int diff = max - count;
+ if (diff > addedAmount) {
+ diff = addedAmount;
+ }
+ addedAmount -= diff;
+
+ if (rv.isEmpty()) rv = new LinkedHashMap<>();
+
+ if (computeNewItem) {
+ current = (current == null ? added : current).clone();
+ current.setAmount(count + diff);
+ rv.put(i, current);
+ } else {
+ rv.put(i, null);
+ }
+ }
+ }
+ }
+
+ return rv;
+ }
+
+
+ public static ItemStack getNewHeldItemIfPickedUp(PlayerInventory inventory, ItemStack added) {
+ int heldItemSlot = inventory.getHeldItemSlot();
+ ItemStack heldItem = inventory.getItem(heldItemSlot);
+
+ if (SpigotUtil.isItemPresent(heldItem) && !added.isSimilar(heldItem)) {
+ return null;
+ }
+
+ int amt = added.getAmount();
+ for (int i = 0; i < heldItemSlot; i++) {
+ ItemStack item = inventory.getItem(i);
+ if (!SpigotUtil.isItemPresent(item)) {
+ return null;
+ }
+ if (item.isSimilar(item)) {
+ amt -= Math.max(0, item.getMaxStackSize() - item.getAmount());
+ if (amt <= 0) {
+ return null;
+ }
+ }
+ }
+
+ added = added.clone();
+ added.setAmount(amt + Math.max(0, heldItem == null ? 0 : heldItem.getAmount()));
+ return added;
+ }
+
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/Logging.java b/dicore3/core/src/main/java/io/dico/dicore/Logging.java
index fa2a676..50c4b52 100644
--- a/dicore3/core/src/main/java/io/dico/dicore/Logging.java
+++ b/dicore3/core/src/main/java/io/dico/dicore/Logging.java
@@ -1,116 +1,116 @@
-package io.dico.dicore;
-
-import java.util.logging.Logger;
-
-public interface Logging {
-
- void info(Object o);
-
- void warn(Object o);
-
- void error(Object o);
-
- void debug(Object o);
-
- void setDebugging(boolean debugging);
-
- boolean isDebugging();
-
- class RootLogging implements Logging {
- private final String prefix;
- private final Logger root;
- private boolean debugging;
-
- public RootLogging(String prefix, Logger root, boolean debugging) {
- this.root = root;
- this.prefix = prefix;
- this.debugging = debugging;
- }
-
- @Override
- public void info(Object o) {
- root.info(prefix(o));
- }
-
- @Override
- public void warn(Object o) {
- root.warning(prefix(o));
- }
-
- @Override
- public void error(Object o) {
- root.severe(prefix(o));
- }
-
- @Override
- public void debug(Object o) {
- if (debugging) {
- root.info(String.format("[DEBUG] %s", prefix(o)));
- }
- }
-
- @Override
- public boolean isDebugging() {
- return debugging;
- }
-
- @Override
- public void setDebugging(boolean debugging) {
- this.debugging = debugging;
- }
-
- private String prefix(Object o) {
- return String.format("[%s] %s", prefix, String.valueOf(o));
- }
- }
-
- class SubLogging implements Logging {
- protected String prefix;
- private final Logging superLogger;
- private boolean debugging;
-
- public SubLogging(String prefix, Logging superLogger, boolean debugging) {
- this.superLogger = superLogger;
- this.prefix = prefix;
- this.debugging = debugging;
- }
-
- @Override
- public void info(Object o) {
- superLogger.info(prefix(o));
- }
-
- @Override
- public void warn(Object o) {
- superLogger.warn(prefix(o));
- }
-
- @Override
- public void error(Object o) {
- superLogger.error(prefix(o));
- }
-
- @Override
- public void debug(Object o) {
- if (debugging) {
- superLogger.info(String.format("[DEBUG] %s", prefix(o)));
- }
- }
-
- @Override
- public boolean isDebugging() {
- return debugging;
- }
-
- @Override
- public void setDebugging(boolean debugging) {
- this.debugging = debugging;
- }
-
- private String prefix(Object o) {
- return String.format("[%s] %s", prefix, String.valueOf(o));
- }
-
- }
-
-}
+package io.dico.dicore;
+
+import java.util.logging.Logger;
+
+public interface Logging {
+
+ void info(Object o);
+
+ void warn(Object o);
+
+ void error(Object o);
+
+ void debug(Object o);
+
+ void setDebugging(boolean debugging);
+
+ boolean isDebugging();
+
+ class RootLogging implements Logging {
+ private final String prefix;
+ private final Logger root;
+ private boolean debugging;
+
+ public RootLogging(String prefix, Logger root, boolean debugging) {
+ this.root = root;
+ this.prefix = prefix;
+ this.debugging = debugging;
+ }
+
+ @Override
+ public void info(Object o) {
+ root.info(prefix(o));
+ }
+
+ @Override
+ public void warn(Object o) {
+ root.warning(prefix(o));
+ }
+
+ @Override
+ public void error(Object o) {
+ root.severe(prefix(o));
+ }
+
+ @Override
+ public void debug(Object o) {
+ if (debugging) {
+ root.info(String.format("[DEBUG] %s", prefix(o)));
+ }
+ }
+
+ @Override
+ public boolean isDebugging() {
+ return debugging;
+ }
+
+ @Override
+ public void setDebugging(boolean debugging) {
+ this.debugging = debugging;
+ }
+
+ private String prefix(Object o) {
+ return String.format("[%s] %s", prefix, String.valueOf(o));
+ }
+ }
+
+ class SubLogging implements Logging {
+ protected String prefix;
+ private final Logging superLogger;
+ private boolean debugging;
+
+ public SubLogging(String prefix, Logging superLogger, boolean debugging) {
+ this.superLogger = superLogger;
+ this.prefix = prefix;
+ this.debugging = debugging;
+ }
+
+ @Override
+ public void info(Object o) {
+ superLogger.info(prefix(o));
+ }
+
+ @Override
+ public void warn(Object o) {
+ superLogger.warn(prefix(o));
+ }
+
+ @Override
+ public void error(Object o) {
+ superLogger.error(prefix(o));
+ }
+
+ @Override
+ public void debug(Object o) {
+ if (debugging) {
+ superLogger.info(String.format("[DEBUG] %s", prefix(o)));
+ }
+ }
+
+ @Override
+ public boolean isDebugging() {
+ return debugging;
+ }
+
+ @Override
+ public void setDebugging(boolean debugging) {
+ this.debugging = debugging;
+ }
+
+ private String prefix(Object o) {
+ return String.format("[%s] %s", prefix, String.valueOf(o));
+ }
+
+ }
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/Reflection.java b/dicore3/core/src/main/java/io/dico/dicore/Reflection.java
index b7b1ed4..2452d75 100644
--- a/dicore3/core/src/main/java/io/dico/dicore/Reflection.java
+++ b/dicore3/core/src/main/java/io/dico/dicore/Reflection.java
@@ -1,780 +1,780 @@
-package io.dico.dicore;
-
-import io.dico.dicore.exceptions.ExceptionHandler;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.Objects;
-import java.util.function.Consumer;
-
-/**
- * Reflective utilities
- */
-@SuppressWarnings("unchecked")
-public class Reflection {
- private static final ExceptionHandler exceptionHandler;
- private static final Field fieldModifiersField = restrictedSearchField(Field.class, "modifiers");
- private static Consumer<String> errorTarget;
-
- private Reflection() {
-
- }
-
- static {
- exceptionHandler = new ExceptionHandler() {
- @Override
- public void handle(Throwable ex) {
- handleGenericException(ex);
- }
-
- @Override
- public Object handleGenericException(Throwable ex, Object... args) {
- String action = args.length == 0 || !(args[0] instanceof String) ? "executing a reflective operation" : (String) args[0];
- ExceptionHandler.log(errorTarget, action, ex);
- return null;
- }
- };
-
- // don't use method reference here: the current reference in System.out would be cached.
- setErrorTarget(msg -> System.out.println(msg));
- }
-
- /**
- * Sets the output where ReflectiveOperationException's and similar are sent.
- * This defaults to {@link System#out}.
- *
- * @param target The new output
- * @throws NullPointerException if target is null
- */
- public static void setErrorTarget(Consumer<String> target) {
- errorTarget = Objects.requireNonNull(target);
- }
-
- /**
- * This search modifier tells the implementation that it should subsequently search superclasses for the field/method.
- * Using this modifier means a call to {@link #deepSearchField(Class, String)} will be used instead of {@link #restrictedSearchField(Class, String)}
- * and a call to {@link #deepSearchMethod(Class, String, Class[])} will be used instead of {@link #restrictedSearchMethod(Class, String, Class[])}
- */
- public static final int DEEP_SEARCH = 0x1;
-
- /**
- * This search modifier applies only to fields, and tells the implementation that a final modifier might be present on a found field, and that it should be removed.
- */
- public static final int REMOVE_FINAL = 0x2;
-
- /**
- * This search modifier applies only to methods, and tells the implementation that it should completely ignore parameter types and return the first method with a matching name
- * The implementation uses {@link Class#getDeclaredMethods()} instead of {@link Class#getDeclaredMethod(String, Class[])} if this modifier is set.
- */
- public static final int IGNORE_PARAMS = 0x2;
-
- /*
- ### FIELD METHODS ###
- */
-
- /**
- * Search a field of any accessibility within the class or any of its superclasses.
- * The first field with the given name that is found will be returned.
- * <p>
- * If a field is found and it is not accessible, this method attempts to make it accessible.
- * If a {@link SecurityException} is thrown in the process, that is ignored and the field will be returned nonetheless.
- * <p>
- * This method throws IllegalArgumentException if the Field is not found, because, in most cases, that should never happen,
- * and it should simplify debugging. In some cases, if you want to know if the field exists, you'll have to use try/catch for that.
- *
- * @param clazz The lowest class in the ladder to start searching from
- * @param fieldName The name of the field
- * //@param fieldType the type of the field, or null if it can be any.
- * @return The field
- * @throws NullPointerException if clazz is null or fieldName is null
- * @throws IllegalArgumentException if the field doesn't exist
- * @see #restrictedSearchField(Class, String)
- */
- public static Field deepSearchField(Class<?> clazz, String fieldName/*, Class<?> fieldType*/) {
- Class<?> currentClass = clazz;
- Field result;
- do {
- // throws NPE if class or fieldName is null
- result = internalSearchField(clazz, fieldName);
- if (result != null) {
- return result;
- }
- currentClass = currentClass.getSuperclass();
- } while (currentClass != null);
-
- throw new IllegalArgumentException("field not found in " + clazz.getCanonicalName() + " and superclasses: " + fieldName);
- }
-
- /**
- * Search a field of any accessibility within the class, but not its superclasses.
- * <p>
- * If a field is found and it is not accessible, this method attempts to make it accessible.
- * If a {@link SecurityException} is thrown in the process, that is ignored and the field will be returned nonetheless.
- * <p>
- * This method throws IllegalArgumentException if the Field is not found, because, in most cases, that should never happen,
- * and it should simplify debugging. In some cases, if you want to know if the field exists, you'll have to use try/catch for that.
- *
- * @param clazz The only class to search for the field
- * @param fieldName The name of the field
- * @return The field
- * @throws NullPointerException if clazz or fieldName is null
- * @throws IllegalArgumentException if the field does not exist
- */
- public static Field restrictedSearchField(Class<?> clazz, String fieldName) {
- Field result = internalSearchField(clazz, fieldName);
- if (result == null) {
- throw new IllegalArgumentException("field not found in " + clazz.getCanonicalName() + ": " + fieldName);
- }
- return result;
- }
-
- /**
- * Searches for a field using the given search method.
- *
- * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL}
- * @param clazz The class to search in/from
- * @param fieldName Name of the field
- * @return The field
- * @throws NullPointerException if clazz or fieldName is null
- * @throws IllegalArgumentException if the field is not found
- */
- public static Field searchField(int modifiers, Class<?> clazz, String fieldName) {
- Field result;
- if ((modifiers & DEEP_SEARCH) != 0) {
- result = deepSearchField(clazz, fieldName);
- } else {
- result = restrictedSearchField(clazz, fieldName);
- }
- if ((modifiers & REMOVE_FINAL) != 0) {
- removeFinalModifier(result);
- }
- return result;
- }
-
- /**
- * @return The same as {@link #restrictedSearchField(Class, String)}, but returns null instead of throwing IllegalArgumentException
- * @see #restrictedSearchField(Class, String)
- */
- private static Field internalSearchField(Class<?> clazz, String fieldName) {
- Field result;
- try {
- // throws NullPointerException if either clazz or fieldName are null.
- result = clazz.getDeclaredField(fieldName);
- } catch (NoSuchFieldException | SecurityException ex) {
- return null;
- }
-
- if (!result.isAccessible()) try {
- result.setAccessible(true);
- } catch (SecurityException ignored) {
-
- }
-
- return result;
- }
-
- /**
- * Attempts to remove existing final modifier of the given field
- * This method should always return true.
- *
- * @param field The field whose final modifier to remove
- * @return true if the field most definitely has no final modifier after this call
- * @throws NullPointerException if field is null
- */
- public static boolean removeFinalModifier(Field field) {
- Objects.requireNonNull(field);
- try {
- int modifiers = (int) fieldModifiersField.get(field);
- if (modifiers != (modifiers &= ~Modifier.FINAL)) {
- fieldModifiersField.set(field, modifiers);
- }
- return true;
- } catch (Exception ex) {
- return false;
- }
- }
-
- /**
- * Gets field value of the field named fieldName and the given instance
- * To find the field, {@link #deepSearchField(Class, String)} is used (DEEP search method).
- * <p>
- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
- *
- * @param instance The instance whose field value to get
- * @param fieldName the name of the field
- * @param <T> The expected/known field type
- * @return The field value
- * @throws IllegalArgumentException if the field doesn't exist
- * @see #deepSearchField(Class, String)
- * @see #getFieldValue(Class, String, Object)
- */
- public static <T> T getFieldValue(Object instance, String fieldName) {
- return getFieldValue(deepSearchField(instance.getClass(), fieldName), instance);
- }
-
- /**
- * Gets field value of the field named fieldName and the given instance
- * To find the field, {@link #restrictedSearchField(Class, String)} is used (RESTRICTED search method).
- * <p>
- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
- *
- * @param clazz The class to search for the field
- * @param instance The instance whose field value to get
- * @param fieldName the name of the field
- * @param <T> The expected/known field type
- * @return The field value
- * @throws IllegalArgumentException if the field doesn't exist
- * @see #restrictedSearchField(Class, String)
- * @see #getFieldValue(Field, Object)
- */
- public static <T> T getFieldValue(Class<?> clazz, String fieldName, Object instance) {
- return getFieldValue(restrictedSearchField(clazz, fieldName), instance);
- }
-
- /**
- * Gets field value of the field named fieldName and the given instance
- * To find the field, {@link #searchField(int, Class, String)} is used.
- * <p>
- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
- *
- * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL}
- * @param clazz The class to search for the field
- * @param instance The instance whose field value to get
- * @param fieldName the name of the field
- * @param <T> The expected/known field type
- * @return The field value
- * @throws IllegalArgumentException if the field doesn't exist
- * @see #searchField(int, Class, String)
- * @see #getFieldValue(Field, Object)
- */
- public static <T> T getFieldValue(int modifiers, Class<?> clazz, String fieldName, Object instance) {
- return getFieldValue(searchField(modifiers, clazz, fieldName), instance);
- }
-
- /**
- * Gets field value of the given field and the given instance
- * <p>
- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
- *
- * @param field the field
- * @param instance The instance whose field value to get
- * @param <T> The expected/known field type
- * @return The field value
- */
- public static <T> T getFieldValue(Field field, Object instance) {
- return exceptionHandler.supplySafe(() -> (T) field.get(instance));
- }
-
- /**
- * Gets static field value of the field named fieldName
- * To find the field, {@link #restrictedSearchField(Class, String)} is used (RESTRICTED search method).
- * <p>
- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
- *
- * @param clazz The class to search for the field
- * @param fieldName the name of the field
- * @param <T> The expected/known field type
- * @return The field value
- * @throws IllegalArgumentException if the field doesn't exist
- * @see #restrictedSearchField(Class, String)
- * @see #getStaticFieldValue(Field)
- */
- public static <T> T getStaticFieldValue(Class<?> clazz, String fieldName) {
- return getStaticFieldValue(restrictedSearchField(clazz, fieldName));
- }
-
- /**
- * Gets static field value of the field named fieldName
- * To find the field, {@link #searchField(int, Class, String)} is used.
- * <p>
- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
- *
- * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL}
- * @param clazz The class to search for the field
- * @param fieldName the name of the field
- * @param <T> The expected/known field type
- * @return The field value
- * @throws IllegalArgumentException if the field doesn't exist
- * @see #deepSearchField(Class, String)
- * @see #getStaticFieldValue(Field)
- */
- public static <T> T getStaticFieldValue(int modifiers, Class<?> clazz, String fieldName) {
- return getStaticFieldValue(searchField(modifiers, clazz, fieldName));
- }
-
- /**
- * Gets static field value
- * <p>
- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
- * <p>
- * Equivalent to the call {@code getFieldValue(field, (Object) null)}
- *
- * @param field the field
- * @param <T> The expected/known field type
- * @return The field value
- * @see #getFieldValue(Field, Object)
- */
- public static <T> T getStaticFieldValue(Field field) {
- return getFieldValue(field, (Object) null);
- }
-
- /**
- * Sets field value of the field named fieldName and the given instance
- * To find the field, {@link #deepSearchField(Class, String)} is used (DEEP search method).
- * <p>
- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
- *
- * @param instance The instance whose field value to set
- * @param fieldName the name of the field
- * @param newValue the new field value
- * @throws IllegalArgumentException if the field doesn't exist
- * @see #deepSearchField(Class, String)
- * @see #setFieldValue(Class, String, Object, Object)
- */
- public static void setFieldValue(Object instance, String fieldName, Object newValue) {
- setFieldValue(deepSearchField(instance.getClass(), fieldName), instance, newValue);
- }
-
- /**
- * Sets field value of the field named fieldName and the given instance
- * To find the field, {@link #restrictedSearchField(Class, String)} is used (RESTRICTED search method).
- * <p>
- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
- *
- * @param clazz The class to search for the field
- * @param fieldName the name of the field
- * @param instance The field owner
- * @param newValue The new field value
- * @throws IllegalArgumentException if the field doesn't exist
- * @see #restrictedSearchField(Class, String)
- * @see #setFieldValue(Field, Object, Object)
- */
- public static void setFieldValue(Class<?> clazz, String fieldName, Object instance, Object newValue) {
- setFieldValue(restrictedSearchField(clazz, fieldName), instance, newValue);
- }
-
- /**
- * Sets field value of the field named fieldName and the given instance
- * To find the field, {@link #searchField(int, Class, String)} is used.
- * <p>
- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
- *
- * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL}
- * @param clazz The class to search for the field
- * @param instance The instance whose field value to set
- * @param fieldName the name of the field
- * @param newValue The new field value
- * @throws IllegalArgumentException if the field doesn't exist
- * @see #searchField(int, Class, String)
- * @see #setFieldValue(Field, Object, Object)
- */
- public static void setFieldValue(int modifiers, Class<?> clazz, String fieldName, Object instance, Object newValue) {
- setFieldValue(searchField(modifiers, clazz, fieldName), instance, newValue);
- }
-
- /**
- * Sets a field value
- * <p>
- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
- *
- * @param field The field
- * @param instance The field owner
- * @param newValue The new field value
- */
- public static void setFieldValue(Field field, Object instance, Object newValue) {
- exceptionHandler.runSafe(() -> field.set(instance, newValue));
- }
-
- /**
- * Sets static field value of the field name fieldName
- * To find the field, {@link #restrictedSearchField(Class, String)} is used (RESTRICTED search method).
- * <p>
- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
- *
- * @param clazz The class to search for the field
- * @param fieldName the name of the field
- * @param newValue The new field value
- * @throws IllegalArgumentException if the field doesn't exist
- * @see #restrictedSearchField(Class, String)
- * @see #setStaticFieldValue(Field, Object)
- */
- public static void setStaticFieldValue(Class<?> clazz, String fieldName, Object newValue) {
- setStaticFieldValue(restrictedSearchField(clazz, fieldName), newValue);
- }
-
- /**
- * Sets static field value of the field named fieldName
- * To find the field, {@link #searchField(int, Class, String)} is used.
- * <p>
- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
- *
- * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL}
- * @param clazz The class to search for the field
- * @param fieldName the name of the field
- * @param newValue The new field value
- * @throws IllegalArgumentException if the field doesn't exist
- * @see #searchField(int, Class, String)
- * @see #setStaticFieldValue(Field, Object)
- */
- public static void setStaticFieldValue(int modifiers, Class<?> clazz, String fieldName, Object newValue) {
- setStaticFieldValue(searchField(modifiers, clazz, fieldName), newValue);
- }
-
- /**
- * Sets a static field value
- * <p>
- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
- *
- * @param field The field
- * @param newValue The new field value
- */
- public static void setStaticFieldValue(Field field, Object newValue) {
- setFieldValue(field, (Object) null, newValue);
- }
-
- /*
- ### METHOD METHODS ###
- */
-
- /**
- * Search a method of any accessibility within the class or any of its superclasses.
- * The first method with the given name that is found will be returned.
- * <p>
- * If a method is found and it is not accessible, this method attempts to make it accessible.
- * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless.
- * <p>
- * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen,
- * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that.
- *
- * @param clazz The lowest class in the ladder to start searching from
- * @param methodName The name of the method
- * @param parameterTypes the parameter types of the sought method.
- * @return The method
- * @throws NullPointerException if clazz is null or methodName is null
- * @throws IllegalArgumentException if the method doesn't exist
- * @see #restrictedSearchMethod(Class, String, Class[])
- */
- public static Method deepSearchMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) {
- return deepSearchMethod(0, clazz, methodName, parameterTypes);
- }
-
- /**
- * Search a method of any accessibility within the class or any of its superclasses.
- * The first method with the given name that is found will be returned.
- * <p>
- * If a method is found and it is not accessible, this method attempts to make it accessible.
- * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless.
- * <p>
- * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen,
- * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that.
- *
- * @param modifiers The modifiers for method search. Can have {@link #IGNORE_PARAMS}
- * @param clazz The lowest class in the ladder to start searching from
- * @param methodName The name of the method
- * @param parameterTypes the parameter types of the sought method.
- * @return The method
- * @throws NullPointerException if clazz is null or methodName is null
- * @throws IllegalArgumentException if the method doesn't exist
- * @see #restrictedSearchMethod(Class, String, Class[])
- */
- public static Method deepSearchMethod(int modifiers, Class<?> clazz, String methodName, Class<?>... parameterTypes) {
- Class<?> currentClass = clazz;
- Method result;
- do {
- // throws NPE if class or methodName is null
- result = internalSearchMethod(modifiers, currentClass, methodName, parameterTypes);
- if (result != null) {
- return result;
- }
- currentClass = currentClass.getSuperclass();
- } while (currentClass != null);
-
- throw new IllegalArgumentException("method not found in " + clazz.getCanonicalName() + " and superclasses: " + methodName);
- }
-
- /**
- * Search a method of any accessibility within the class, but not its superclasses.
- * <p>
- * If a method is found and it is not accessible, this method attempts to make it accessible.
- * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless.
- * <p>
- * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen,
- * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that.
- *
- * @param clazz The only class to search for the method
- * @param methodName The name of the method
- * @param parameterTypes the parameter types of the sought method.
- * @return The method
- * @throws NullPointerException if clazz or methodName is null
- * @throws IllegalArgumentException if the method does not exist
- */
- public static Method restrictedSearchMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) {
- return restrictedSearchMethod(0, clazz, methodName, parameterTypes);
- }
-
- /**
- * Search a method of any accessibility within the class, but not its superclasses.
- * <p>
- * If a method is found and it is not accessible, this method attempts to make it accessible.
- * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless.
- * <p>
- * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen,
- * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that.
- *
- * @param modifiers The modifiers for method search. Can have {@link #IGNORE_PARAMS}
- * @param clazz The only class to search for the method
- * @param methodName The name of the method
- * @param parameterTypes the parameter types of the sought method.
- * @return The method
- * @throws NullPointerException if clazz or methodName is null
- * @throws IllegalArgumentException if the method does not exist
- */
- public static Method restrictedSearchMethod(int modifiers, Class<?> clazz, String methodName, Class<?>... parameterTypes) {
- Method result = internalSearchMethod(modifiers, clazz, methodName, parameterTypes);
- if (result == null) {
- throw new IllegalArgumentException("method not found in " + clazz.getCanonicalName() + ": " + methodName);
- }
- return result;
- }
-
- /**
- * Searches for a method using the given search method.
- * <p>
- * If a method is found and it is not accessible, this method attempts to make it accessible.
- * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless.
- * <p>
- * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen,
- * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that.
- *
- * @param modifiers The modifiers for method search. Can have {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS}
- * @param clazz The class to search in/from
- * @param methodName Name of the method
- * @param parameterTypes the parameter types of the sought method.
- * @return The method
- * @throws NullPointerException if clazz or methodName is null
- * @throws IllegalArgumentException if the method is not found
- */
- public static Method searchMethod(int modifiers, Class<?> clazz, String methodName, Class<?>... parameterTypes) {
- if ((modifiers & DEEP_SEARCH) != 0) {
- return deepSearchMethod(modifiers, clazz, methodName, parameterTypes);
- } else {
- return restrictedSearchMethod(modifiers, clazz, methodName, parameterTypes);
- }
- }
-
- /**
- * @return The same as {@link #restrictedSearchMethod(Class, String, Class[]) }, but returns null instead of throwing IllegalArgumentException
- * @see #restrictedSearchMethod(Class, String, Class[])
- */
- private static Method internalSearchMethod(int modifiers, Class<?> clazz, String methodName, Class<?>... parameterTypes) {
- Method result = null;
-
- if ((modifiers & IGNORE_PARAMS) != 0) {
-
- // throws NullPointerException if either clazz or methodName are null.
- methodName = methodName.intern();
- for (Method method : clazz.getDeclaredMethods()) {
- // all method names are interned. Identity comparison is much faster.
- if (method.getName() == methodName) {
- result = method;
- break;
- }
- }
-
- if (result == null) {
- return null;
- }
-
- } else {
-
- try {
- // throws NullPointerException if either clazz or methodName are null.
- result = clazz.getDeclaredMethod(methodName, parameterTypes);
- } catch (NoSuchMethodException | SecurityException ex) {
- return null;
- }
-
- }
-
- if (!result.isAccessible()) try {
- result.setAccessible(true);
- } catch (SecurityException ignored) {
-
- }
-
- return result;
- }
-
- /**
- * Invokes the method named methodName with the given instance and arguments
- * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters,
- * modifiers {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS}, and the class {@link Object#getClass() instance.getClass()}
- * <p>
- * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar,
- * and call {@link #invokeMethod(Method, Object, Object...)}
- * <p>
- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
- *
- * @param methodName Name of the method
- * @param instance The instance to invoke the method on
- * @param args The arguments to use in the method call
- * @param <T> The expected/known method return type
- * @return The result of calling the method
- * @throws NullPointerException if instance or methodName is null
- * @throws IllegalArgumentException if the method is not found
- * @see #invokeMethod(Method, Object, Object...)
- */
- public static <T> T invokeMethod(Object instance, String methodName, Object... args) {
- return invokeMethod(searchMethod(DEEP_SEARCH | IGNORE_PARAMS, instance.getClass(), methodName), instance, args);
- }
-
- /**
- * Invokes the method named methodName with the given instance and arguments
- * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters,
- * as well as the modifier {@link #IGNORE_PARAMS}
- * <p>
- * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar,
- * and call {@link #invokeMethod(Method, Object, Object...)}
- * <p>
- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
- *
- * @param clazz The class to search in/from
- * @param methodName Name of the method
- * @param instance The instance to invoke the method on
- * @param args The arguments to use in the method call
- * @param <T> The expected/known method return type
- * @return The result of calling the method
- * @throws NullPointerException if clazz or methodName is null
- * @throws IllegalArgumentException if the method is not found
- * @see #invokeMethod(Method, Object, Object...)
- */
- public static <T> T invokeMethod(Class<?> clazz, String methodName, Object instance, Object... args) {
- return invokeMethod(searchMethod(IGNORE_PARAMS, clazz, methodName), instance, args);
- }
-
- /**
- * Invokes the method named methodName with the given instance and arguments
- * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters.
- * For this search, the result of calling {@link Object#getClass() instance.getClass()} is used.
- * <p>
- * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar,
- * and call {@link #invokeMethod(Method, Object, Object...)}
- * <p>
- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
- *
- * @param modifiers The modifiers for method search. Can have {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS}
- * @param methodName Name of the method
- * @param instance The instance to invoke the method on
- * @param args The arguments to use in the method call
- * @param <T> The expected/known method return type
- * @return The result of calling the method
- * @throws NullPointerException if instance or methodName is null
- * @throws IllegalArgumentException if the method is not found
- * @see #invokeMethod(Method, Object, Object...)
- */
- public static <T> T invokeMethod(int modifiers, Object instance, String methodName, Object... args) {
- return invokeMethod(searchMethod(modifiers, instance.getClass(), methodName), instance, args);
- }
-
- /**
- * Invokes the method named methodName with the given instance and arguments
- * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters.
- * <p>
- * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar,
- * and call {@link #invokeMethod(Method, Object, Object...)}
- * <p>
- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
- *
- * @param modifiers The modifiers for method search. Can have {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS}
- * @param clazz The class to search in/from
- * @param methodName Name of the method
- * @param instance The instance to invoke the method on
- * @param args The arguments to use in the method call
- * @param <T> The expected/known method return type
- * @return The result of calling the method
- * @throws NullPointerException if clazz or methodName is null
- * @throws IllegalArgumentException if the method is not found
- * @see #invokeMethod(Method, Object, Object...)
- */
- public static <T> T invokeMethod(int modifiers, Class<?> clazz, String methodName, Object instance, Object... args) {
- return invokeMethod(searchMethod(modifiers, clazz, methodName), instance, args);
- }
-
- /**
- * Invokes the method with the given instance and arguments
- * <p>
- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
- *
- * @param method The method to invoke
- * @param instance The instance to invoke the method on
- * @param args The arguments to use in the method call
- * @param <T> The expected/known method return type
- * @return The result of calling the method
- */
- public static <T> T invokeMethod(Method method, Object instance, Object... args) {
- return exceptionHandler.supplySafe(() -> (T) method.invoke(instance, args));
- }
-
- /**
- * Invokes the static method named methodName with the given arguments
- * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters,
- * as well as the modifier {@link #IGNORE_PARAMS}
- * <p>
- * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar,
- * and call {@link #invokeMethod(Method, Object, Object...)}
- * <p>
- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
- *
- * @param clazz The class to search in/from
- * @param methodName Name of the method
- * @param args The arguments to use in the method call
- * @param <T> The expected/known method return type
- * @return The result of calling the method
- * @throws NullPointerException if clazz or methodName is null
- * @throws IllegalArgumentException if the method is not found
- * @see #invokeStaticMethod(Method, Object...)
- */
- public static <T> T invokeStaticMethod(Class<?> clazz, String methodName, Object... args) {
- return invokeStaticMethod(searchMethod(IGNORE_PARAMS, clazz, methodName), args);
- }
-
- /**
- * Invokes the static method named methodName with the given arguments
- * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters.
- * <p>
- * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar,
- * and call {@link #invokeMethod(Method, Object, Object...)}
- * <p>
- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
- *
- * @param modifiers The modifiers for method search. Can have {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS}
- * @param clazz The class to search in/from
- * @param methodName Name of the method
- * @param args The arguments to use in the method call
- * @param <T> The expected/known method return type
- * @return The result of calling the method
- * @throws NullPointerException if clazz or methodName is null
- * @throws IllegalArgumentException if the method is not found
- * @see #invokeStaticMethod(Method, Object...)
- */
- public static <T> T invokeStaticMethod(int modifiers, Class<?> clazz, String methodName, Object... args) {
- return invokeStaticMethod(searchMethod(modifiers, clazz, methodName), args);
- }
-
- /**
- * Invokes the static method with the given arguments
- * <p>
- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
- *
- * @param method The method to invoke
- * @param args The arguments to use in the method call
- * @param <T> The expected/known method return type
- * @return The result of calling the method
- * @see #invokeMethod(Method, Object, Object...)
- */
- public static <T> T invokeStaticMethod(Method method, Object... args) {
- return invokeMethod(method, (Object) null, args);
- }
-
-}
+package io.dico.dicore;
+
+import io.dico.dicore.exceptions.ExceptionHandler;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Objects;
+import java.util.function.Consumer;
+
+/**
+ * Reflective utilities
+ */
+@SuppressWarnings("unchecked")
+public class Reflection {
+ private static final ExceptionHandler exceptionHandler;
+ private static final Field fieldModifiersField = restrictedSearchField(Field.class, "modifiers");
+ private static Consumer<String> errorTarget;
+
+ private Reflection() {
+
+ }
+
+ static {
+ exceptionHandler = new ExceptionHandler() {
+ @Override
+ public void handle(Throwable ex) {
+ handleGenericException(ex);
+ }
+
+ @Override
+ public Object handleGenericException(Throwable ex, Object... args) {
+ String action = args.length == 0 || !(args[0] instanceof String) ? "executing a reflective operation" : (String) args[0];
+ ExceptionHandler.log(errorTarget, action, ex);
+ return null;
+ }
+ };
+
+ // don't use method reference here: the current reference in System.out would be cached.
+ setErrorTarget(msg -> System.out.println(msg));
+ }
+
+ /**
+ * Sets the output where ReflectiveOperationException's and similar are sent.
+ * This defaults to {@link System#out}.
+ *
+ * @param target The new output
+ * @throws NullPointerException if target is null
+ */
+ public static void setErrorTarget(Consumer<String> target) {
+ errorTarget = Objects.requireNonNull(target);
+ }
+
+ /**
+ * This search modifier tells the implementation that it should subsequently search superclasses for the field/method.
+ * Using this modifier means a call to {@link #deepSearchField(Class, String)} will be used instead of {@link #restrictedSearchField(Class, String)}
+ * and a call to {@link #deepSearchMethod(Class, String, Class[])} will be used instead of {@link #restrictedSearchMethod(Class, String, Class[])}
+ */
+ public static final int DEEP_SEARCH = 0x1;
+
+ /**
+ * This search modifier applies only to fields, and tells the implementation that a final modifier might be present on a found field, and that it should be removed.
+ */
+ public static final int REMOVE_FINAL = 0x2;
+
+ /**
+ * This search modifier applies only to methods, and tells the implementation that it should completely ignore parameter types and return the first method with a matching name
+ * The implementation uses {@link Class#getDeclaredMethods()} instead of {@link Class#getDeclaredMethod(String, Class[])} if this modifier is set.
+ */
+ public static final int IGNORE_PARAMS = 0x2;
+
+ /*
+ ### FIELD METHODS ###
+ */
+
+ /**
+ * Search a field of any accessibility within the class or any of its superclasses.
+ * The first field with the given name that is found will be returned.
+ * <p>
+ * If a field is found and it is not accessible, this method attempts to make it accessible.
+ * If a {@link SecurityException} is thrown in the process, that is ignored and the field will be returned nonetheless.
+ * <p>
+ * This method throws IllegalArgumentException if the Field is not found, because, in most cases, that should never happen,
+ * and it should simplify debugging. In some cases, if you want to know if the field exists, you'll have to use try/catch for that.
+ *
+ * @param clazz The lowest class in the ladder to start searching from
+ * @param fieldName The name of the field
+ * //@param fieldType the type of the field, or null if it can be any.
+ * @return The field
+ * @throws NullPointerException if clazz is null or fieldName is null
+ * @throws IllegalArgumentException if the field doesn't exist
+ * @see #restrictedSearchField(Class, String)
+ */
+ public static Field deepSearchField(Class<?> clazz, String fieldName/*, Class<?> fieldType*/) {
+ Class<?> currentClass = clazz;
+ Field result;
+ do {
+ // throws NPE if class or fieldName is null
+ result = internalSearchField(clazz, fieldName);
+ if (result != null) {
+ return result;
+ }
+ currentClass = currentClass.getSuperclass();
+ } while (currentClass != null);
+
+ throw new IllegalArgumentException("field not found in " + clazz.getCanonicalName() + " and superclasses: " + fieldName);
+ }
+
+ /**
+ * Search a field of any accessibility within the class, but not its superclasses.
+ * <p>
+ * If a field is found and it is not accessible, this method attempts to make it accessible.
+ * If a {@link SecurityException} is thrown in the process, that is ignored and the field will be returned nonetheless.
+ * <p>
+ * This method throws IllegalArgumentException if the Field is not found, because, in most cases, that should never happen,
+ * and it should simplify debugging. In some cases, if you want to know if the field exists, you'll have to use try/catch for that.
+ *
+ * @param clazz The only class to search for the field
+ * @param fieldName The name of the field
+ * @return The field
+ * @throws NullPointerException if clazz or fieldName is null
+ * @throws IllegalArgumentException if the field does not exist
+ */
+ public static Field restrictedSearchField(Class<?> clazz, String fieldName) {
+ Field result = internalSearchField(clazz, fieldName);
+ if (result == null) {
+ throw new IllegalArgumentException("field not found in " + clazz.getCanonicalName() + ": " + fieldName);
+ }
+ return result;
+ }
+
+ /**
+ * Searches for a field using the given search method.
+ *
+ * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL}
+ * @param clazz The class to search in/from
+ * @param fieldName Name of the field
+ * @return The field
+ * @throws NullPointerException if clazz or fieldName is null
+ * @throws IllegalArgumentException if the field is not found
+ */
+ public static Field searchField(int modifiers, Class<?> clazz, String fieldName) {
+ Field result;
+ if ((modifiers & DEEP_SEARCH) != 0) {
+ result = deepSearchField(clazz, fieldName);
+ } else {
+ result = restrictedSearchField(clazz, fieldName);
+ }
+ if ((modifiers & REMOVE_FINAL) != 0) {
+ removeFinalModifier(result);
+ }
+ return result;
+ }
+
+ /**
+ * @return The same as {@link #restrictedSearchField(Class, String)}, but returns null instead of throwing IllegalArgumentException
+ * @see #restrictedSearchField(Class, String)
+ */
+ private static Field internalSearchField(Class<?> clazz, String fieldName) {
+ Field result;
+ try {
+ // throws NullPointerException if either clazz or fieldName are null.
+ result = clazz.getDeclaredField(fieldName);
+ } catch (NoSuchFieldException | SecurityException ex) {
+ return null;
+ }
+
+ if (!result.isAccessible()) try {
+ result.setAccessible(true);
+ } catch (SecurityException ignored) {
+
+ }
+
+ return result;
+ }
+
+ /**
+ * Attempts to remove existing final modifier of the given field
+ * This method should always return true.
+ *
+ * @param field The field whose final modifier to remove
+ * @return true if the field most definitely has no final modifier after this call
+ * @throws NullPointerException if field is null
+ */
+ public static boolean removeFinalModifier(Field field) {
+ Objects.requireNonNull(field);
+ try {
+ int modifiers = (int) fieldModifiersField.get(field);
+ if (modifiers != (modifiers &= ~Modifier.FINAL)) {
+ fieldModifiersField.set(field, modifiers);
+ }
+ return true;
+ } catch (Exception ex) {
+ return false;
+ }
+ }
+
+ /**
+ * Gets field value of the field named fieldName and the given instance
+ * To find the field, {@link #deepSearchField(Class, String)} is used (DEEP search method).
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param instance The instance whose field value to get
+ * @param fieldName the name of the field
+ * @param <T> The expected/known field type
+ * @return The field value
+ * @throws IllegalArgumentException if the field doesn't exist
+ * @see #deepSearchField(Class, String)
+ * @see #getFieldValue(Class, String, Object)
+ */
+ public static <T> T getFieldValue(Object instance, String fieldName) {
+ return getFieldValue(deepSearchField(instance.getClass(), fieldName), instance);
+ }
+
+ /**
+ * Gets field value of the field named fieldName and the given instance
+ * To find the field, {@link #restrictedSearchField(Class, String)} is used (RESTRICTED search method).
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param clazz The class to search for the field
+ * @param instance The instance whose field value to get
+ * @param fieldName the name of the field
+ * @param <T> The expected/known field type
+ * @return The field value
+ * @throws IllegalArgumentException if the field doesn't exist
+ * @see #restrictedSearchField(Class, String)
+ * @see #getFieldValue(Field, Object)
+ */
+ public static <T> T getFieldValue(Class<?> clazz, String fieldName, Object instance) {
+ return getFieldValue(restrictedSearchField(clazz, fieldName), instance);
+ }
+
+ /**
+ * Gets field value of the field named fieldName and the given instance
+ * To find the field, {@link #searchField(int, Class, String)} is used.
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL}
+ * @param clazz The class to search for the field
+ * @param instance The instance whose field value to get
+ * @param fieldName the name of the field
+ * @param <T> The expected/known field type
+ * @return The field value
+ * @throws IllegalArgumentException if the field doesn't exist
+ * @see #searchField(int, Class, String)
+ * @see #getFieldValue(Field, Object)
+ */
+ public static <T> T getFieldValue(int modifiers, Class<?> clazz, String fieldName, Object instance) {
+ return getFieldValue(searchField(modifiers, clazz, fieldName), instance);
+ }
+
+ /**
+ * Gets field value of the given field and the given instance
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param field the field
+ * @param instance The instance whose field value to get
+ * @param <T> The expected/known field type
+ * @return The field value
+ */
+ public static <T> T getFieldValue(Field field, Object instance) {
+ return exceptionHandler.supplySafe(() -> (T) field.get(instance));
+ }
+
+ /**
+ * Gets static field value of the field named fieldName
+ * To find the field, {@link #restrictedSearchField(Class, String)} is used (RESTRICTED search method).
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param clazz The class to search for the field
+ * @param fieldName the name of the field
+ * @param <T> The expected/known field type
+ * @return The field value
+ * @throws IllegalArgumentException if the field doesn't exist
+ * @see #restrictedSearchField(Class, String)
+ * @see #getStaticFieldValue(Field)
+ */
+ public static <T> T getStaticFieldValue(Class<?> clazz, String fieldName) {
+ return getStaticFieldValue(restrictedSearchField(clazz, fieldName));
+ }
+
+ /**
+ * Gets static field value of the field named fieldName
+ * To find the field, {@link #searchField(int, Class, String)} is used.
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL}
+ * @param clazz The class to search for the field
+ * @param fieldName the name of the field
+ * @param <T> The expected/known field type
+ * @return The field value
+ * @throws IllegalArgumentException if the field doesn't exist
+ * @see #deepSearchField(Class, String)
+ * @see #getStaticFieldValue(Field)
+ */
+ public static <T> T getStaticFieldValue(int modifiers, Class<?> clazz, String fieldName) {
+ return getStaticFieldValue(searchField(modifiers, clazz, fieldName));
+ }
+
+ /**
+ * Gets static field value
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ * <p>
+ * Equivalent to the call {@code getFieldValue(field, (Object) null)}
+ *
+ * @param field the field
+ * @param <T> The expected/known field type
+ * @return The field value
+ * @see #getFieldValue(Field, Object)
+ */
+ public static <T> T getStaticFieldValue(Field field) {
+ return getFieldValue(field, (Object) null);
+ }
+
+ /**
+ * Sets field value of the field named fieldName and the given instance
+ * To find the field, {@link #deepSearchField(Class, String)} is used (DEEP search method).
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param instance The instance whose field value to set
+ * @param fieldName the name of the field
+ * @param newValue the new field value
+ * @throws IllegalArgumentException if the field doesn't exist
+ * @see #deepSearchField(Class, String)
+ * @see #setFieldValue(Class, String, Object, Object)
+ */
+ public static void setFieldValue(Object instance, String fieldName, Object newValue) {
+ setFieldValue(deepSearchField(instance.getClass(), fieldName), instance, newValue);
+ }
+
+ /**
+ * Sets field value of the field named fieldName and the given instance
+ * To find the field, {@link #restrictedSearchField(Class, String)} is used (RESTRICTED search method).
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param clazz The class to search for the field
+ * @param fieldName the name of the field
+ * @param instance The field owner
+ * @param newValue The new field value
+ * @throws IllegalArgumentException if the field doesn't exist
+ * @see #restrictedSearchField(Class, String)
+ * @see #setFieldValue(Field, Object, Object)
+ */
+ public static void setFieldValue(Class<?> clazz, String fieldName, Object instance, Object newValue) {
+ setFieldValue(restrictedSearchField(clazz, fieldName), instance, newValue);
+ }
+
+ /**
+ * Sets field value of the field named fieldName and the given instance
+ * To find the field, {@link #searchField(int, Class, String)} is used.
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL}
+ * @param clazz The class to search for the field
+ * @param instance The instance whose field value to set
+ * @param fieldName the name of the field
+ * @param newValue The new field value
+ * @throws IllegalArgumentException if the field doesn't exist
+ * @see #searchField(int, Class, String)
+ * @see #setFieldValue(Field, Object, Object)
+ */
+ public static void setFieldValue(int modifiers, Class<?> clazz, String fieldName, Object instance, Object newValue) {
+ setFieldValue(searchField(modifiers, clazz, fieldName), instance, newValue);
+ }
+
+ /**
+ * Sets a field value
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param field The field
+ * @param instance The field owner
+ * @param newValue The new field value
+ */
+ public static void setFieldValue(Field field, Object instance, Object newValue) {
+ exceptionHandler.runSafe(() -> field.set(instance, newValue));
+ }
+
+ /**
+ * Sets static field value of the field name fieldName
+ * To find the field, {@link #restrictedSearchField(Class, String)} is used (RESTRICTED search method).
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param clazz The class to search for the field
+ * @param fieldName the name of the field
+ * @param newValue The new field value
+ * @throws IllegalArgumentException if the field doesn't exist
+ * @see #restrictedSearchField(Class, String)
+ * @see #setStaticFieldValue(Field, Object)
+ */
+ public static void setStaticFieldValue(Class<?> clazz, String fieldName, Object newValue) {
+ setStaticFieldValue(restrictedSearchField(clazz, fieldName), newValue);
+ }
+
+ /**
+ * Sets static field value of the field named fieldName
+ * To find the field, {@link #searchField(int, Class, String)} is used.
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL}
+ * @param clazz The class to search for the field
+ * @param fieldName the name of the field
+ * @param newValue The new field value
+ * @throws IllegalArgumentException if the field doesn't exist
+ * @see #searchField(int, Class, String)
+ * @see #setStaticFieldValue(Field, Object)
+ */
+ public static void setStaticFieldValue(int modifiers, Class<?> clazz, String fieldName, Object newValue) {
+ setStaticFieldValue(searchField(modifiers, clazz, fieldName), newValue);
+ }
+
+ /**
+ * Sets a static field value
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param field The field
+ * @param newValue The new field value
+ */
+ public static void setStaticFieldValue(Field field, Object newValue) {
+ setFieldValue(field, (Object) null, newValue);
+ }
+
+ /*
+ ### METHOD METHODS ###
+ */
+
+ /**
+ * Search a method of any accessibility within the class or any of its superclasses.
+ * The first method with the given name that is found will be returned.
+ * <p>
+ * If a method is found and it is not accessible, this method attempts to make it accessible.
+ * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless.
+ * <p>
+ * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen,
+ * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that.
+ *
+ * @param clazz The lowest class in the ladder to start searching from
+ * @param methodName The name of the method
+ * @param parameterTypes the parameter types of the sought method.
+ * @return The method
+ * @throws NullPointerException if clazz is null or methodName is null
+ * @throws IllegalArgumentException if the method doesn't exist
+ * @see #restrictedSearchMethod(Class, String, Class[])
+ */
+ public static Method deepSearchMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) {
+ return deepSearchMethod(0, clazz, methodName, parameterTypes);
+ }
+
+ /**
+ * Search a method of any accessibility within the class or any of its superclasses.
+ * The first method with the given name that is found will be returned.
+ * <p>
+ * If a method is found and it is not accessible, this method attempts to make it accessible.
+ * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless.
+ * <p>
+ * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen,
+ * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that.
+ *
+ * @param modifiers The modifiers for method search. Can have {@link #IGNORE_PARAMS}
+ * @param clazz The lowest class in the ladder to start searching from
+ * @param methodName The name of the method
+ * @param parameterTypes the parameter types of the sought method.
+ * @return The method
+ * @throws NullPointerException if clazz is null or methodName is null
+ * @throws IllegalArgumentException if the method doesn't exist
+ * @see #restrictedSearchMethod(Class, String, Class[])
+ */
+ public static Method deepSearchMethod(int modifiers, Class<?> clazz, String methodName, Class<?>... parameterTypes) {
+ Class<?> currentClass = clazz;
+ Method result;
+ do {
+ // throws NPE if class or methodName is null
+ result = internalSearchMethod(modifiers, currentClass, methodName, parameterTypes);
+ if (result != null) {
+ return result;
+ }
+ currentClass = currentClass.getSuperclass();
+ } while (currentClass != null);
+
+ throw new IllegalArgumentException("method not found in " + clazz.getCanonicalName() + " and superclasses: " + methodName);
+ }
+
+ /**
+ * Search a method of any accessibility within the class, but not its superclasses.
+ * <p>
+ * If a method is found and it is not accessible, this method attempts to make it accessible.
+ * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless.
+ * <p>
+ * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen,
+ * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that.
+ *
+ * @param clazz The only class to search for the method
+ * @param methodName The name of the method
+ * @param parameterTypes the parameter types of the sought method.
+ * @return The method
+ * @throws NullPointerException if clazz or methodName is null
+ * @throws IllegalArgumentException if the method does not exist
+ */
+ public static Method restrictedSearchMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) {
+ return restrictedSearchMethod(0, clazz, methodName, parameterTypes);
+ }
+
+ /**
+ * Search a method of any accessibility within the class, but not its superclasses.
+ * <p>
+ * If a method is found and it is not accessible, this method attempts to make it accessible.
+ * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless.
+ * <p>
+ * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen,
+ * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that.
+ *
+ * @param modifiers The modifiers for method search. Can have {@link #IGNORE_PARAMS}
+ * @param clazz The only class to search for the method
+ * @param methodName The name of the method
+ * @param parameterTypes the parameter types of the sought method.
+ * @return The method
+ * @throws NullPointerException if clazz or methodName is null
+ * @throws IllegalArgumentException if the method does not exist
+ */
+ public static Method restrictedSearchMethod(int modifiers, Class<?> clazz, String methodName, Class<?>... parameterTypes) {
+ Method result = internalSearchMethod(modifiers, clazz, methodName, parameterTypes);
+ if (result == null) {
+ throw new IllegalArgumentException("method not found in " + clazz.getCanonicalName() + ": " + methodName);
+ }
+ return result;
+ }
+
+ /**
+ * Searches for a method using the given search method.
+ * <p>
+ * If a method is found and it is not accessible, this method attempts to make it accessible.
+ * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless.
+ * <p>
+ * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen,
+ * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that.
+ *
+ * @param modifiers The modifiers for method search. Can have {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS}
+ * @param clazz The class to search in/from
+ * @param methodName Name of the method
+ * @param parameterTypes the parameter types of the sought method.
+ * @return The method
+ * @throws NullPointerException if clazz or methodName is null
+ * @throws IllegalArgumentException if the method is not found
+ */
+ public static Method searchMethod(int modifiers, Class<?> clazz, String methodName, Class<?>... parameterTypes) {
+ if ((modifiers & DEEP_SEARCH) != 0) {
+ return deepSearchMethod(modifiers, clazz, methodName, parameterTypes);
+ } else {
+ return restrictedSearchMethod(modifiers, clazz, methodName, parameterTypes);
+ }
+ }
+
+ /**
+ * @return The same as {@link #restrictedSearchMethod(Class, String, Class[]) }, but returns null instead of throwing IllegalArgumentException
+ * @see #restrictedSearchMethod(Class, String, Class[])
+ */
+ private static Method internalSearchMethod(int modifiers, Class<?> clazz, String methodName, Class<?>... parameterTypes) {
+ Method result = null;
+
+ if ((modifiers & IGNORE_PARAMS) != 0) {
+
+ // throws NullPointerException if either clazz or methodName are null.
+ methodName = methodName.intern();
+ for (Method method : clazz.getDeclaredMethods()) {
+ // all method names are interned. Identity comparison is much faster.
+ if (method.getName() == methodName) {
+ result = method;
+ break;
+ }
+ }
+
+ if (result == null) {
+ return null;
+ }
+
+ } else {
+
+ try {
+ // throws NullPointerException if either clazz or methodName are null.
+ result = clazz.getDeclaredMethod(methodName, parameterTypes);
+ } catch (NoSuchMethodException | SecurityException ex) {
+ return null;
+ }
+
+ }
+
+ if (!result.isAccessible()) try {
+ result.setAccessible(true);
+ } catch (SecurityException ignored) {
+
+ }
+
+ return result;
+ }
+
+ /**
+ * Invokes the method named methodName with the given instance and arguments
+ * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters,
+ * modifiers {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS}, and the class {@link Object#getClass() instance.getClass()}
+ * <p>
+ * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar,
+ * and call {@link #invokeMethod(Method, Object, Object...)}
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param methodName Name of the method
+ * @param instance The instance to invoke the method on
+ * @param args The arguments to use in the method call
+ * @param <T> The expected/known method return type
+ * @return The result of calling the method
+ * @throws NullPointerException if instance or methodName is null
+ * @throws IllegalArgumentException if the method is not found
+ * @see #invokeMethod(Method, Object, Object...)
+ */
+ public static <T> T invokeMethod(Object instance, String methodName, Object... args) {
+ return invokeMethod(searchMethod(DEEP_SEARCH | IGNORE_PARAMS, instance.getClass(), methodName), instance, args);
+ }
+
+ /**
+ * Invokes the method named methodName with the given instance and arguments
+ * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters,
+ * as well as the modifier {@link #IGNORE_PARAMS}
+ * <p>
+ * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar,
+ * and call {@link #invokeMethod(Method, Object, Object...)}
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param clazz The class to search in/from
+ * @param methodName Name of the method
+ * @param instance The instance to invoke the method on
+ * @param args The arguments to use in the method call
+ * @param <T> The expected/known method return type
+ * @return The result of calling the method
+ * @throws NullPointerException if clazz or methodName is null
+ * @throws IllegalArgumentException if the method is not found
+ * @see #invokeMethod(Method, Object, Object...)
+ */
+ public static <T> T invokeMethod(Class<?> clazz, String methodName, Object instance, Object... args) {
+ return invokeMethod(searchMethod(IGNORE_PARAMS, clazz, methodName), instance, args);
+ }
+
+ /**
+ * Invokes the method named methodName with the given instance and arguments
+ * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters.
+ * For this search, the result of calling {@link Object#getClass() instance.getClass()} is used.
+ * <p>
+ * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar,
+ * and call {@link #invokeMethod(Method, Object, Object...)}
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param modifiers The modifiers for method search. Can have {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS}
+ * @param methodName Name of the method
+ * @param instance The instance to invoke the method on
+ * @param args The arguments to use in the method call
+ * @param <T> The expected/known method return type
+ * @return The result of calling the method
+ * @throws NullPointerException if instance or methodName is null
+ * @throws IllegalArgumentException if the method is not found
+ * @see #invokeMethod(Method, Object, Object...)
+ */
+ public static <T> T invokeMethod(int modifiers, Object instance, String methodName, Object... args) {
+ return invokeMethod(searchMethod(modifiers, instance.getClass(), methodName), instance, args);
+ }
+
+ /**
+ * Invokes the method named methodName with the given instance and arguments
+ * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters.
+ * <p>
+ * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar,
+ * and call {@link #invokeMethod(Method, Object, Object...)}
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param modifiers The modifiers for method search. Can have {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS}
+ * @param clazz The class to search in/from
+ * @param methodName Name of the method
+ * @param instance The instance to invoke the method on
+ * @param args The arguments to use in the method call
+ * @param <T> The expected/known method return type
+ * @return The result of calling the method
+ * @throws NullPointerException if clazz or methodName is null
+ * @throws IllegalArgumentException if the method is not found
+ * @see #invokeMethod(Method, Object, Object...)
+ */
+ public static <T> T invokeMethod(int modifiers, Class<?> clazz, String methodName, Object instance, Object... args) {
+ return invokeMethod(searchMethod(modifiers, clazz, methodName), instance, args);
+ }
+
+ /**
+ * Invokes the method with the given instance and arguments
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param method The method to invoke
+ * @param instance The instance to invoke the method on
+ * @param args The arguments to use in the method call
+ * @param <T> The expected/known method return type
+ * @return The result of calling the method
+ */
+ public static <T> T invokeMethod(Method method, Object instance, Object... args) {
+ return exceptionHandler.supplySafe(() -> (T) method.invoke(instance, args));
+ }
+
+ /**
+ * Invokes the static method named methodName with the given arguments
+ * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters,
+ * as well as the modifier {@link #IGNORE_PARAMS}
+ * <p>
+ * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar,
+ * and call {@link #invokeMethod(Method, Object, Object...)}
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param clazz The class to search in/from
+ * @param methodName Name of the method
+ * @param args The arguments to use in the method call
+ * @param <T> The expected/known method return type
+ * @return The result of calling the method
+ * @throws NullPointerException if clazz or methodName is null
+ * @throws IllegalArgumentException if the method is not found
+ * @see #invokeStaticMethod(Method, Object...)
+ */
+ public static <T> T invokeStaticMethod(Class<?> clazz, String methodName, Object... args) {
+ return invokeStaticMethod(searchMethod(IGNORE_PARAMS, clazz, methodName), args);
+ }
+
+ /**
+ * Invokes the static method named methodName with the given arguments
+ * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters.
+ * <p>
+ * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar,
+ * and call {@link #invokeMethod(Method, Object, Object...)}
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param modifiers The modifiers for method search. Can have {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS}
+ * @param clazz The class to search in/from
+ * @param methodName Name of the method
+ * @param args The arguments to use in the method call
+ * @param <T> The expected/known method return type
+ * @return The result of calling the method
+ * @throws NullPointerException if clazz or methodName is null
+ * @throws IllegalArgumentException if the method is not found
+ * @see #invokeStaticMethod(Method, Object...)
+ */
+ public static <T> T invokeStaticMethod(int modifiers, Class<?> clazz, String methodName, Object... args) {
+ return invokeStaticMethod(searchMethod(modifiers, clazz, methodName), args);
+ }
+
+ /**
+ * Invokes the static method with the given arguments
+ * <p>
+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out}
+ *
+ * @param method The method to invoke
+ * @param args The arguments to use in the method call
+ * @param <T> The expected/known method return type
+ * @return The result of calling the method
+ * @see #invokeMethod(Method, Object, Object...)
+ */
+ public static <T> T invokeStaticMethod(Method method, Object... args) {
+ return invokeMethod(method, (Object) null, args);
+ }
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/SetBasedWhitelist.java b/dicore3/core/src/main/java/io/dico/dicore/SetBasedWhitelist.java
index 6596b65..d4486ed 100644
--- a/dicore3/core/src/main/java/io/dico/dicore/SetBasedWhitelist.java
+++ b/dicore3/core/src/main/java/io/dico/dicore/SetBasedWhitelist.java
@@ -1,59 +1,59 @@
-package io.dico.dicore;
-
-import org.bukkit.configuration.ConfigurationSection;
-
-import java.util.*;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
-public class SetBasedWhitelist implements Whitelist {
- private final Set set;
- private final boolean blacklist;
-
- public SetBasedWhitelist(Object[] array, boolean blacklist) {
- this(Arrays.asList(array), blacklist);
- }
-
- public SetBasedWhitelist(ConfigurationSection section, Function<String, ?> parser) {
- this(section.getStringList("listed").stream().map(parser).filter(Objects::nonNull).collect(Collectors.toList()),
- section.getBoolean("blacklist", false));
- }
-
- @SuppressWarnings("unchecked")
- public SetBasedWhitelist(Collection collection, boolean blacklist) {
- Set set;
- if (collection.isEmpty()) {
- set = Collections.emptySet();
- } else if (collection.iterator().next() instanceof Enum) {
- set = EnumSet.copyOf(collection);
- } else if (collection instanceof Set) {
- set = (Set) collection;
- } else {
- set = new HashSet<>(collection);
- }
-
- this.set = set;
- this.blacklist = blacklist;
- }
-
- @Override
- public boolean isWhitelisted(Object o) {
- return blacklist != set.contains(o);
- }
-
- @SuppressWarnings("unchecked")
- @Override
- public String toString() {
- return (blacklist ? "Blacklist" : "Whitelist") + "{"
- + String.join(", ", (CharSequence[]) set.stream().map(String::valueOf).toArray(String[]::new)) + "}";
- }
-
- public Set getSet() {
- return set;
- }
-
- public boolean isBlacklist() {
- return blacklist;
- }
-
-}
+package io.dico.dicore;
+
+import org.bukkit.configuration.ConfigurationSection;
+
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+public class SetBasedWhitelist implements Whitelist {
+ private final Set set;
+ private final boolean blacklist;
+
+ public SetBasedWhitelist(Object[] array, boolean blacklist) {
+ this(Arrays.asList(array), blacklist);
+ }
+
+ public SetBasedWhitelist(ConfigurationSection section, Function<String, ?> parser) {
+ this(section.getStringList("listed").stream().map(parser).filter(Objects::nonNull).collect(Collectors.toList()),
+ section.getBoolean("blacklist", false));
+ }
+
+ @SuppressWarnings("unchecked")
+ public SetBasedWhitelist(Collection collection, boolean blacklist) {
+ Set set;
+ if (collection.isEmpty()) {
+ set = Collections.emptySet();
+ } else if (collection.iterator().next() instanceof Enum) {
+ set = EnumSet.copyOf(collection);
+ } else if (collection instanceof Set) {
+ set = (Set) collection;
+ } else {
+ set = new HashSet<>(collection);
+ }
+
+ this.set = set;
+ this.blacklist = blacklist;
+ }
+
+ @Override
+ public boolean isWhitelisted(Object o) {
+ return blacklist != set.contains(o);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public String toString() {
+ return (blacklist ? "Blacklist" : "Whitelist") + "{"
+ + String.join(", ", (CharSequence[]) set.stream().map(String::valueOf).toArray(String[]::new)) + "}";
+ }
+
+ public Set getSet() {
+ return set;
+ }
+
+ public boolean isBlacklist() {
+ return blacklist;
+ }
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/SpigotUtil.java b/dicore3/core/src/main/java/io/dico/dicore/SpigotUtil.java
index 44659fa..afcae02 100644
--- a/dicore3/core/src/main/java/io/dico/dicore/SpigotUtil.java
+++ b/dicore3/core/src/main/java/io/dico/dicore/SpigotUtil.java
@@ -1,408 +1,408 @@
-package io.dico.dicore;
-
-import org.bukkit.Bukkit;
-import org.bukkit.Location;
-import org.bukkit.Material;
-import org.bukkit.World;
-import org.bukkit.block.Block;
-import org.bukkit.block.BlockFace;
-import org.bukkit.block.BlockState;
-import org.bukkit.configuration.serialization.ConfigurationSerializable;
-import org.bukkit.entity.*;
-import org.bukkit.inventory.Inventory;
-import org.bukkit.inventory.InventoryHolder;
-import org.bukkit.inventory.ItemStack;
-import org.bukkit.material.Attachable;
-import org.bukkit.material.MaterialData;
-import org.bukkit.projectiles.ProjectileSource;
-
-import java.util.*;
-import java.util.function.BiConsumer;
-import java.util.function.Consumer;
-import java.util.function.Predicate;
-
-public class SpigotUtil {
-
- private SpigotUtil() {
- throw new UnsupportedOperationException();
- }
-
- public static World matchWorld(String input) {
- try {
- UUID uid = UUID.fromString(input);
- World world = Bukkit.getWorld(uid);
- if (world != null) {
- return world;
- }
- } catch (IllegalArgumentException ignored) {
- }
-
- World result = Bukkit.getWorld(input);
- if (result == null) {
- input = input.toLowerCase().replace("_", "").replaceAll("[-_]", "");
- for (World world : Bukkit.getWorlds()) {
- if (world.getName().toLowerCase().equals(input)) {
- result = world;
- break;
- }
- }
- }
-
- return result;
- }
-
- public static Block getSupportingBlock(Block block) {
- MaterialData data = block.getState().getData();
- if (data instanceof Attachable) {
- BlockFace attachedOn = ((Attachable) data).getAttachedFace();
- return block.getRelative(attachedOn);
- }
- return null;
- }
-
- public static boolean isItemPresent(ItemStack stack) {
- return stack != null && stack.getType() != Material.AIR && stack.getAmount() > 0;
- }
-
- public static boolean removeItems(Inventory from, ItemStack item, int amount) {
- for (Map.Entry<Integer, ? extends ItemStack> entry : from.all(item.getType()).entrySet()) {
- ItemStack stack = entry.getValue();
- if (item.isSimilar(stack)) {
- amount -= stack.getAmount();
- int stackAmount = -Math.min(0, amount);
- if (stackAmount == 0) {
- from.setItem(entry.getKey(), null);
- } else {
- stack.setAmount(stackAmount);
- }
- }
- }
- return amount <= 0;
- }
-
- public static BlockFace yawToFace(float yaw) {
- if ((yaw %= 360) < 0)
- yaw += 360;
- if (45 <= yaw && yaw < 135)
- return BlockFace.WEST;
- if (135 <= yaw && yaw < 225)
- return BlockFace.NORTH;
- if (225 <= yaw && yaw < 315)
- return BlockFace.EAST;
- return BlockFace.SOUTH;
- }
-
- public static void addItems(InventoryHolder entity, ItemStack... items) {
- Location dropLocation;
- if (entity instanceof Entity) {
- dropLocation = ((Entity) entity).getLocation();
- } else if (entity instanceof BlockState) {
- dropLocation = ((BlockState) entity).getLocation().add(0.5, 1, 0.5);
- } else {
- throw new IllegalArgumentException("Can't find location of this InventoryHolder: " + entity);
- }
- World world = dropLocation.getWorld();
- for (ItemStack toDrop : entity.getInventory().addItem(items).values()) {
- world.dropItemNaturally(dropLocation, toDrop);
- }
- }
-
- public static String asJsonString(Object object) {
- return asJsonString(null, object, 0);
- }
-
- public static String asJsonString(String key, Object object, int indentation) {
- String indent = new String(new char[indentation * 2]).replace('\0', ' ');
- StringBuilder builder = new StringBuilder(indent);
- if (key != null) {
- builder.append(key).append(": ");
- }
- if (object instanceof ConfigurationSerializable) {
- object = ((ConfigurationSerializable) object).serialize();
- }
- if (object instanceof Map) {
- builder.append("{\n");
- Map<?, ?> map = (Map) object;
- for (Map.Entry entry : map.entrySet()) {
- builder.append(asJsonString(String.valueOf(entry.getKey()), entry.getValue(), indentation + 1));
- }
- builder.append(indent).append("}");
- } else if (object instanceof List) {
- builder.append("[\n");
- List list = (List) object;
- for (Object entry : list) {
- builder.append(asJsonString(null, entry, indentation + 1));
- }
- builder.append(indent).append("]");
- } else {
- builder.append(String.valueOf(object));
- }
- return builder.append(",\n").toString();
- }
-
- public static String asJsonString(String key, Object object, int indentation, BiConsumer<List<?>, StringBuilder> listHeader) {
- String indent = new String(new char[indentation * 2]).replace('\0', ' ');
- StringBuilder builder = new StringBuilder(indent);
- if (key != null) {
- builder.append(key).append(": ");
- }
- if (object instanceof ConfigurationSerializable) {
- object = ((ConfigurationSerializable) object).serialize();
- }
- if (object instanceof Map) {
- builder.append("{\n");
- Map<?, ?> map = (Map) object;
- for (Map.Entry entry : map.entrySet()) {
- builder.append(asJsonString(String.valueOf(entry.getKey()), entry.getValue(), indentation + 1, listHeader));
- }
- builder.append(indent).append("}");
- } else if (object instanceof List) {
- builder.append("[");
- List list = (List) object;
- listHeader.accept(list, builder);
- builder.append("\n");
- for (Object entry : list) {
- builder.append(asJsonString(null, entry, indentation + 1, listHeader));
- }
- builder.append(indent).append("]");
- } else {
- builder.append(String.valueOf(object));
- }
- return builder.append(",\n").toString();
- }
-
- public static BlockFace estimateDirectionTo(Location from, Location to) {
- double dx = from.getX() - to.getX();
- double dz = from.getZ() - to.getZ();
-
- boolean xGreater = Math.abs(dx) - Math.abs(dz) > 0;
- double f = xGreater ? 2 / Math.abs(dx) : 2 / Math.abs(dz);
- dx *= f;
- dz *= f;
-
- double other = Math.abs(xGreater ? dz : dx);
-
- if (other <= .5) {
- return xGreater ? (dx < 0 ? BlockFace.WEST : BlockFace.EAST) : (dz < 0 ? BlockFace.NORTH : BlockFace.SOUTH);
- }
-
- if (other < 1.5) {
- if (xGreater) {
- return dx < 0 ? (dz < 0 ? BlockFace.WEST_NORTH_WEST : BlockFace.WEST_SOUTH_WEST) : (dz < 0 ? BlockFace.EAST_NORTH_EAST : BlockFace.EAST_SOUTH_EAST);
- }
- return dx < 0 ? (dz < 0 ? BlockFace.NORTH_NORTH_WEST : BlockFace.SOUTH_SOUTH_WEST) : (dz < 0 ? BlockFace.NORTH_NORTH_EAST : BlockFace.SOUTH_SOUTH_EAST);
- }
-
- return dx < 0 ? (dz < 0 ? BlockFace.NORTH_WEST : BlockFace.SOUTH_WEST) : (dz < 0 ? BlockFace.NORTH_EAST : BlockFace.SOUTH_EAST);
- }
-
- public static Entity findEntityFromDamager(Entity damager, EntityType searched) {
- if (damager.getType() == searched) {
- return damager;
- }
-
- if (damager instanceof Projectile) {
- ProjectileSource shooter = ((Projectile) damager).getShooter();
- if (shooter instanceof Entity && ((Entity) shooter).getType() == searched) {
- return (Entity) shooter;
- }
- return null;
- }
-
- if (damager.getType() == EntityType.PRIMED_TNT) {
- Entity source = ((TNTPrimed) damager).getSource();
- if (source.getType() == searched) {
- return source;
- }
- }
-
- return null;
- }
-
- public static int xpForNextLevel(int currentLevel) {
- if (currentLevel >= 30) {
- return 112 + (currentLevel - 30) * 9;
- }
-
- if (currentLevel >= 15) {
- return 37 + (currentLevel - 15) * 5;
- }
-
- return 7 + currentLevel * 2;
- }
-
- public static int removeExp(Player entity, int xp) {
- int total = entity.getTotalExperience();
- if (xp > total) {
- xp = total;
- }
-
- int level = entity.getLevel();
- if (level < 0) {
- return 0;
- }
-
- int removed = 0;
- int xpForNextLevel = xpForNextLevel(level);
- int current = (int) entity.getExp() * xpForNextLevel;
-
- if (xp > current) {
- xp -= current;
- total -= current;
- removed += current;
-
- if (level == 0) {
- entity.setExp(0F);
- entity.setTotalExperience(total);
- return removed;
- }
- } else {
- current -= xp;
- total -= xp;
- removed += xp;
-
- entity.setExp((float) current / xpForNextLevel);
- entity.setTotalExperience(total);
- return removed;
- }
-
- do {
- xpForNextLevel = xpForNextLevel(--level);
- if (xpForNextLevel >= xp) {
- total -= xp;
- removed += xp;
-
- entity.setExp(1F / xpForNextLevel * (xpForNextLevel - xp));
- entity.setTotalExperience(total);
- entity.setLevel(level);
- return removed;
- }
-
- xp -= xpForNextLevel;
- total -= xpForNextLevel;
- removed += xpForNextLevel;
- } while (level > 0);
-
- entity.setExp(0F);
- entity.setTotalExperience(0);
- entity.setLevel(0);
- return removed;
- }
-
- public static int getTotalExp(Player entity) {
- int rv = 0;
- int level = Math.min(entity.getLevel(), 20000);
- for (int i = 0; i < level; i++) {
- rv += xpForNextLevel(i);
- }
- rv += Math.min(1F, Math.max(0F, entity.getExp())) * xpForNextLevel(level);
- return rv;
- }
-
- public static double getTotalExpLevels(Player entity) {
- int xp = entity.getTotalExperience();
-
- int level = 0;
- while (xp > 0 && level < 20000) {
- int needed = xpForNextLevel(level);
- if (needed > xp) {
- return level + ((double) xp / needed);
- }
- xp -= needed;
- level++;
- }
-
- return level;
- }
-
- public static int getNearbyPlayerCount(Player origin, double range, Predicate<Player> predicate) {
- List<Entity> entities = origin.getNearbyEntities(range, range, range);
- int result = 0;
- for (Entity entity : entities) {
- if (entity.getType() == EntityType.PLAYER && predicate.test((Player) entity)) {
- result++;
- }
- }
- return result;
- }
-
- public static void getNearbyPlayers(Player origin, double range, Collection<Player> collection, Predicate<Player> predicate) {
- List<Entity> entities = origin.getNearbyEntities(range, range, range);
- for (Entity entity : entities) {
- if (entity.getType() == EntityType.PLAYER && predicate.test((Player) entity)) {
- collection.add((Player) entity);
- }
- }
- }
-
- public static void forEachNearbyPlayer(Player origin, double range, Consumer<Player> action) {
- List<Entity> entities = origin.getNearbyEntities(range, range, range);
- for (Entity entity : entities) {
- if (entity.getType() == EntityType.PLAYER) {
- action.accept((Player) entity);
- }
- }
- }
-
- public static double distanceSquared(Location first, Location second) {
- double dx = first.getX() - second.getX();
- double dy = first.getY() - second.getY();
- double dz = first.getZ() - second.getZ();
-
- return dx * dx + dy * dy + dz * dz;
- }
-
-
- public static <T extends Entity> Iterator<T> findNearbyEntities(Entity origin, boolean includeSelf, Predicate<Entity> predicate, double horizontalRange, double verticalRange) {
- Objects.requireNonNull(origin);
- return new Iterator<T>() {
- Entity next;
- List<Entity> nearby;
- int index = 0;
- int size;
-
- {
- if (includeSelf) {
- next = origin;
- } else {
- next = findNext();
- }
- }
-
- Entity findNext() {
- if (nearby == null) {
- nearby = origin.getNearbyEntities(horizontalRange, verticalRange, horizontalRange);
- size = nearby.size();
- }
-
- while (index < size) {
- Entity e = nearby.get(index++);
- if (predicate.test(e)) {
- return e;
- }
- }
-
- return null;
- }
-
- @Override
- public boolean hasNext() {
- return next != null;
- }
-
- @Override
- public T next() {
- if (next == null) {
- throw new NoSuchElementException();
- }
- Entity result = next;
- next = findNext();
- //noinspection unchecked
- return (T) result;
- }
-
- };
- }
-
-
-}
+package io.dico.dicore;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.World;
+import org.bukkit.block.Block;
+import org.bukkit.block.BlockFace;
+import org.bukkit.block.BlockState;
+import org.bukkit.configuration.serialization.ConfigurationSerializable;
+import org.bukkit.entity.*;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.InventoryHolder;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.material.Attachable;
+import org.bukkit.material.MaterialData;
+import org.bukkit.projectiles.ProjectileSource;
+
+import java.util.*;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+public class SpigotUtil {
+
+ private SpigotUtil() {
+ throw new UnsupportedOperationException();
+ }
+
+ public static World matchWorld(String input) {
+ try {
+ UUID uid = UUID.fromString(input);
+ World world = Bukkit.getWorld(uid);
+ if (world != null) {
+ return world;
+ }
+ } catch (IllegalArgumentException ignored) {
+ }
+
+ World result = Bukkit.getWorld(input);
+ if (result == null) {
+ input = input.toLowerCase().replace("_", "").replaceAll("[-_]", "");
+ for (World world : Bukkit.getWorlds()) {
+ if (world.getName().toLowerCase().equals(input)) {
+ result = world;
+ break;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ public static Block getSupportingBlock(Block block) {
+ MaterialData data = block.getState().getData();
+ if (data instanceof Attachable) {
+ BlockFace attachedOn = ((Attachable) data).getAttachedFace();
+ return block.getRelative(attachedOn);
+ }
+ return null;
+ }
+
+ public static boolean isItemPresent(ItemStack stack) {
+ return stack != null && stack.getType() != Material.AIR && stack.getAmount() > 0;
+ }
+
+ public static boolean removeItems(Inventory from, ItemStack item, int amount) {
+ for (Map.Entry<Integer, ? extends ItemStack> entry : from.all(item.getType()).entrySet()) {
+ ItemStack stack = entry.getValue();
+ if (item.isSimilar(stack)) {
+ amount -= stack.getAmount();
+ int stackAmount = -Math.min(0, amount);
+ if (stackAmount == 0) {
+ from.setItem(entry.getKey(), null);
+ } else {
+ stack.setAmount(stackAmount);
+ }
+ }
+ }
+ return amount <= 0;
+ }
+
+ public static BlockFace yawToFace(float yaw) {
+ if ((yaw %= 360) < 0)
+ yaw += 360;
+ if (45 <= yaw && yaw < 135)
+ return BlockFace.WEST;
+ if (135 <= yaw && yaw < 225)
+ return BlockFace.NORTH;
+ if (225 <= yaw && yaw < 315)
+ return BlockFace.EAST;
+ return BlockFace.SOUTH;
+ }
+
+ public static void addItems(InventoryHolder entity, ItemStack... items) {
+ Location dropLocation;
+ if (entity instanceof Entity) {
+ dropLocation = ((Entity) entity).getLocation();
+ } else if (entity instanceof BlockState) {
+ dropLocation = ((BlockState) entity).getLocation().add(0.5, 1, 0.5);
+ } else {
+ throw new IllegalArgumentException("Can't find location of this InventoryHolder: " + entity);
+ }
+ World world = dropLocation.getWorld();
+ for (ItemStack toDrop : entity.getInventory().addItem(items).values()) {
+ world.dropItemNaturally(dropLocation, toDrop);
+ }
+ }
+
+ public static String asJsonString(Object object) {
+ return asJsonString(null, object, 0);
+ }
+
+ public static String asJsonString(String key, Object object, int indentation) {
+ String indent = new String(new char[indentation * 2]).replace('\0', ' ');
+ StringBuilder builder = new StringBuilder(indent);
+ if (key != null) {
+ builder.append(key).append(": ");
+ }
+ if (object instanceof ConfigurationSerializable) {
+ object = ((ConfigurationSerializable) object).serialize();
+ }
+ if (object instanceof Map) {
+ builder.append("{\n");
+ Map<?, ?> map = (Map) object;
+ for (Map.Entry entry : map.entrySet()) {
+ builder.append(asJsonString(String.valueOf(entry.getKey()), entry.getValue(), indentation + 1));
+ }
+ builder.append(indent).append("}");
+ } else if (object instanceof List) {
+ builder.append("[\n");
+ List list = (List) object;
+ for (Object entry : list) {
+ builder.append(asJsonString(null, entry, indentation + 1));
+ }
+ builder.append(indent).append("]");
+ } else {
+ builder.append(String.valueOf(object));
+ }
+ return builder.append(",\n").toString();
+ }
+
+ public static String asJsonString(String key, Object object, int indentation, BiConsumer<List<?>, StringBuilder> listHeader) {
+ String indent = new String(new char[indentation * 2]).replace('\0', ' ');
+ StringBuilder builder = new StringBuilder(indent);
+ if (key != null) {
+ builder.append(key).append(": ");
+ }
+ if (object instanceof ConfigurationSerializable) {
+ object = ((ConfigurationSerializable) object).serialize();
+ }
+ if (object instanceof Map) {
+ builder.append("{\n");
+ Map<?, ?> map = (Map) object;
+ for (Map.Entry entry : map.entrySet()) {
+ builder.append(asJsonString(String.valueOf(entry.getKey()), entry.getValue(), indentation + 1, listHeader));
+ }
+ builder.append(indent).append("}");
+ } else if (object instanceof List) {
+ builder.append("[");
+ List list = (List) object;
+ listHeader.accept(list, builder);
+ builder.append("\n");
+ for (Object entry : list) {
+ builder.append(asJsonString(null, entry, indentation + 1, listHeader));
+ }
+ builder.append(indent).append("]");
+ } else {
+ builder.append(String.valueOf(object));
+ }
+ return builder.append(",\n").toString();
+ }
+
+ public static BlockFace estimateDirectionTo(Location from, Location to) {
+ double dx = from.getX() - to.getX();
+ double dz = from.getZ() - to.getZ();
+
+ boolean xGreater = Math.abs(dx) - Math.abs(dz) > 0;
+ double f = xGreater ? 2 / Math.abs(dx) : 2 / Math.abs(dz);
+ dx *= f;
+ dz *= f;
+
+ double other = Math.abs(xGreater ? dz : dx);
+
+ if (other <= .5) {
+ return xGreater ? (dx < 0 ? BlockFace.WEST : BlockFace.EAST) : (dz < 0 ? BlockFace.NORTH : BlockFace.SOUTH);
+ }
+
+ if (other < 1.5) {
+ if (xGreater) {
+ return dx < 0 ? (dz < 0 ? BlockFace.WEST_NORTH_WEST : BlockFace.WEST_SOUTH_WEST) : (dz < 0 ? BlockFace.EAST_NORTH_EAST : BlockFace.EAST_SOUTH_EAST);
+ }
+ return dx < 0 ? (dz < 0 ? BlockFace.NORTH_NORTH_WEST : BlockFace.SOUTH_SOUTH_WEST) : (dz < 0 ? BlockFace.NORTH_NORTH_EAST : BlockFace.SOUTH_SOUTH_EAST);
+ }
+
+ return dx < 0 ? (dz < 0 ? BlockFace.NORTH_WEST : BlockFace.SOUTH_WEST) : (dz < 0 ? BlockFace.NORTH_EAST : BlockFace.SOUTH_EAST);
+ }
+
+ public static Entity findEntityFromDamager(Entity damager, EntityType searched) {
+ if (damager.getType() == searched) {
+ return damager;
+ }
+
+ if (damager instanceof Projectile) {
+ ProjectileSource shooter = ((Projectile) damager).getShooter();
+ if (shooter instanceof Entity && ((Entity) shooter).getType() == searched) {
+ return (Entity) shooter;
+ }
+ return null;
+ }
+
+ if (damager.getType() == EntityType.PRIMED_TNT) {
+ Entity source = ((TNTPrimed) damager).getSource();
+ if (source.getType() == searched) {
+ return source;
+ }
+ }
+
+ return null;
+ }
+
+ public static int xpForNextLevel(int currentLevel) {
+ if (currentLevel >= 30) {
+ return 112 + (currentLevel - 30) * 9;
+ }
+
+ if (currentLevel >= 15) {
+ return 37 + (currentLevel - 15) * 5;
+ }
+
+ return 7 + currentLevel * 2;
+ }
+
+ public static int removeExp(Player entity, int xp) {
+ int total = entity.getTotalExperience();
+ if (xp > total) {
+ xp = total;
+ }
+
+ int level = entity.getLevel();
+ if (level < 0) {
+ return 0;
+ }
+
+ int removed = 0;
+ int xpForNextLevel = xpForNextLevel(level);
+ int current = (int) entity.getExp() * xpForNextLevel;
+
+ if (xp > current) {
+ xp -= current;
+ total -= current;
+ removed += current;
+
+ if (level == 0) {
+ entity.setExp(0F);
+ entity.setTotalExperience(total);
+ return removed;
+ }
+ } else {
+ current -= xp;
+ total -= xp;
+ removed += xp;
+
+ entity.setExp((float) current / xpForNextLevel);
+ entity.setTotalExperience(total);
+ return removed;
+ }
+
+ do {
+ xpForNextLevel = xpForNextLevel(--level);
+ if (xpForNextLevel >= xp) {
+ total -= xp;
+ removed += xp;
+
+ entity.setExp(1F / xpForNextLevel * (xpForNextLevel - xp));
+ entity.setTotalExperience(total);
+ entity.setLevel(level);
+ return removed;
+ }
+
+ xp -= xpForNextLevel;
+ total -= xpForNextLevel;
+ removed += xpForNextLevel;
+ } while (level > 0);
+
+ entity.setExp(0F);
+ entity.setTotalExperience(0);
+ entity.setLevel(0);
+ return removed;
+ }
+
+ public static int getTotalExp(Player entity) {
+ int rv = 0;
+ int level = Math.min(entity.getLevel(), 20000);
+ for (int i = 0; i < level; i++) {
+ rv += xpForNextLevel(i);
+ }
+ rv += Math.min(1F, Math.max(0F, entity.getExp())) * xpForNextLevel(level);
+ return rv;
+ }
+
+ public static double getTotalExpLevels(Player entity) {
+ int xp = entity.getTotalExperience();
+
+ int level = 0;
+ while (xp > 0 && level < 20000) {
+ int needed = xpForNextLevel(level);
+ if (needed > xp) {
+ return level + ((double) xp / needed);
+ }
+ xp -= needed;
+ level++;
+ }
+
+ return level;
+ }
+
+ public static int getNearbyPlayerCount(Player origin, double range, Predicate<Player> predicate) {
+ List<Entity> entities = origin.getNearbyEntities(range, range, range);
+ int result = 0;
+ for (Entity entity : entities) {
+ if (entity.getType() == EntityType.PLAYER && predicate.test((Player) entity)) {
+ result++;
+ }
+ }
+ return result;
+ }
+
+ public static void getNearbyPlayers(Player origin, double range, Collection<Player> collection, Predicate<Player> predicate) {
+ List<Entity> entities = origin.getNearbyEntities(range, range, range);
+ for (Entity entity : entities) {
+ if (entity.getType() == EntityType.PLAYER && predicate.test((Player) entity)) {
+ collection.add((Player) entity);
+ }
+ }
+ }
+
+ public static void forEachNearbyPlayer(Player origin, double range, Consumer<Player> action) {
+ List<Entity> entities = origin.getNearbyEntities(range, range, range);
+ for (Entity entity : entities) {
+ if (entity.getType() == EntityType.PLAYER) {
+ action.accept((Player) entity);
+ }
+ }
+ }
+
+ public static double distanceSquared(Location first, Location second) {
+ double dx = first.getX() - second.getX();
+ double dy = first.getY() - second.getY();
+ double dz = first.getZ() - second.getZ();
+
+ return dx * dx + dy * dy + dz * dz;
+ }
+
+
+ public static <T extends Entity> Iterator<T> findNearbyEntities(Entity origin, boolean includeSelf, Predicate<Entity> predicate, double horizontalRange, double verticalRange) {
+ Objects.requireNonNull(origin);
+ return new Iterator<T>() {
+ Entity next;
+ List<Entity> nearby;
+ int index = 0;
+ int size;
+
+ {
+ if (includeSelf) {
+ next = origin;
+ } else {
+ next = findNext();
+ }
+ }
+
+ Entity findNext() {
+ if (nearby == null) {
+ nearby = origin.getNearbyEntities(horizontalRange, verticalRange, horizontalRange);
+ size = nearby.size();
+ }
+
+ while (index < size) {
+ Entity e = nearby.get(index++);
+ if (predicate.test(e)) {
+ return e;
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return next != null;
+ }
+
+ @Override
+ public T next() {
+ if (next == null) {
+ throw new NoSuchElementException();
+ }
+ Entity result = next;
+ next = findNext();
+ //noinspection unchecked
+ return (T) result;
+ }
+
+ };
+ }
+
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/StringUtil.java b/dicore3/core/src/main/java/io/dico/dicore/StringUtil.java
index 62a389e..fb76755 100644
--- a/dicore3/core/src/main/java/io/dico/dicore/StringUtil.java
+++ b/dicore3/core/src/main/java/io/dico/dicore/StringUtil.java
@@ -1,473 +1,473 @@
-package io.dico.dicore;
-
-import java.util.*;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.IntStream;
-
-public class StringUtil {
-
- public static String capitalize(String input) {
- if (input.length() > 0) {
- char first = input.charAt(0);
- if (first != (first = Character.toUpperCase(first))) {
- char[] result = input.toCharArray();
- result[0] = first;
- return String.valueOf(result);
- }
- }
- return input;
- }
-
- /**
- * Capitalizes the first character of the string or the first character of each word
- *
- * @param input the string to capitalize
- * @param spaceChar the character separating each word. If @code '\0' is passed, only the first character of
- * the input is capitalized.
- * @return the capitalized string
- */
- public static String capitalize(String input, char spaceChar) {
- if (spaceChar == '\0') {
- return capitalize(input);
- }
-
- char[] result = null;
- boolean capitalize = true;
- for (int n = input.length(), i = 0; i < n; i++) {
- char c = input.charAt(i);
- if (capitalize && c != (c = Character.toUpperCase(c))) {
- if (result == null) result = input.toCharArray();
- result[i] = c;
- }
- capitalize = c == spaceChar;
- }
- return result != null ? String.valueOf(result) : input;
- }
-
- public static String capitalize(String input, char spaceChar, char newSpaceChar) {
- if (newSpaceChar == '\0') {
- return capitalize(input, spaceChar);
- }
-
- char[] result = null;
- boolean capitalize = true;
- for (int n = input.length(), i = 0; i < n; i++) {
- char c = input.charAt(i);
- if (capitalize && c != (c = Character.toUpperCase(c))) {
- if (result == null) result = input.toCharArray();
- result[i] = c;
- }
- if (capitalize = c == spaceChar) {
- if (result == null) result = input.toCharArray();
- result[i] = newSpaceChar;
- }
- }
- return result != null ? String.valueOf(result) : input;
- }
-
- /**
- * Returns a lowercase version of the input with _ replaced with a space.
- * Mainly used for making names of enum constants readable.
- *
- * @param input
- * @return a humanified version of @code input
- */
- public static String humanify(String input) {
- return input == null ? null : input.toLowerCase().replace('_', ' ');
- }
-
- /**
- * Enumerate the given items, separating them by ", " and finally by " and "
- *
- * @param words the items to enumerate (it's not really enumerating....)
- * @return the enumerated string
- */
- public static String enumerate(String... words) {
- StringBuilder result = new StringBuilder();
- int size = words.length;
- int secondLastIndex = size - 2;
- for (int i = 0; i < size; i++) {
- String word = words[i];
- if (word.isEmpty())
- continue;
- result.append(word);
- if (i < secondLastIndex)
- result.append(", ");
- else if (i == secondLastIndex)
- result.append(" and ");
- }
- return result.toString();
- }
-
- public static String enumerate(String list, String regex) {
- return enumerate(list.split(regex));
- }
-
- /**
- * Return a formatted string of the length in millis, containing days, hours and minutes.
- *
- * @param length The delay in milliseconds
- * @return the formatted string
- */
- public static String getTimeLength(long length) {
- int minute = 60000; // in millis
- int hour = 60 * minute;
- int day = 24 * hour;
-
- int minutes = (int) ((length / minute) % 60);
- int hours = (int) ((length / hour) % 24);
- int days = (int) (length / day); //returns floor
-
- String result = ""; // It will be splitted at "|"
- if (days != 0)
- result += days + " days|";
- if (hours != 0)
- result += hours + " hours|";
- if (minutes != 0)
- result += minutes + " minutes|";
- return enumerate(result.split("\\|"));
- }
-
- /**
- * Return a formatted String to represent the given time length, in the given units
- *
- * @param sourceAmount Amount of delay
- * @param sourceUnit Unit of delay
- * @param ifEmpty the String to return if the
- * @param displayedUnits units displayed
- * @return the formatted string
- * @throws IllegalArgumentException if there are no displayed units
- */
- public static String getTimeLength(long sourceAmount, TimeUnit sourceUnit, String ifEmpty, TimeUnit... displayedUnits) {
- if (displayedUnits.length == 0) {
- throw new IllegalArgumentException("No displayed units");
- }
- Arrays.sort(displayedUnits, Collections.reverseOrder(TimeUnit::compareTo)); // sort by opposite of enum declaration order (largest -> smallest)
- List<String> segments = new ArrayList<>(displayedUnits.length);
- for (TimeUnit unit : displayedUnits) {
- long displayedAmount = unit.convert(sourceAmount, sourceUnit);
- sourceAmount -= sourceUnit.convert(displayedAmount, unit);
- if (displayedAmount > 0) {
- String unitWord = unit.name().toLowerCase(); // plural
- if (displayedAmount == 1) {
- unitWord = unitWord.substring(0, unitWord.length() - 1); // remove s at the end
- }
- segments.add(displayedAmount + " " + unitWord);
- }
- }
- return segments.isEmpty() ? ifEmpty : enumerate(segments.toArray(new String[segments.size()]));
- }
-
- /**
- * Returns the delay represented by a ban-like delay representation, in milliseconds
- * Example: "5d2h5m3s" for 5 days, 2 hours, 5 minutes and 3 seconds.
- * <p>
- * Supported characters are s, m, h, d, w.
- * Negative numbers are supported.
- *
- * @param input The input string
- * @return The delay in milliseconds
- * @throws IllegalArgumentException if the input string isn't properly formatted, or any non-digit character isn't recognized (capitals are not recognized).
- */
- public static long getTimeLength(String input) { //if -1: error
- long count = 0;
- int i = 0;
- while (i < input.length()) {
- int num = 0;
- char unit = '\0';
- boolean negate;
- if (negate = input.charAt(i) == '-') {
- i++;
- }
- do {
- char c = input.charAt(i);
- int digit = c - '0';
- if (0 <= digit && digit < 10) {
- num = 10 * num + digit;
- } else {
- unit = c;
- break;
- }
- } while (i < input.length());
-
- long unitTime = getUnitTime(unit);
- if (unitTime == -1)
- throw new IllegalArgumentException();
- if (negate) {
- unitTime = -unitTime;
- }
- count += (num * unitTime);
- }
- return count;
- }
-
- /**
- * Returns the time represented by the given unit character in milliseconds.
- * <p>
- * 's' -> 1000
- * 'm' -> 1000 * 60
- * 'h' -> 1000 * 60 * 60
- * 'd' -> 1000 * 60 * 60 * 24
- * 'w' -> 1000 * 60 * 60 * 24 * 7
- * anything else -> -1
- *
- * @param unit The unit character, as shown above
- * @return the millisecond delay represented by the unit
- */
- public static long getUnitTime(char unit) { //if -1: no value found
- switch (Character.toLowerCase(unit)) {
- case 's':
- return 1000;
- case 'm':
- return 1000 * 60;
- case 'h':
- return 1000 * 60 * 60;
- case 'd':
- return 1000 * 60 * 60 * 24;
- case 'w':
- return 1000 * 60 * 60 * 24 * 7;
- default:
- return -1;
- }
- }
-
- /**
- * Computes a binary representation of the value.
- * The returned representation always displays 64 bits.
- * Every 8 bits, the digits are seperated by an _
- * The representation is prefixed by 0b.
- * <p>
- * Example: 0b00000000_11111111_00000001_11110000_00001111_11001100_00001111_10111010
- *
- * @param entry the value to represent in binary
- * @return A binary representation of the long value
- */
- public static String toBinaryString(long entry) {
- String binary = Long.toBinaryString(entry);
- String binary64 = String.valueOf(new char[64 - binary.length()]).replace('\0', '0') + binary;
- String withUnderscores = String.join("_", IntStream.range(0, 8).mapToObj(x -> binary64.substring(x * 8, x * 8 + 8)).toArray(String[]::new));
- return "0b" + withUnderscores;
- }
-
- /**
- * Turns a generic java classname into a name formatted properly to be an enum constant.
- *
- * @param name The string value I'd describe as a generic java classname (so we have CapitalCase)
- * @return An enum constant version of it (ENUM_FORMAT: CAPITAL_CASE)
- */
- public static String toEnumFormat(String name) {
- StringBuilder result = new StringBuilder(name.length() + 2);
-
- boolean capital = true;
- for (int i = 0, n = name.length(); i < n; i++) {
- char c = name.charAt(i);
- if (capital) {
- capital = Character.isUpperCase(c);
- } else if (Character.isUpperCase(c)) {
- capital = true;
- result.append('_');
- }
- result.append(capital ? c : Character.toUpperCase(c));
- }
-
- return result.toString();
- }
-
- /**
- * Replaces any occurrence of toReplace with another string.
- * Any colours that occured before the occurence of toReplace, are copied to the end of the replacement.
- *
- * @param target The String to query
- * @param toReplace The sequence to replace
- * @param with the replacing sequence
- * @return the result
- */
- public static String replaceKeepColours(String target, String toReplace, String with) {
- int index = -toReplace.length();
- while ((index = target.indexOf(toReplace, index + toReplace.length())) != -1) {
- String start = target.substring(0, index);
- Formatting coloursBefore = Formatting.getFormats(start);
- String after;
- try {
- after = target.substring(index + toReplace.length());
- } catch (IndexOutOfBoundsException e) {
- after = "";
- }
- target = start + with + coloursBefore + after;
- }
- return target;
- }
-
- public static String replParam(String target, String param, Object repl) {
- return replParam(target, param, repl, false);
- }
-
- public static String replParams(String target, String[] params, Object[] repls) {
- return replParams(target, params, repls, false, false);
- }
-
- public static boolean replParams(String[] target, String[] params, Object[] repls) {
- return replParams(target, 0, target.length, params, repls);
- }
-
- public static boolean replParams(String[] target, int from, int to, String[] params, Object[] repls) {
- return replParams(target, from, to, params, repls, false);
- }
-
- public static boolean replParams(List<String> target, String[] params, Object[] repls) {
- return replParams(target, 0, target.size(), params, repls);
- }
-
- public static boolean replParams(List<String> target, int from, int to, String[] params, Object[] repls) {
- return replParams(target, from, to, params, repls, false);
- }
-
- public static String replParamAndTranslate(String target, String param, Object repl) {
- return replParam(target, param, repl, true);
- }
-
- public static String replParamsAndTranslate(String target, String[] params, Object[] repls) {
- return replParams(target, params, repls, false, true);
- }
-
- public static boolean replParamsAndTranslate(String[] target, String[] params, Object[] repls) {
- return replParamsAndTranslate(target, 0, target.length, params, repls);
- }
-
- public static boolean replParamsAndTranslate(String[] target, int from, int to, String[] params, Object[] repls) {
- return replParams(target, from, to, params, repls, true);
- }
-
- public static boolean replParamsAndTranslate(List<String> target, String[] params, Object[] repls) {
- return replParamsAndTranslate(target, 0, target.size(), params, repls);
- }
-
- public static boolean replParamsAndTranslate(List<String> target, int from, int to, String[] params, Object[] repls) {
- return replParams(target, from, to, params, repls, true);
- }
-
- private static String replParam(String target, String param, Object replacementObj, boolean translate) {
- int idx = target.indexOf(param, 0);
- if (idx == -1) {
- return translate ? Formatting.translate(target) : target;
- }
-
- String rep = replacementObj.toString();
- StringBuilder builder = new StringBuilder(target);
- do {
- builder.replace(idx, idx + param.length(), rep);
- idx = builder.indexOf(param, idx + rep.length());
- } while (idx != -1);
-
- if (translate) {
- Formatting.translate(builder);
- }
-
- return builder.toString();
- }
-
- @SuppressWarnings("StringEquality")
- private static boolean replParams(String[] target, int from, int to, String[] params, Object[] repls, boolean translate) {
- if (from < 0 || to < from || to > target.length) {
- throw new IllegalArgumentException("Invalid from-to for array size " + target.length + ": " + from + "-" + to);
- }
-
- boolean change = false;
- for (int i = from; i < to; i++) {
- String val = target[i];
- if (val != (val = replParams(val, params, repls, true, translate))) {
- target[i] = val;
- change = true;
- }
- }
- return change;
- }
-
- @SuppressWarnings("StringEquality")
- private static boolean replParams(List<String> target, int from, int to, String[] params, Object[] repls, boolean translate) {
- if (from < 0 || to < from || to > target.size()) {
- throw new IllegalArgumentException("Invalid from-to for list size " + target.size() + ": " + from + "-" + to);
- }
-
- boolean change = false;
- if (target instanceof RandomAccess) {
- for (int i = from; i < to; i++) {
- String val = target.get(i);
- if (val != (val = replParams(val, params, repls, true, translate))) {
- target.set(i, val);
- change = true;
- }
- }
- } else {
- ListIterator<String> itr = target.listIterator(from);
- for (int n = to - from, i = 0; i < n && itr.hasNext(); i++) {
- String val = itr.next();
- if (val != (val = replParams(val, params, repls, true, translate))) {
- itr.set(val);
- change = true;
- }
- }
- }
- return change;
- }
-
- private static String replParams(String target, String[] params, Object[] repls, boolean updateRepls, boolean translate) {
- int n = params.length;
- if (n != repls.length) {
- throw new IllegalArgumentException();
- }
-
- String param = null;
- int idx = -1;
- int i;
- for (i = 0; i < n; i++) {
- param = params[i];
- if (param == null) {
- continue;
- }
- idx = target.indexOf(param, 0);
- if (idx != -1) {
- break;
- }
- }
-
- if (idx == -1) {
- return translate ? Formatting.translate(target) : target;
- }
-
- String repl = repls[i].toString();
- if (updateRepls) {
- repls[i] = repl;
- }
-
- StringBuilder builder = new StringBuilder(target);
- do {
- builder.replace(idx, idx + param.length(), repl);
- idx = builder.indexOf(param, idx + repl.length());
- } while (idx != -1);
-
- for (i++; i < n; i++) {
- param = params[i];
- if (param == null || (idx = builder.indexOf(param, 0)) == -1) {
- continue;
- }
-
- repl = repls[i].toString();
- if (updateRepls) {
- repls[i] = repl;
- }
-
- do {
- builder.replace(idx, idx + param.length(), repl);
- idx = builder.indexOf(param, idx + repl.length());
- } while (idx != -1);
- }
-
- if (translate) {
- Formatting.translate(builder);
- }
-
- return builder.toString();
- }
-
-}
+package io.dico.dicore;
+
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.IntStream;
+
+public class StringUtil {
+
+ public static String capitalize(String input) {
+ if (input.length() > 0) {
+ char first = input.charAt(0);
+ if (first != (first = Character.toUpperCase(first))) {
+ char[] result = input.toCharArray();
+ result[0] = first;
+ return String.valueOf(result);
+ }
+ }
+ return input;
+ }
+
+ /**
+ * Capitalizes the first character of the string or the first character of each word
+ *
+ * @param input the string to capitalize
+ * @param spaceChar the character separating each word. If @code '\0' is passed, only the first character of
+ * the input is capitalized.
+ * @return the capitalized string
+ */
+ public static String capitalize(String input, char spaceChar) {
+ if (spaceChar == '\0') {
+ return capitalize(input);
+ }
+
+ char[] result = null;
+ boolean capitalize = true;
+ for (int n = input.length(), i = 0; i < n; i++) {
+ char c = input.charAt(i);
+ if (capitalize && c != (c = Character.toUpperCase(c))) {
+ if (result == null) result = input.toCharArray();
+ result[i] = c;
+ }
+ capitalize = c == spaceChar;
+ }
+ return result != null ? String.valueOf(result) : input;
+ }
+
+ public static String capitalize(String input, char spaceChar, char newSpaceChar) {
+ if (newSpaceChar == '\0') {
+ return capitalize(input, spaceChar);
+ }
+
+ char[] result = null;
+ boolean capitalize = true;
+ for (int n = input.length(), i = 0; i < n; i++) {
+ char c = input.charAt(i);
+ if (capitalize && c != (c = Character.toUpperCase(c))) {
+ if (result == null) result = input.toCharArray();
+ result[i] = c;
+ }
+ if (capitalize = c == spaceChar) {
+ if (result == null) result = input.toCharArray();
+ result[i] = newSpaceChar;
+ }
+ }
+ return result != null ? String.valueOf(result) : input;
+ }
+
+ /**
+ * Returns a lowercase version of the input with _ replaced with a space.
+ * Mainly used for making names of enum constants readable.
+ *
+ * @param input
+ * @return a humanified version of @code input
+ */
+ public static String humanify(String input) {
+ return input == null ? null : input.toLowerCase().replace('_', ' ');
+ }
+
+ /**
+ * Enumerate the given items, separating them by ", " and finally by " and "
+ *
+ * @param words the items to enumerate (it's not really enumerating....)
+ * @return the enumerated string
+ */
+ public static String enumerate(String... words) {
+ StringBuilder result = new StringBuilder();
+ int size = words.length;
+ int secondLastIndex = size - 2;
+ for (int i = 0; i < size; i++) {
+ String word = words[i];
+ if (word.isEmpty())
+ continue;
+ result.append(word);
+ if (i < secondLastIndex)
+ result.append(", ");
+ else if (i == secondLastIndex)
+ result.append(" and ");
+ }
+ return result.toString();
+ }
+
+ public static String enumerate(String list, String regex) {
+ return enumerate(list.split(regex));
+ }
+
+ /**
+ * Return a formatted string of the length in millis, containing days, hours and minutes.
+ *
+ * @param length The delay in milliseconds
+ * @return the formatted string
+ */
+ public static String getTimeLength(long length) {
+ int minute = 60000; // in millis
+ int hour = 60 * minute;
+ int day = 24 * hour;
+
+ int minutes = (int) ((length / minute) % 60);
+ int hours = (int) ((length / hour) % 24);
+ int days = (int) (length / day); //returns floor
+
+ String result = ""; // It will be splitted at "|"
+ if (days != 0)
+ result += days + " days|";
+ if (hours != 0)
+ result += hours + " hours|";
+ if (minutes != 0)
+ result += minutes + " minutes|";
+ return enumerate(result.split("\\|"));
+ }
+
+ /**
+ * Return a formatted String to represent the given time length, in the given units
+ *
+ * @param sourceAmount Amount of delay
+ * @param sourceUnit Unit of delay
+ * @param ifEmpty the String to return if the
+ * @param displayedUnits units displayed
+ * @return the formatted string
+ * @throws IllegalArgumentException if there are no displayed units
+ */
+ public static String getTimeLength(long sourceAmount, TimeUnit sourceUnit, String ifEmpty, TimeUnit... displayedUnits) {
+ if (displayedUnits.length == 0) {
+ throw new IllegalArgumentException("No displayed units");
+ }
+ Arrays.sort(displayedUnits, Collections.reverseOrder(TimeUnit::compareTo)); // sort by opposite of enum declaration order (largest -> smallest)
+ List<String> segments = new ArrayList<>(displayedUnits.length);
+ for (TimeUnit unit : displayedUnits) {
+ long displayedAmount = unit.convert(sourceAmount, sourceUnit);
+ sourceAmount -= sourceUnit.convert(displayedAmount, unit);
+ if (displayedAmount > 0) {
+ String unitWord = unit.name().toLowerCase(); // plural
+ if (displayedAmount == 1) {
+ unitWord = unitWord.substring(0, unitWord.length() - 1); // remove s at the end
+ }
+ segments.add(displayedAmount + " " + unitWord);
+ }
+ }
+ return segments.isEmpty() ? ifEmpty : enumerate(segments.toArray(new String[segments.size()]));
+ }
+
+ /**
+ * Returns the delay represented by a ban-like delay representation, in milliseconds
+ * Example: "5d2h5m3s" for 5 days, 2 hours, 5 minutes and 3 seconds.
+ * <p>
+ * Supported characters are s, m, h, d, w.
+ * Negative numbers are supported.
+ *
+ * @param input The input string
+ * @return The delay in milliseconds
+ * @throws IllegalArgumentException if the input string isn't properly formatted, or any non-digit character isn't recognized (capitals are not recognized).
+ */
+ public static long getTimeLength(String input) { //if -1: error
+ long count = 0;
+ int i = 0;
+ while (i < input.length()) {
+ int num = 0;
+ char unit = '\0';
+ boolean negate;
+ if (negate = input.charAt(i) == '-') {
+ i++;
+ }
+ do {
+ char c = input.charAt(i);
+ int digit = c - '0';
+ if (0 <= digit && digit < 10) {
+ num = 10 * num + digit;
+ } else {
+ unit = c;
+ break;
+ }
+ } while (i < input.length());
+
+ long unitTime = getUnitTime(unit);
+ if (unitTime == -1)
+ throw new IllegalArgumentException();
+ if (negate) {
+ unitTime = -unitTime;
+ }
+ count += (num * unitTime);
+ }
+ return count;
+ }
+
+ /**
+ * Returns the time represented by the given unit character in milliseconds.
+ * <p>
+ * 's' -> 1000
+ * 'm' -> 1000 * 60
+ * 'h' -> 1000 * 60 * 60
+ * 'd' -> 1000 * 60 * 60 * 24
+ * 'w' -> 1000 * 60 * 60 * 24 * 7
+ * anything else -> -1
+ *
+ * @param unit The unit character, as shown above
+ * @return the millisecond delay represented by the unit
+ */
+ public static long getUnitTime(char unit) { //if -1: no value found
+ switch (Character.toLowerCase(unit)) {
+ case 's':
+ return 1000;
+ case 'm':
+ return 1000 * 60;
+ case 'h':
+ return 1000 * 60 * 60;
+ case 'd':
+ return 1000 * 60 * 60 * 24;
+ case 'w':
+ return 1000 * 60 * 60 * 24 * 7;
+ default:
+ return -1;
+ }
+ }
+
+ /**
+ * Computes a binary representation of the value.
+ * The returned representation always displays 64 bits.
+ * Every 8 bits, the digits are seperated by an _
+ * The representation is prefixed by 0b.
+ * <p>
+ * Example: 0b00000000_11111111_00000001_11110000_00001111_11001100_00001111_10111010
+ *
+ * @param entry the value to represent in binary
+ * @return A binary representation of the long value
+ */
+ public static String toBinaryString(long entry) {
+ String binary = Long.toBinaryString(entry);
+ String binary64 = String.valueOf(new char[64 - binary.length()]).replace('\0', '0') + binary;
+ String withUnderscores = String.join("_", IntStream.range(0, 8).mapToObj(x -> binary64.substring(x * 8, x * 8 + 8)).toArray(String[]::new));
+ return "0b" + withUnderscores;
+ }
+
+ /**
+ * Turns a generic java classname into a name formatted properly to be an enum constant.
+ *
+ * @param name The string value I'd describe as a generic java classname (so we have CapitalCase)
+ * @return An enum constant version of it (ENUM_FORMAT: CAPITAL_CASE)
+ */
+ public static String toEnumFormat(String name) {
+ StringBuilder result = new StringBuilder(name.length() + 2);
+
+ boolean capital = true;
+ for (int i = 0, n = name.length(); i < n; i++) {
+ char c = name.charAt(i);
+ if (capital) {
+ capital = Character.isUpperCase(c);
+ } else if (Character.isUpperCase(c)) {
+ capital = true;
+ result.append('_');
+ }
+ result.append(capital ? c : Character.toUpperCase(c));
+ }
+
+ return result.toString();
+ }
+
+ /**
+ * Replaces any occurrence of toReplace with another string.
+ * Any colours that occured before the occurence of toReplace, are copied to the end of the replacement.
+ *
+ * @param target The String to query
+ * @param toReplace The sequence to replace
+ * @param with the replacing sequence
+ * @return the result
+ */
+ public static String replaceKeepColours(String target, String toReplace, String with) {
+ int index = -toReplace.length();
+ while ((index = target.indexOf(toReplace, index + toReplace.length())) != -1) {
+ String start = target.substring(0, index);
+ Formatting coloursBefore = Formatting.getFormats(start);
+ String after;
+ try {
+ after = target.substring(index + toReplace.length());
+ } catch (IndexOutOfBoundsException e) {
+ after = "";
+ }
+ target = start + with + coloursBefore + after;
+ }
+ return target;
+ }
+
+ public static String replParam(String target, String param, Object repl) {
+ return replParam(target, param, repl, false);
+ }
+
+ public static String replParams(String target, String[] params, Object[] repls) {
+ return replParams(target, params, repls, false, false);
+ }
+
+ public static boolean replParams(String[] target, String[] params, Object[] repls) {
+ return replParams(target, 0, target.length, params, repls);
+ }
+
+ public static boolean replParams(String[] target, int from, int to, String[] params, Object[] repls) {
+ return replParams(target, from, to, params, repls, false);
+ }
+
+ public static boolean replParams(List<String> target, String[] params, Object[] repls) {
+ return replParams(target, 0, target.size(), params, repls);
+ }
+
+ public static boolean replParams(List<String> target, int from, int to, String[] params, Object[] repls) {
+ return replParams(target, from, to, params, repls, false);
+ }
+
+ public static String replParamAndTranslate(String target, String param, Object repl) {
+ return replParam(target, param, repl, true);
+ }
+
+ public static String replParamsAndTranslate(String target, String[] params, Object[] repls) {
+ return replParams(target, params, repls, false, true);
+ }
+
+ public static boolean replParamsAndTranslate(String[] target, String[] params, Object[] repls) {
+ return replParamsAndTranslate(target, 0, target.length, params, repls);
+ }
+
+ public static boolean replParamsAndTranslate(String[] target, int from, int to, String[] params, Object[] repls) {
+ return replParams(target, from, to, params, repls, true);
+ }
+
+ public static boolean replParamsAndTranslate(List<String> target, String[] params, Object[] repls) {
+ return replParamsAndTranslate(target, 0, target.size(), params, repls);
+ }
+
+ public static boolean replParamsAndTranslate(List<String> target, int from, int to, String[] params, Object[] repls) {
+ return replParams(target, from, to, params, repls, true);
+ }
+
+ private static String replParam(String target, String param, Object replacementObj, boolean translate) {
+ int idx = target.indexOf(param, 0);
+ if (idx == -1) {
+ return translate ? Formatting.translate(target) : target;
+ }
+
+ String rep = replacementObj.toString();
+ StringBuilder builder = new StringBuilder(target);
+ do {
+ builder.replace(idx, idx + param.length(), rep);
+ idx = builder.indexOf(param, idx + rep.length());
+ } while (idx != -1);
+
+ if (translate) {
+ Formatting.translate(builder);
+ }
+
+ return builder.toString();
+ }
+
+ @SuppressWarnings("StringEquality")
+ private static boolean replParams(String[] target, int from, int to, String[] params, Object[] repls, boolean translate) {
+ if (from < 0 || to < from || to > target.length) {
+ throw new IllegalArgumentException("Invalid from-to for array size " + target.length + ": " + from + "-" + to);
+ }
+
+ boolean change = false;
+ for (int i = from; i < to; i++) {
+ String val = target[i];
+ if (val != (val = replParams(val, params, repls, true, translate))) {
+ target[i] = val;
+ change = true;
+ }
+ }
+ return change;
+ }
+
+ @SuppressWarnings("StringEquality")
+ private static boolean replParams(List<String> target, int from, int to, String[] params, Object[] repls, boolean translate) {
+ if (from < 0 || to < from || to > target.size()) {
+ throw new IllegalArgumentException("Invalid from-to for list size " + target.size() + ": " + from + "-" + to);
+ }
+
+ boolean change = false;
+ if (target instanceof RandomAccess) {
+ for (int i = from; i < to; i++) {
+ String val = target.get(i);
+ if (val != (val = replParams(val, params, repls, true, translate))) {
+ target.set(i, val);
+ change = true;
+ }
+ }
+ } else {
+ ListIterator<String> itr = target.listIterator(from);
+ for (int n = to - from, i = 0; i < n && itr.hasNext(); i++) {
+ String val = itr.next();
+ if (val != (val = replParams(val, params, repls, true, translate))) {
+ itr.set(val);
+ change = true;
+ }
+ }
+ }
+ return change;
+ }
+
+ private static String replParams(String target, String[] params, Object[] repls, boolean updateRepls, boolean translate) {
+ int n = params.length;
+ if (n != repls.length) {
+ throw new IllegalArgumentException();
+ }
+
+ String param = null;
+ int idx = -1;
+ int i;
+ for (i = 0; i < n; i++) {
+ param = params[i];
+ if (param == null) {
+ continue;
+ }
+ idx = target.indexOf(param, 0);
+ if (idx != -1) {
+ break;
+ }
+ }
+
+ if (idx == -1) {
+ return translate ? Formatting.translate(target) : target;
+ }
+
+ String repl = repls[i].toString();
+ if (updateRepls) {
+ repls[i] = repl;
+ }
+
+ StringBuilder builder = new StringBuilder(target);
+ do {
+ builder.replace(idx, idx + param.length(), repl);
+ idx = builder.indexOf(param, idx + repl.length());
+ } while (idx != -1);
+
+ for (i++; i < n; i++) {
+ param = params[i];
+ if (param == null || (idx = builder.indexOf(param, 0)) == -1) {
+ continue;
+ }
+
+ repl = repls[i].toString();
+ if (updateRepls) {
+ repls[i] = repl;
+ }
+
+ do {
+ builder.replace(idx, idx + param.length(), repl);
+ idx = builder.indexOf(param, idx + repl.length());
+ } while (idx != -1);
+ }
+
+ if (translate) {
+ Formatting.translate(builder);
+ }
+
+ return builder.toString();
+ }
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/Whitelist.java b/dicore3/core/src/main/java/io/dico/dicore/Whitelist.java
index 5f8f4e2..e467d91 100644
--- a/dicore3/core/src/main/java/io/dico/dicore/Whitelist.java
+++ b/dicore3/core/src/main/java/io/dico/dicore/Whitelist.java
@@ -1,91 +1,91 @@
-package io.dico.dicore;
-
-import org.bukkit.configuration.ConfigurationSection;
-
-import java.util.*;
-import java.util.function.Function;
-import java.util.function.Predicate;
-import java.util.stream.Collectors;
-
-@FunctionalInterface
-public interface Whitelist extends Predicate {
-
- Whitelist EVERYTHING = item -> true;
- Whitelist NOTHING = item -> false;
- Whitelist NOT_NULL = Objects::nonNull;
-
- static <T> Predicate<T> everythingAsPredicate() {
- return (Predicate<T>) EVERYTHING;
- }
-
- static <T> Predicate<T> nothingAsPredicate() {
- return (Predicate<T>) NOTHING;
- }
-
- static <T> Predicate<T> notNullAsPredicate() {
- return (Predicate<T>) NOT_NULL;
- }
-
- static Whitelist only(Object item) {
- return item::equals;
- }
-
- static Whitelist not(Object item) {
- return o -> !item.equals(o);
- }
-
- static Whitelist only(Object item1, Object item2) {
- return item -> item1.equals(item) || item2.equals(item);
- }
-
- static Whitelist not(Object item1, Object item2) {
- return item -> !(item1.equals(item) || item2.equals(item));
- }
-
- static Whitelist only(Object[] objects) {
- return new SetBasedWhitelist(objects, false);
- }
-
- static Whitelist not(Object[] objects) {
- return new SetBasedWhitelist(objects, true);
- }
-
- static Whitelist fromConfig(ConfigurationSection section, Function<String, ?> parser) {
- if (section == null) {
- return NOTHING;
- }
- boolean blacklist = section.getBoolean("blacklist", false);
- Set list = section.getStringList("listed").stream().map(parser).filter(Objects::nonNull).collect(Collectors.toSet());
- switch (list.size()) {
- case 0:
- return blacklist ? EVERYTHING : NOTHING;
- case 1: {
- Iterator iterator = list.iterator();
- return blacklist ? not(iterator.next()) : only(iterator.next());
- }
- case 2: {
- Iterator iterator = list.iterator();
- return blacklist ? not(iterator.next(), iterator.next()) : only(iterator.next(), iterator.next());
- }
- default:
- Object item = list.iterator().next();
- if (item instanceof Enum) {
- list = EnumSet.copyOf(list);
- }
- return new SetBasedWhitelist(list, blacklist);
- }
- }
-
- static void copyIntoConfig(ConfigurationSection target, Function<Object, String> mapper, boolean blacklist, Object... objects) {
- target.set("blacklist", blacklist);
- target.set("listed", Arrays.stream(objects).map(mapper).unordered().distinct().collect(Collectors.toList()));
- }
-
- @Override
- default boolean test(Object o) {
- return isWhitelisted(o);
- }
-
- boolean isWhitelisted(Object o);
-}
-
+package io.dico.dicore;
+
+import org.bukkit.configuration.ConfigurationSection;
+
+import java.util.*;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+@FunctionalInterface
+public interface Whitelist extends Predicate {
+
+ Whitelist EVERYTHING = item -> true;
+ Whitelist NOTHING = item -> false;
+ Whitelist NOT_NULL = Objects::nonNull;
+
+ static <T> Predicate<T> everythingAsPredicate() {
+ return (Predicate<T>) EVERYTHING;
+ }
+
+ static <T> Predicate<T> nothingAsPredicate() {
+ return (Predicate<T>) NOTHING;
+ }
+
+ static <T> Predicate<T> notNullAsPredicate() {
+ return (Predicate<T>) NOT_NULL;
+ }
+
+ static Whitelist only(Object item) {
+ return item::equals;
+ }
+
+ static Whitelist not(Object item) {
+ return o -> !item.equals(o);
+ }
+
+ static Whitelist only(Object item1, Object item2) {
+ return item -> item1.equals(item) || item2.equals(item);
+ }
+
+ static Whitelist not(Object item1, Object item2) {
+ return item -> !(item1.equals(item) || item2.equals(item));
+ }
+
+ static Whitelist only(Object[] objects) {
+ return new SetBasedWhitelist(objects, false);
+ }
+
+ static Whitelist not(Object[] objects) {
+ return new SetBasedWhitelist(objects, true);
+ }
+
+ static Whitelist fromConfig(ConfigurationSection section, Function<String, ?> parser) {
+ if (section == null) {
+ return NOTHING;
+ }
+ boolean blacklist = section.getBoolean("blacklist", false);
+ Set list = section.getStringList("listed").stream().map(parser).filter(Objects::nonNull).collect(Collectors.toSet());
+ switch (list.size()) {
+ case 0:
+ return blacklist ? EVERYTHING : NOTHING;
+ case 1: {
+ Iterator iterator = list.iterator();
+ return blacklist ? not(iterator.next()) : only(iterator.next());
+ }
+ case 2: {
+ Iterator iterator = list.iterator();
+ return blacklist ? not(iterator.next(), iterator.next()) : only(iterator.next(), iterator.next());
+ }
+ default:
+ Object item = list.iterator().next();
+ if (item instanceof Enum) {
+ list = EnumSet.copyOf(list);
+ }
+ return new SetBasedWhitelist(list, blacklist);
+ }
+ }
+
+ static void copyIntoConfig(ConfigurationSection target, Function<Object, String> mapper, boolean blacklist, Object... objects) {
+ target.set("blacklist", blacklist);
+ target.set("listed", Arrays.stream(objects).map(mapper).unordered().distinct().collect(Collectors.toList()));
+ }
+
+ @Override
+ default boolean test(Object o) {
+ return isWhitelisted(o);
+ }
+
+ boolean isWhitelisted(Object o);
+}
+
diff --git a/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListener.java b/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListener.java
index 93584ea..378424d 100644
--- a/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListener.java
+++ b/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListener.java
@@ -1,46 +1,46 @@
-package io.dico.dicore.event;
-
-import io.dico.dicore.InterfaceChain;
-
-public interface ChainedListener<T> extends InterfaceChain<SimpleListener<T>, ChainedListener<T>>, SimpleListener<T> {
-
- @Override
- default ChainedListener<T> getEmptyInstance() {
- return ChainedListeners.empty();
- }
-
- @Override
- default ChainedListener<T> withElement(SimpleListener<T> element) {
- if (element == null) {
- return this;
- }
-
- int count = getElementCount() + 1;
- return new ChainedListener<T>() {
- @Override
- public void accept(T event) {
- try {
- ChainedListener.this.accept(event);
- } finally {
- element.accept(event);
- }
- }
-
- @Override
- public ChainedListener<T> withoutLastNode() {
- return ChainedListener.this;
- }
-
- @Override
- public SimpleListener<T> getDelegateOfLastNode() {
- return element;
- }
-
- @Override
- public int getElementCount() {
- return count;
- }
- };
- }
-
-}
+package io.dico.dicore.event;
+
+import io.dico.dicore.InterfaceChain;
+
+public interface ChainedListener<T> extends InterfaceChain<SimpleListener<T>, ChainedListener<T>>, SimpleListener<T> {
+
+ @Override
+ default ChainedListener<T> getEmptyInstance() {
+ return ChainedListeners.empty();
+ }
+
+ @Override
+ default ChainedListener<T> withElement(SimpleListener<T> element) {
+ if (element == null) {
+ return this;
+ }
+
+ int count = getElementCount() + 1;
+ return new ChainedListener<T>() {
+ @Override
+ public void accept(T event) {
+ try {
+ ChainedListener.this.accept(event);
+ } finally {
+ element.accept(event);
+ }
+ }
+
+ @Override
+ public ChainedListener<T> withoutLastNode() {
+ return ChainedListener.this;
+ }
+
+ @Override
+ public SimpleListener<T> getDelegateOfLastNode() {
+ return element;
+ }
+
+ @Override
+ public int getElementCount() {
+ return count;
+ }
+ };
+ }
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListenerHandle.java b/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListenerHandle.java
index 86b8448..875c131 100644
--- a/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListenerHandle.java
+++ b/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListenerHandle.java
@@ -1,55 +1,55 @@
-package io.dico.dicore.event;
-
-import io.dico.dicore.InterfaceChain;
-
-public interface ChainedListenerHandle extends InterfaceChain<ListenerHandle, ChainedListenerHandle>, ListenerHandle {
-
- @Override
- default ChainedListenerHandle getEmptyInstance() {
- return ChainedListenerHandles.empty();
- }
-
- @Override
- default ChainedListenerHandle withElement(ListenerHandle element) {
- if (element == null) {
- return this;
- }
-
- int count = getElementCount() + 1;
- return new ChainedListenerHandle() {
- @Override
- public void register() {
- try {
- ChainedListenerHandle.this.register();
- } finally {
- element.register();
- }
- }
-
- @Override
- public void unregister() {
- try {
- ChainedListenerHandle.this.unregister();
- } finally {
- element.unregister();
- }
- }
-
- @Override
- public ChainedListenerHandle withoutLastNode() {
- return ChainedListenerHandle.this;
- }
-
- @Override
- public ListenerHandle getDelegateOfLastNode() {
- return element;
- }
-
- @Override
- public int getElementCount() {
- return count;
- }
- };
- }
-
-}
+package io.dico.dicore.event;
+
+import io.dico.dicore.InterfaceChain;
+
+public interface ChainedListenerHandle extends InterfaceChain<ListenerHandle, ChainedListenerHandle>, ListenerHandle {
+
+ @Override
+ default ChainedListenerHandle getEmptyInstance() {
+ return ChainedListenerHandles.empty();
+ }
+
+ @Override
+ default ChainedListenerHandle withElement(ListenerHandle element) {
+ if (element == null) {
+ return this;
+ }
+
+ int count = getElementCount() + 1;
+ return new ChainedListenerHandle() {
+ @Override
+ public void register() {
+ try {
+ ChainedListenerHandle.this.register();
+ } finally {
+ element.register();
+ }
+ }
+
+ @Override
+ public void unregister() {
+ try {
+ ChainedListenerHandle.this.unregister();
+ } finally {
+ element.unregister();
+ }
+ }
+
+ @Override
+ public ChainedListenerHandle withoutLastNode() {
+ return ChainedListenerHandle.this;
+ }
+
+ @Override
+ public ListenerHandle getDelegateOfLastNode() {
+ return element;
+ }
+
+ @Override
+ public int getElementCount() {
+ return count;
+ }
+ };
+ }
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListenerHandles.java b/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListenerHandles.java
index 0be0508..15c4553 100644
--- a/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListenerHandles.java
+++ b/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListenerHandles.java
@@ -1,63 +1,63 @@
-package io.dico.dicore.event;
-
-public class ChainedListenerHandles {
-
- private ChainedListenerHandles() {
-
- }
-
- private static final ChainedListenerHandle empty = new ChainedListenerHandle() {
- @Override
- public void register() {
-
- }
-
- public void unregister() {
-
- }
-
- @Override
- public ChainedListenerHandle withElement(ListenerHandle other) {
- return ChainedListenerHandles.singleton(other);
- }
-
- @Override
- public int getElementCount() {
- return 0;
- }
-
- @Override
- public ListenerHandle getDelegateOfLastNode() {
- return null;
- }
- };
-
- public static ChainedListenerHandle empty() {
- return empty;
- }
-
- public static ChainedListenerHandle singleton(ListenerHandle element) {
- if (element instanceof ChainedListenerHandle) {
- return (ChainedListenerHandle) element;
- }
- if (element == null) {
- return empty();
- }
- return new ChainedListenerHandle() {
- @Override
- public void register() {
- element.register();
- }
-
- public void unregister() {
- element.unregister();
- }
-
- @Override
- public ListenerHandle getDelegateOfLastNode() {
- return element;
- }
- };
- }
-
-}
+package io.dico.dicore.event;
+
+public class ChainedListenerHandles {
+
+ private ChainedListenerHandles() {
+
+ }
+
+ private static final ChainedListenerHandle empty = new ChainedListenerHandle() {
+ @Override
+ public void register() {
+
+ }
+
+ public void unregister() {
+
+ }
+
+ @Override
+ public ChainedListenerHandle withElement(ListenerHandle other) {
+ return ChainedListenerHandles.singleton(other);
+ }
+
+ @Override
+ public int getElementCount() {
+ return 0;
+ }
+
+ @Override
+ public ListenerHandle getDelegateOfLastNode() {
+ return null;
+ }
+ };
+
+ public static ChainedListenerHandle empty() {
+ return empty;
+ }
+
+ public static ChainedListenerHandle singleton(ListenerHandle element) {
+ if (element instanceof ChainedListenerHandle) {
+ return (ChainedListenerHandle) element;
+ }
+ if (element == null) {
+ return empty();
+ }
+ return new ChainedListenerHandle() {
+ @Override
+ public void register() {
+ element.register();
+ }
+
+ public void unregister() {
+ element.unregister();
+ }
+
+ @Override
+ public ListenerHandle getDelegateOfLastNode() {
+ return element;
+ }
+ };
+ }
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListeners.java b/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListeners.java
index 1d2bcd4..44050a2 100644
--- a/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListeners.java
+++ b/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListeners.java
@@ -1,56 +1,56 @@
-package io.dico.dicore.event;
-
-@SuppressWarnings("unchecked")
-public class ChainedListeners {
-
- private static final ChainedListener<?> empty = new ChainedListener<Object>() {
- @Override
- public void accept(Object event) {
-
- }
-
- @Override
- public ChainedListener<Object> withElement(SimpleListener other) {
- return ChainedListeners.singleton(other);
- }
-
- @Override
- public int getElementCount() {
- return 0;
- }
-
- @Override
- public SimpleListener<Object> getDelegateOfLastNode() {
- return null;
- }
- };
-
- private ChainedListeners() {
-
- }
-
- public static <T> ChainedListener<T> empty() {
- return (ChainedListener<T>) empty;
- }
-
- public static <T> ChainedListener<T> singleton(SimpleListener<T> element) {
- if (element instanceof ChainedListener) {
- return (ChainedListener<T>) element;
- }
- if (element == null) {
- return empty();
- }
- return new ChainedListener<T>() {
- @Override
- public void accept(T event) {
- element.accept(event);
- }
-
- @Override
- public SimpleListener getDelegateOfLastNode() {
- return element;
- }
- };
- }
-
-}
+package io.dico.dicore.event;
+
+@SuppressWarnings("unchecked")
+public class ChainedListeners {
+
+ private static final ChainedListener<?> empty = new ChainedListener<Object>() {
+ @Override
+ public void accept(Object event) {
+
+ }
+
+ @Override
+ public ChainedListener<Object> withElement(SimpleListener other) {
+ return ChainedListeners.singleton(other);
+ }
+
+ @Override
+ public int getElementCount() {
+ return 0;
+ }
+
+ @Override
+ public SimpleListener<Object> getDelegateOfLastNode() {
+ return null;
+ }
+ };
+
+ private ChainedListeners() {
+
+ }
+
+ public static <T> ChainedListener<T> empty() {
+ return (ChainedListener<T>) empty;
+ }
+
+ public static <T> ChainedListener<T> singleton(SimpleListener<T> element) {
+ if (element instanceof ChainedListener) {
+ return (ChainedListener<T>) element;
+ }
+ if (element == null) {
+ return empty();
+ }
+ return new ChainedListener<T>() {
+ @Override
+ public void accept(T event) {
+ element.accept(event);
+ }
+
+ @Override
+ public SimpleListener getDelegateOfLastNode() {
+ return element;
+ }
+ };
+ }
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/event/HandlerList.java b/dicore3/core/src/main/java/io/dico/dicore/event/HandlerList.java
index 56265bb..fa290c0 100644
--- a/dicore3/core/src/main/java/io/dico/dicore/event/HandlerList.java
+++ b/dicore3/core/src/main/java/io/dico/dicore/event/HandlerList.java
@@ -1,91 +1,91 @@
-package io.dico.dicore.event;
-
-import org.bukkit.event.Cancellable;
-import org.bukkit.event.EventPriority;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.function.Consumer;
-
-public final class HandlerList<T> {
- private final List<Listener<T>> source = new ArrayList<>();
- private Listener<T>[] listeners = newArray(0);
-
- public void refresh() {
- source.sort(Comparator.comparingInt(l -> l.getPriority().ordinal()));
- listeners = source.toArray(newArray(source.size()));
- }
-
- @SuppressWarnings("unchecked")
- private static <T> Listener<T>[] newArray(int length) {
- return new Listener[length];
- }
-
- public void register(Listener<T> listener) {
- if (!source.contains(listener) && source.add(listener)) {
- refresh();
- }
- }
-
- public ListenerHandle getListenerHandle(Listener<T> listener) {
- return new ListenerHandle() {
- @Override
- public void register() {
- HandlerList.this.register(listener);
- }
-
- @Override
- public void unregister() {
- HandlerList.this.unregister(listener);
- }
- };
- }
-
- public void register(EventPriority priority, Consumer<T> listener) {
- register(new Listener<T>() {
- @Override
- public EventPriority getPriority() {
- return priority;
- }
-
- @Override
- public void accept(T event) {
- listener.accept(event);
- }
- });
- }
-
- public List<Listener<T>> getRegistrations() {
- return Collections.unmodifiableList(source);
- }
-
- public void unregister(Listener<T> listener) {
- if (source.remove(listener)) {
- refresh();
- }
- }
-
- public void callEvent(T event) {
- if (event instanceof Cancellable) {
- Cancellable c = (Cancellable) event;
- boolean cancelled = c.isCancelled();
- for (Listener<T> listener : listeners) {
- if (listener.listensToCancelledState(cancelled)) {
- //EnchantsPlugin.getInstance().debug("Listener acceptance: " + listener.getClass().getSimpleName());
- listener.accept(event);
- cancelled = c.isCancelled();
- } /*else {
- EnchantsPlugin.getInstance().debug("Listener does not listen to cancelled state of " + cancelled + ": " + listener.getClass().getSimpleName());
- }*/
- }
- } else {
- for (Listener<T> listener : listeners) {
- //EnchantsPlugin.getInstance().debug("Listener acceptance: " + listener.getClass().getSimpleName());
- listener.accept(event);
- }
- }
- }
-
-}
+package io.dico.dicore.event;
+
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.EventPriority;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.function.Consumer;
+
+public final class HandlerList<T> {
+ private final List<Listener<T>> source = new ArrayList<>();
+ private Listener<T>[] listeners = newArray(0);
+
+ public void refresh() {
+ source.sort(Comparator.comparingInt(l -> l.getPriority().ordinal()));
+ listeners = source.toArray(newArray(source.size()));
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <T> Listener<T>[] newArray(int length) {
+ return new Listener[length];
+ }
+
+ public void register(Listener<T> listener) {
+ if (!source.contains(listener) && source.add(listener)) {
+ refresh();
+ }
+ }
+
+ public ListenerHandle getListenerHandle(Listener<T> listener) {
+ return new ListenerHandle() {
+ @Override
+ public void register() {
+ HandlerList.this.register(listener);
+ }
+
+ @Override
+ public void unregister() {
+ HandlerList.this.unregister(listener);
+ }
+ };
+ }
+
+ public void register(EventPriority priority, Consumer<T> listener) {
+ register(new Listener<T>() {
+ @Override
+ public EventPriority getPriority() {
+ return priority;
+ }
+
+ @Override
+ public void accept(T event) {
+ listener.accept(event);
+ }
+ });
+ }
+
+ public List<Listener<T>> getRegistrations() {
+ return Collections.unmodifiableList(source);
+ }
+
+ public void unregister(Listener<T> listener) {
+ if (source.remove(listener)) {
+ refresh();
+ }
+ }
+
+ public void callEvent(T event) {
+ if (event instanceof Cancellable) {
+ Cancellable c = (Cancellable) event;
+ boolean cancelled = c.isCancelled();
+ for (Listener<T> listener : listeners) {
+ if (listener.listensToCancelledState(cancelled)) {
+ //EnchantsPlugin.getInstance().debug("Listener acceptance: " + listener.getClass().getSimpleName());
+ listener.accept(event);
+ cancelled = c.isCancelled();
+ } /*else {
+ EnchantsPlugin.getInstance().debug("Listener does not listen to cancelled state of " + cancelled + ": " + listener.getClass().getSimpleName());
+ }*/
+ }
+ } else {
+ for (Listener<T> listener : listeners) {
+ //EnchantsPlugin.getInstance().debug("Listener acceptance: " + listener.getClass().getSimpleName());
+ listener.accept(event);
+ }
+ }
+ }
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/event/Listener.java b/dicore3/core/src/main/java/io/dico/dicore/event/Listener.java
index c3e5413..064cd28 100644
--- a/dicore3/core/src/main/java/io/dico/dicore/event/Listener.java
+++ b/dicore3/core/src/main/java/io/dico/dicore/event/Listener.java
@@ -1,17 +1,17 @@
-package io.dico.dicore.event;
-
-import org.bukkit.event.EventPriority;
-
-public interface Listener<T> {
-
- default EventPriority getPriority() {
- return EventPriority.NORMAL;
- }
-
- default boolean listensToCancelledState(boolean cancelled) {
- return !cancelled;
- }
-
- void accept(T event);
-
-}
+package io.dico.dicore.event;
+
+import org.bukkit.event.EventPriority;
+
+public interface Listener<T> {
+
+ default EventPriority getPriority() {
+ return EventPriority.NORMAL;
+ }
+
+ default boolean listensToCancelledState(boolean cancelled) {
+ return !cancelled;
+ }
+
+ void accept(T event);
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/event/ListenerHandle.java b/dicore3/core/src/main/java/io/dico/dicore/event/ListenerHandle.java
index 6042954..fbedd9f 100644
--- a/dicore3/core/src/main/java/io/dico/dicore/event/ListenerHandle.java
+++ b/dicore3/core/src/main/java/io/dico/dicore/event/ListenerHandle.java
@@ -1,9 +1,9 @@
-package io.dico.dicore.event;
-
-public interface ListenerHandle {
-
- void register();
-
- void unregister();
-
-}
+package io.dico.dicore.event;
+
+public interface ListenerHandle {
+
+ void register();
+
+ void unregister();
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/event/SimpleListener.java b/dicore3/core/src/main/java/io/dico/dicore/event/SimpleListener.java
index 9688975..4e49a55 100644
--- a/dicore3/core/src/main/java/io/dico/dicore/event/SimpleListener.java
+++ b/dicore3/core/src/main/java/io/dico/dicore/event/SimpleListener.java
@@ -1,7 +1,7 @@
-package io.dico.dicore.event;
-
-public interface SimpleListener<T> {
-
- void accept(T event);
-
-}
+package io.dico.dicore.event;
+
+public interface SimpleListener<T> {
+
+ void accept(T event);
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/exceptions/ExceptionHandler.java b/dicore3/core/src/main/java/io/dico/dicore/exceptions/ExceptionHandler.java
index e95b65e..2777125 100644
--- a/dicore3/core/src/main/java/io/dico/dicore/exceptions/ExceptionHandler.java
+++ b/dicore3/core/src/main/java/io/dico/dicore/exceptions/ExceptionHandler.java
@@ -1,203 +1,203 @@
-package io.dico.dicore.exceptions;
-
-import io.dico.dicore.exceptions.checkedfunctions.CheckedRunnable;
-import io.dico.dicore.exceptions.checkedfunctions.CheckedSupplier;
-import io.dico.dicore.exceptions.checkedfunctions.CheckedFunctionalObject;
-
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.sql.SQLException;
-import java.util.Objects;
-import java.util.function.Consumer;
-import java.util.function.Supplier;
-
-@FunctionalInterface
-public interface ExceptionHandler {
-
- /**
- * Handle the given exception according to this handler's implementation
- *
- * @param ex The exception to be handled
- * @throws NullPointerException if ex is null, unless the implementation specifies otherwise
- * @throws Error ex if ex is an instance of Error, unless the implementation specifies otherwise
- */
- void handle(Throwable ex);
-
- /**
- * Handle the given exception according to this handler's implementation
- * This method is intended for use by {@link CheckedFunctionalObject} and subinterfaces.
- * It supplies exception handlers the option to acquire more information, by overriding this method and calling it from {@link #handle(Throwable)}
- *
- * @param ex The exception to be handled
- * @param args Any arguments passed, this is used by {@link CheckedFunctionalObject} and subinterfaces.
- * @return {@code null} (unless specified otherwise by the implementation)
- * @throws NullPointerException if ex is null, unless the implementation specifies otherwise
- * @throws Error ex if ex is an instance of Error, unless the implementation specifies otherwise
- */
- default Object handleGenericException(Throwable ex, Object... args) {
- handle(ex);
- return null;
- }
-
- /**
- * @return true if this {@link ExceptionHandler}'s {@link #handleGenericException(Throwable, Object...)} method is <b>never</b> expected to throw
- * an unchecked exception other than {@link Error}
- */
- default boolean isSafe() {
- return true;
- }
-
- /**
- * Runs the given checked action, handling any thrown exceptions using this exception handler.
- * <p>
- * Any exceptions thrown by this handler are delegated to the caller.
- *
- * @param action The action to run
- * @throws NullPointerException if action is null
- */
- default void runSafe(CheckedRunnable<? extends Throwable> action) {
- Objects.requireNonNull(action);
- try {
- action.checkedRun();
- } catch (Throwable ex) {
- handle(ex);
- }
- }
-
- /**
- * Computes the result of the given checked supplier, handling any thrown exceptions using this exception handler.
- * <p>
- * Any exceptions thrown by this handler are delegated to the caller.
- *
- * @param action The supplier whose result to compute
- * @param <T> generic type parameter for the supplier and the result type of this method
- * @return The result of this computation, or null if an error occurred
- * @throws NullPointerException if action is null
- */
-
- default <T> T supplySafe(CheckedSupplier<T, ? extends Throwable> action) {
- Objects.requireNonNull(action);
- try {
- return action.checkedGet();
- } catch (Throwable ex) {
- handle(ex);
- return null;
- }
- }
-
- /**
- * @param action The action to wrap
- * @return A runnable that wraps the given action using this handler's {@link #runSafe(CheckedRunnable)} method.
- * @see #runSafe(CheckedRunnable)
- */
- default Runnable safeRunnable(CheckedRunnable<? extends Throwable> action) {
- return () -> runSafe(action);
- }
-
- /**
- * @param supplier The computation to wrap
- * @return A supplier that wraps the given computation using this handler's {@link #supplySafe(CheckedSupplier)} method.
- * @see #supplySafe(CheckedSupplier)
- */
- default <T> Supplier<T> safeSupplier(CheckedSupplier<T, ? extends Throwable> supplier) {
- return () -> supplySafe(supplier);
- }
-
- /**
- * Logs the given exception as an error to {@code out}
- * <p>
- * Format: Error occurred while {@code failedActivityDescription}, followed by additional details and a stack trace
- *
- * @param out The consumer to accept the error message, for instance {@code {@link java.util.logging.Logger logger}::warning}.
- * @param failedActivityDescription A description of the activity that was being executed when the exception was thrown
- * @param ex The exception that was thrown
- * @throws NullPointerException if any argument is null
- */
- static void log(Consumer<String> out, String failedActivityDescription, Throwable ex) {
- if (out == null || failedActivityDescription == null || ex == null) {
- throw new NullPointerException();
- }
-
- StringWriter msg = new StringWriter(1024);
- msg.append("Error occurred while ").append(failedActivityDescription).append(':');
-
- if (ex instanceof SQLException) {
- SQLException sqlex = (SQLException) ex;
- msg.append('\n').append("Error code: ").append(Integer.toString(sqlex.getErrorCode()));
- msg.append('\n').append("SQL State: ").append(sqlex.getSQLState());
- }
-
- msg.append('\n').append("=======START STACK=======");
- try (PrintWriter pw = new PrintWriter(msg)) {
- ex.printStackTrace(pw);
- }
- msg.append('\n').append("========END STACK========");
-
- out.accept(msg.toString());
- }
-
- /**
- * @param activityDescription The activity description
- * @return An ExceptionHandler that prints to {@link System#out}
- * @see #log(Consumer, String)
- */
- static ExceptionHandler log(String activityDescription) {
- // A method reference would cache the current value in System.out
- // This would update if the value in System.out is changed (by for example, applying a PrintStream that handles colours).
- return log(msg -> System.out.println(msg), activityDescription);
- }
-
- /**
- * @param out The Consumer to be passed to {@link #log(Consumer, String, Throwable)}
- * @param activityDescription The activity description to be passed to {@link #log(Consumer, String, Throwable)}
- * @return An ExceptionHandler that passes exceptions with given arguments to {@link #log(Consumer, String, Throwable)}
- * @see #log(Consumer, String, Throwable)
- */
- static ExceptionHandler log(Consumer<String> out, String activityDescription) {
- return ex -> log(out, activityDescription, ex);
- }
-
- /**
- * This ExceptionHandler turns any Throwable into an unchecked exception, then throws it again.
- */
- ExceptionHandler UNCHECKED = new ExceptionHandler() {
- @Override
- public void handle(Throwable ex) {
- errorFilter(ex);
- if (ex instanceof RuntimeException) {
- throw (RuntimeException) ex;
- }
- throw new RuntimeException(ex);
- }
-
- @Override
- public boolean isSafe() {
- return false;
- }
- };
-
- /**
- * This ExceptionHandler suppresses all exceptions,
- * apart from {@link Error} because that is not usually supposed to be caught
- */
- ExceptionHandler SUPPRESS = ExceptionHandler::errorFilter;
-
- /**
- * This ExceptionHandler calls {@link Throwable#printStackTrace()} unless it is a {@link NullPointerException} or {@link Error}.
- */
- ExceptionHandler PRINT_UNLESS_NP = ex -> {
- errorFilter(ex);
- if (!(ex instanceof NullPointerException)) {
- ex.printStackTrace();
- }
- };
-
- static void errorFilter(Throwable ex) {
- if (ex instanceof Error) {
- throw (Error) ex;
- } else if (ex == null) {
- throw new NullPointerException();
- }
- }
-
-}
+package io.dico.dicore.exceptions;
+
+import io.dico.dicore.exceptions.checkedfunctions.CheckedRunnable;
+import io.dico.dicore.exceptions.checkedfunctions.CheckedSupplier;
+import io.dico.dicore.exceptions.checkedfunctions.CheckedFunctionalObject;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.sql.SQLException;
+import java.util.Objects;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+@FunctionalInterface
+public interface ExceptionHandler {
+
+ /**
+ * Handle the given exception according to this handler's implementation
+ *
+ * @param ex The exception to be handled
+ * @throws NullPointerException if ex is null, unless the implementation specifies otherwise
+ * @throws Error ex if ex is an instance of Error, unless the implementation specifies otherwise
+ */
+ void handle(Throwable ex);
+
+ /**
+ * Handle the given exception according to this handler's implementation
+ * This method is intended for use by {@link CheckedFunctionalObject} and subinterfaces.
+ * It supplies exception handlers the option to acquire more information, by overriding this method and calling it from {@link #handle(Throwable)}
+ *
+ * @param ex The exception to be handled
+ * @param args Any arguments passed, this is used by {@link CheckedFunctionalObject} and subinterfaces.
+ * @return {@code null} (unless specified otherwise by the implementation)
+ * @throws NullPointerException if ex is null, unless the implementation specifies otherwise
+ * @throws Error ex if ex is an instance of Error, unless the implementation specifies otherwise
+ */
+ default Object handleGenericException(Throwable ex, Object... args) {
+ handle(ex);
+ return null;
+ }
+
+ /**
+ * @return true if this {@link ExceptionHandler}'s {@link #handleGenericException(Throwable, Object...)} method is <b>never</b> expected to throw
+ * an unchecked exception other than {@link Error}
+ */
+ default boolean isSafe() {
+ return true;
+ }
+
+ /**
+ * Runs the given checked action, handling any thrown exceptions using this exception handler.
+ * <p>
+ * Any exceptions thrown by this handler are delegated to the caller.
+ *
+ * @param action The action to run
+ * @throws NullPointerException if action is null
+ */
+ default void runSafe(CheckedRunnable<? extends Throwable> action) {
+ Objects.requireNonNull(action);
+ try {
+ action.checkedRun();
+ } catch (Throwable ex) {
+ handle(ex);
+ }
+ }
+
+ /**
+ * Computes the result of the given checked supplier, handling any thrown exceptions using this exception handler.
+ * <p>
+ * Any exceptions thrown by this handler are delegated to the caller.
+ *
+ * @param action The supplier whose result to compute
+ * @param <T> generic type parameter for the supplier and the result type of this method
+ * @return The result of this computation, or null if an error occurred
+ * @throws NullPointerException if action is null
+ */
+
+ default <T> T supplySafe(CheckedSupplier<T, ? extends Throwable> action) {
+ Objects.requireNonNull(action);
+ try {
+ return action.checkedGet();
+ } catch (Throwable ex) {
+ handle(ex);
+ return null;
+ }
+ }
+
+ /**
+ * @param action The action to wrap
+ * @return A runnable that wraps the given action using this handler's {@link #runSafe(CheckedRunnable)} method.
+ * @see #runSafe(CheckedRunnable)
+ */
+ default Runnable safeRunnable(CheckedRunnable<? extends Throwable> action) {
+ return () -> runSafe(action);
+ }
+
+ /**
+ * @param supplier The computation to wrap
+ * @return A supplier that wraps the given computation using this handler's {@link #supplySafe(CheckedSupplier)} method.
+ * @see #supplySafe(CheckedSupplier)
+ */
+ default <T> Supplier<T> safeSupplier(CheckedSupplier<T, ? extends Throwable> supplier) {
+ return () -> supplySafe(supplier);
+ }
+
+ /**
+ * Logs the given exception as an error to {@code out}
+ * <p>
+ * Format: Error occurred while {@code failedActivityDescription}, followed by additional details and a stack trace
+ *
+ * @param out The consumer to accept the error message, for instance {@code {@link java.util.logging.Logger logger}::warning}.
+ * @param failedActivityDescription A description of the activity that was being executed when the exception was thrown
+ * @param ex The exception that was thrown
+ * @throws NullPointerException if any argument is null
+ */
+ static void log(Consumer<String> out, String failedActivityDescription, Throwable ex) {
+ if (out == null || failedActivityDescription == null || ex == null) {
+ throw new NullPointerException();
+ }
+
+ StringWriter msg = new StringWriter(1024);
+ msg.append("Error occurred while ").append(failedActivityDescription).append(':');
+
+ if (ex instanceof SQLException) {
+ SQLException sqlex = (SQLException) ex;
+ msg.append('\n').append("Error code: ").append(Integer.toString(sqlex.getErrorCode()));
+ msg.append('\n').append("SQL State: ").append(sqlex.getSQLState());
+ }
+
+ msg.append('\n').append("=======START STACK=======");
+ try (PrintWriter pw = new PrintWriter(msg)) {
+ ex.printStackTrace(pw);
+ }
+ msg.append('\n').append("========END STACK========");
+
+ out.accept(msg.toString());
+ }
+
+ /**
+ * @param activityDescription The activity description
+ * @return An ExceptionHandler that prints to {@link System#out}
+ * @see #log(Consumer, String)
+ */
+ static ExceptionHandler log(String activityDescription) {
+ // A method reference would cache the current value in System.out
+ // This would update if the value in System.out is changed (by for example, applying a PrintStream that handles colours).
+ return log(msg -> System.out.println(msg), activityDescription);
+ }
+
+ /**
+ * @param out The Consumer to be passed to {@link #log(Consumer, String, Throwable)}
+ * @param activityDescription The activity description to be passed to {@link #log(Consumer, String, Throwable)}
+ * @return An ExceptionHandler that passes exceptions with given arguments to {@link #log(Consumer, String, Throwable)}
+ * @see #log(Consumer, String, Throwable)
+ */
+ static ExceptionHandler log(Consumer<String> out, String activityDescription) {
+ return ex -> log(out, activityDescription, ex);
+ }
+
+ /**
+ * This ExceptionHandler turns any Throwable into an unchecked exception, then throws it again.
+ */
+ ExceptionHandler UNCHECKED = new ExceptionHandler() {
+ @Override
+ public void handle(Throwable ex) {
+ errorFilter(ex);
+ if (ex instanceof RuntimeException) {
+ throw (RuntimeException) ex;
+ }
+ throw new RuntimeException(ex);
+ }
+
+ @Override
+ public boolean isSafe() {
+ return false;
+ }
+ };
+
+ /**
+ * This ExceptionHandler suppresses all exceptions,
+ * apart from {@link Error} because that is not usually supposed to be caught
+ */
+ ExceptionHandler SUPPRESS = ExceptionHandler::errorFilter;
+
+ /**
+ * This ExceptionHandler calls {@link Throwable#printStackTrace()} unless it is a {@link NullPointerException} or {@link Error}.
+ */
+ ExceptionHandler PRINT_UNLESS_NP = ex -> {
+ errorFilter(ex);
+ if (!(ex instanceof NullPointerException)) {
+ ex.printStackTrace();
+ }
+ };
+
+ static void errorFilter(Throwable ex) {
+ if (ex instanceof Error) {
+ throw (Error) ex;
+ } else if (ex == null) {
+ throw new NullPointerException();
+ }
+ }
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedBiConsumer.java b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedBiConsumer.java
index badf05b..bfcafe8 100644
--- a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedBiConsumer.java
+++ b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedBiConsumer.java
@@ -1,69 +1,69 @@
-package io.dico.dicore.exceptions.checkedfunctions;
-
-import io.dico.dicore.exceptions.ExceptionHandler;
-
-import java.util.function.BiConsumer;
-
-/**
- * checked mimic of {@link BiConsumer}
- *
- * @param <TParam1>
- * @param <TParam2>
- * @param <TException>
- */
-@FunctionalInterface
-public interface CheckedBiConsumer<TParam1, TParam2, TException extends Throwable>
- extends CheckedFunctionalObject<Void, TException>, BiConsumer<TParam1, TParam2> {
-
- /**
- * The consuming action
- *
- * @param t the first argument to consume
- * @param u the second argument to consume
- * @throws TException if an exception occurs
- */
- void checkedAccept(TParam1 t, TParam2 u) throws TException;
-
- /**
- * unchecked wrapper for {@link #checkedAccept(Object, Object)}
- * If a {@link TException} occurs, an unchecked one might be thrown by {@link #resultOnError(Throwable, Object...)}
- *
- * @param t the first input
- * @param u the second input
- * @see #checkedAccept(Object, Object)
- * @see #resultOnError(Throwable, Object...)
- */
- @Override
- default void accept(TParam1 t, TParam2 u) {
- try {
- checkedAccept(t, u);
- } catch (Throwable ex) {
- handleGenericException(ex, t, u);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- default CheckedBiConsumer<TParam1, TParam2, TException> handleExceptionsWith(ExceptionHandler handler) {
- return new CheckedBiConsumer<TParam1, TParam2, TException>() {
- @Override
- public void checkedAccept(TParam1 t, TParam2 u) throws TException {
- CheckedBiConsumer.this.checkedAccept(t, u);
- }
-
- @Override
- public Void handleGenericException(Throwable thrown, Object... args) {
- handler.handleGenericException(thrown, args);
- return null;
- }
-
- @Override
- public CheckedBiConsumer<TParam1, TParam2, TException> handleExceptionsWith(ExceptionHandler handler) {
- return CheckedBiConsumer.this.handleExceptionsWith(handler);
- }
- };
- }
-
-}
+package io.dico.dicore.exceptions.checkedfunctions;
+
+import io.dico.dicore.exceptions.ExceptionHandler;
+
+import java.util.function.BiConsumer;
+
+/**
+ * checked mimic of {@link BiConsumer}
+ *
+ * @param <TParam1>
+ * @param <TParam2>
+ * @param <TException>
+ */
+@FunctionalInterface
+public interface CheckedBiConsumer<TParam1, TParam2, TException extends Throwable>
+ extends CheckedFunctionalObject<Void, TException>, BiConsumer<TParam1, TParam2> {
+
+ /**
+ * The consuming action
+ *
+ * @param t the first argument to consume
+ * @param u the second argument to consume
+ * @throws TException if an exception occurs
+ */
+ void checkedAccept(TParam1 t, TParam2 u) throws TException;
+
+ /**
+ * unchecked wrapper for {@link #checkedAccept(Object, Object)}
+ * If a {@link TException} occurs, an unchecked one might be thrown by {@link #resultOnError(Throwable, Object...)}
+ *
+ * @param t the first input
+ * @param u the second input
+ * @see #checkedAccept(Object, Object)
+ * @see #resultOnError(Throwable, Object...)
+ */
+ @Override
+ default void accept(TParam1 t, TParam2 u) {
+ try {
+ checkedAccept(t, u);
+ } catch (Throwable ex) {
+ handleGenericException(ex, t, u);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ default CheckedBiConsumer<TParam1, TParam2, TException> handleExceptionsWith(ExceptionHandler handler) {
+ return new CheckedBiConsumer<TParam1, TParam2, TException>() {
+ @Override
+ public void checkedAccept(TParam1 t, TParam2 u) throws TException {
+ CheckedBiConsumer.this.checkedAccept(t, u);
+ }
+
+ @Override
+ public Void handleGenericException(Throwable thrown, Object... args) {
+ handler.handleGenericException(thrown, args);
+ return null;
+ }
+
+ @Override
+ public CheckedBiConsumer<TParam1, TParam2, TException> handleExceptionsWith(ExceptionHandler handler) {
+ return CheckedBiConsumer.this.handleExceptionsWith(handler);
+ }
+ };
+ }
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedBiFunction.java b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedBiFunction.java
index 9b39325..11fca3f 100644
--- a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedBiFunction.java
+++ b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedBiFunction.java
@@ -1,76 +1,76 @@
-package io.dico.dicore.exceptions.checkedfunctions;
-
-import io.dico.dicore.exceptions.ExceptionHandler;
-
-import java.util.function.BiFunction;
-
-/**
- * checked mimic of {@link BiFunction}
- *
- * @param <TParam1>
- * @param <TParam2>
- * @param <TResult>
- * @param <TException>
- */
-@FunctionalInterface
-public interface CheckedBiFunction<TParam1, TParam2, TResult, TException extends Throwable>
- extends CheckedFunctionalObject<TResult, TException>, BiFunction<TParam1, TParam2, TResult> {
-
- /**
- * the functional method
- *
- * @param t the first input
- * @param u the second input
- * @return the function output
- * @throws TException if an exception occurs
- */
- TResult checkedApply(TParam1 t, TParam2 u) throws TException;
-
- /**
- * unchecked wrapper for {@link #checkedApply(Object, Object)}
- * If a {@link TException} occurs, an unchecked one might be thrown by {@link #resultOnError(Throwable, Object...)}
- *
- * @param t the first input
- * @param u the second input
- * @return the function output, or the result from {@link #resultOnError(Throwable, Object...)} if an exception was thrown
- * @see #checkedApply(Object, Object)
- * @see #resultOnError(Throwable, Object...)
- */
- @Override
- default TResult apply(TParam1 t, TParam2 u) {
- try {
- return checkedApply(t, u);
- } catch (Throwable ex) {
- return handleGenericException(ex, t, u);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- default CheckedBiFunction<TParam1, TParam2, TResult, TException> handleExceptionsWith(ExceptionHandler handler) {
- return new CheckedBiFunction<TParam1, TParam2, TResult, TException>() {
- @Override
- public TResult checkedApply(TParam1 t, TParam2 u) throws TException {
- return CheckedBiFunction.this.checkedApply(t, u);
- }
-
- @Override
- @SuppressWarnings("unchecked")
- public TResult handleGenericException(Throwable thrown, Object... args) {
- Object result = handler.handleGenericException(thrown, args);
- try {
- return (TResult) result;
- } catch (Exception ex) {
- return null;
- }
- }
-
- @Override
- public CheckedBiFunction<TParam1, TParam2, TResult, TException> handleExceptionsWith(ExceptionHandler handler) {
- return CheckedBiFunction.this.handleExceptionsWith(handler);
- }
- };
- }
-}
+package io.dico.dicore.exceptions.checkedfunctions;
+
+import io.dico.dicore.exceptions.ExceptionHandler;
+
+import java.util.function.BiFunction;
+
+/**
+ * checked mimic of {@link BiFunction}
+ *
+ * @param <TParam1>
+ * @param <TParam2>
+ * @param <TResult>
+ * @param <TException>
+ */
+@FunctionalInterface
+public interface CheckedBiFunction<TParam1, TParam2, TResult, TException extends Throwable>
+ extends CheckedFunctionalObject<TResult, TException>, BiFunction<TParam1, TParam2, TResult> {
+
+ /**
+ * the functional method
+ *
+ * @param t the first input
+ * @param u the second input
+ * @return the function output
+ * @throws TException if an exception occurs
+ */
+ TResult checkedApply(TParam1 t, TParam2 u) throws TException;
+
+ /**
+ * unchecked wrapper for {@link #checkedApply(Object, Object)}
+ * If a {@link TException} occurs, an unchecked one might be thrown by {@link #resultOnError(Throwable, Object...)}
+ *
+ * @param t the first input
+ * @param u the second input
+ * @return the function output, or the result from {@link #resultOnError(Throwable, Object...)} if an exception was thrown
+ * @see #checkedApply(Object, Object)
+ * @see #resultOnError(Throwable, Object...)
+ */
+ @Override
+ default TResult apply(TParam1 t, TParam2 u) {
+ try {
+ return checkedApply(t, u);
+ } catch (Throwable ex) {
+ return handleGenericException(ex, t, u);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ default CheckedBiFunction<TParam1, TParam2, TResult, TException> handleExceptionsWith(ExceptionHandler handler) {
+ return new CheckedBiFunction<TParam1, TParam2, TResult, TException>() {
+ @Override
+ public TResult checkedApply(TParam1 t, TParam2 u) throws TException {
+ return CheckedBiFunction.this.checkedApply(t, u);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public TResult handleGenericException(Throwable thrown, Object... args) {
+ Object result = handler.handleGenericException(thrown, args);
+ try {
+ return (TResult) result;
+ } catch (Exception ex) {
+ return null;
+ }
+ }
+
+ @Override
+ public CheckedBiFunction<TParam1, TParam2, TResult, TException> handleExceptionsWith(ExceptionHandler handler) {
+ return CheckedBiFunction.this.handleExceptionsWith(handler);
+ }
+ };
+ }
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedConsumer.java b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedConsumer.java
index fa48ae8..abe6d53 100644
--- a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedConsumer.java
+++ b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedConsumer.java
@@ -1,67 +1,67 @@
-package io.dico.dicore.exceptions.checkedfunctions;
-
-import io.dico.dicore.exceptions.ExceptionHandler;
-
-import java.util.function.Consumer;
-
-/**
- * checked mimic of {@link Consumer}
- *
- * @param <TParam>
- * @param <TException>
- */
-@FunctionalInterface
-public interface CheckedConsumer<TParam, TException extends Throwable>
- extends CheckedFunctionalObject<Void, TException>, Consumer<TParam> {
-
- /**
- * The consuming action
- *
- * @param t the argument to consume
- * @throws TException if an error occurs
- */
- void checkedAccept(TParam t) throws TException;
-
- /**
- * Unchecked version of {@link #checkedAccept(Object)}
- * If a {@link TException} occurs, an unchecked one might be thrown by {@link #resultOnError(Throwable, Object...)}
- *
- * @param t the argument to consume
- * @see #checkedAccept(Object)
- * @see #resultOnError(Throwable, Object...)
- */
- @Override
- default void accept(TParam t) {
- try {
- checkedAccept(t);
- } catch (Throwable ex) {
- handleGenericException(ex, t);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- default CheckedConsumer<TParam, TException> handleExceptionsWith(ExceptionHandler handler) {
- return new CheckedConsumer<TParam, TException>() {
- @Override
- public void checkedAccept(TParam t) throws TException {
- CheckedConsumer.this.checkedAccept(t);
- }
-
- @Override
- @SuppressWarnings("unchecked")
- public Void handleGenericException(Throwable thrown, Object... args) {
- handler.handleGenericException(thrown, args);
- return null;
- }
-
- @Override
- public CheckedConsumer<TParam, TException> handleExceptionsWith(ExceptionHandler handler) {
- return CheckedConsumer.this.handleExceptionsWith(handler);
- }
- };
- }
-
-}
+package io.dico.dicore.exceptions.checkedfunctions;
+
+import io.dico.dicore.exceptions.ExceptionHandler;
+
+import java.util.function.Consumer;
+
+/**
+ * checked mimic of {@link Consumer}
+ *
+ * @param <TParam>
+ * @param <TException>
+ */
+@FunctionalInterface
+public interface CheckedConsumer<TParam, TException extends Throwable>
+ extends CheckedFunctionalObject<Void, TException>, Consumer<TParam> {
+
+ /**
+ * The consuming action
+ *
+ * @param t the argument to consume
+ * @throws TException if an error occurs
+ */
+ void checkedAccept(TParam t) throws TException;
+
+ /**
+ * Unchecked version of {@link #checkedAccept(Object)}
+ * If a {@link TException} occurs, an unchecked one might be thrown by {@link #resultOnError(Throwable, Object...)}
+ *
+ * @param t the argument to consume
+ * @see #checkedAccept(Object)
+ * @see #resultOnError(Throwable, Object...)
+ */
+ @Override
+ default void accept(TParam t) {
+ try {
+ checkedAccept(t);
+ } catch (Throwable ex) {
+ handleGenericException(ex, t);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ default CheckedConsumer<TParam, TException> handleExceptionsWith(ExceptionHandler handler) {
+ return new CheckedConsumer<TParam, TException>() {
+ @Override
+ public void checkedAccept(TParam t) throws TException {
+ CheckedConsumer.this.checkedAccept(t);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Void handleGenericException(Throwable thrown, Object... args) {
+ handler.handleGenericException(thrown, args);
+ return null;
+ }
+
+ @Override
+ public CheckedConsumer<TParam, TException> handleExceptionsWith(ExceptionHandler handler) {
+ return CheckedConsumer.this.handleExceptionsWith(handler);
+ }
+ };
+ }
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedFunction.java b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedFunction.java
index 9ff806a..48ac207 100644
--- a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedFunction.java
+++ b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedFunction.java
@@ -1,74 +1,74 @@
-package io.dico.dicore.exceptions.checkedfunctions;
-
-import io.dico.dicore.exceptions.ExceptionHandler;
-
-import java.util.function.Function;
-
-/**
- * checked mimic of {@link Function}
- *
- * @param <TParam>
- * @param <TResult>
- * @param <TException>
- */
-@FunctionalInterface
-public interface CheckedFunction<TParam, TResult, TException extends Throwable>
- extends CheckedFunctionalObject<TResult, TException>, Function<TParam, TResult> {
-
- /**
- * the functional method
- *
- * @param t the input
- * @return the function output
- * @throws TException if an exception occurs
- */
- TResult checkedApply(TParam t) throws TException;
-
- /**
- * unchecked wrapper for {@link #checkedApply(Object)}
- * If a {@link TException} occurs, an unchecked one might be thrown by {@link #resultOnError(Throwable, Object...)}
- *
- * @param t the input
- * @return the function output, or the result from {@link #resultOnError(Throwable, Object...)} if an exception was thrown
- * @see #checkedApply(Object)
- * @see #resultOnError(Throwable, Object...)
- */
- @Override
- default TResult apply(TParam t) {
- try {
- return checkedApply(t);
- } catch (Throwable ex) {
- return handleGenericException(ex, t);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- default CheckedFunction<TParam, TResult, TException> handleExceptionsWith(ExceptionHandler handler) {
- return new CheckedFunction<TParam, TResult, TException>() {
- @Override
- public TResult checkedApply(TParam t) throws TException {
- return CheckedFunction.this.checkedApply(t);
- }
-
- @Override
- @SuppressWarnings("unchecked")
- public TResult handleGenericException(Throwable thrown, Object... args) {
- Object result = handler.handleGenericException(thrown, args);
- try {
- return (TResult) result;
- } catch (Exception ex) {
- return null;
- }
- }
-
- @Override
- public CheckedFunction<TParam, TResult, TException> handleExceptionsWith(ExceptionHandler handler) {
- return CheckedFunction.this.handleExceptionsWith(handler);
- }
- };
- }
-
-}
+package io.dico.dicore.exceptions.checkedfunctions;
+
+import io.dico.dicore.exceptions.ExceptionHandler;
+
+import java.util.function.Function;
+
+/**
+ * checked mimic of {@link Function}
+ *
+ * @param <TParam>
+ * @param <TResult>
+ * @param <TException>
+ */
+@FunctionalInterface
+public interface CheckedFunction<TParam, TResult, TException extends Throwable>
+ extends CheckedFunctionalObject<TResult, TException>, Function<TParam, TResult> {
+
+ /**
+ * the functional method
+ *
+ * @param t the input
+ * @return the function output
+ * @throws TException if an exception occurs
+ */
+ TResult checkedApply(TParam t) throws TException;
+
+ /**
+ * unchecked wrapper for {@link #checkedApply(Object)}
+ * If a {@link TException} occurs, an unchecked one might be thrown by {@link #resultOnError(Throwable, Object...)}
+ *
+ * @param t the input
+ * @return the function output, or the result from {@link #resultOnError(Throwable, Object...)} if an exception was thrown
+ * @see #checkedApply(Object)
+ * @see #resultOnError(Throwable, Object...)
+ */
+ @Override
+ default TResult apply(TParam t) {
+ try {
+ return checkedApply(t);
+ } catch (Throwable ex) {
+ return handleGenericException(ex, t);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ default CheckedFunction<TParam, TResult, TException> handleExceptionsWith(ExceptionHandler handler) {
+ return new CheckedFunction<TParam, TResult, TException>() {
+ @Override
+ public TResult checkedApply(TParam t) throws TException {
+ return CheckedFunction.this.checkedApply(t);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public TResult handleGenericException(Throwable thrown, Object... args) {
+ Object result = handler.handleGenericException(thrown, args);
+ try {
+ return (TResult) result;
+ } catch (Exception ex) {
+ return null;
+ }
+ }
+
+ @Override
+ public CheckedFunction<TParam, TResult, TException> handleExceptionsWith(ExceptionHandler handler) {
+ return CheckedFunction.this.handleExceptionsWith(handler);
+ }
+ };
+ }
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedFunctionalObject.java b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedFunctionalObject.java
index e84b33d..d9d7a53 100644
--- a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedFunctionalObject.java
+++ b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedFunctionalObject.java
@@ -1,85 +1,85 @@
-package io.dico.dicore.exceptions.checkedfunctions;
-
-import io.dico.dicore.exceptions.ExceptionHandler;
-
-/**
- * Base interface for all checked functional interfaces
- * Most subinterfaces will mimic interfaces in the package {@link java.util.function}
- * <p>
- * Checked functional interfaces are functions with throws declarations.
- * The name comes from the fact that they can throw <b>checked</b> exceptions.
- * <p>
- * They extend their non-checked counterparts, whose methods are implemented by
- * returning the result of {@link #resultOnError(TException, Object...)} when an exception is thrown
- * <p>
- * Made public to allow more specialized checked functional interfaces to subclass it.
- * Making primitive versions shouldn't provide a significant performance increase because we're checking for exceptions,
- * the performance impact of which is probably a few magnitudes larger. Don't quote me on this.
- *
- * @param <TResult> The return type of this functional interface's method
- * @param <TException> The type of exception that might be thrown
- */
-public interface CheckedFunctionalObject<TResult, TException extends Throwable> extends ExceptionHandler {
-
- /**
- * {@inheritDoc}
- *
- * @param ex The exception to be handled
- */
- @Override
- default void handle(Throwable ex) {
- handleGenericException(ex);
- }
-
- /**
- * {@inheritDoc}
- * <p>
- * Method to handle exceptions thrown by the default implementations of subinterfaces.
- * Since you can't catch a type parameter as exception, this code is in place to take care of it.
- * <p>
- * If the thrown exception is not a TException, an unchecked version is thrown by calling this method.
- *
- * @param thrown The thrown exception
- * @param args the arguments supplied to the method that threw the exception, if any. These are not guaranteed to be given if parameters are present.
- * @return The result computed by {@link #resultOnError(Throwable, Object...)}
- * @see #resultOnError(Throwable, Object...)
- */
- @Override
- @SuppressWarnings("unchecked")
- default TResult handleGenericException(Throwable thrown, Object... args) {
-
- // check if the throwable is a TException
- TException castedException;
- try {
- castedException = (TException) thrown;
- } catch (ClassCastException ex) {
- // if not, throw an unchecked version of it
- ExceptionHandler.UNCHECKED.handleGenericException(thrown);
- // this code is never reached.
- return null;
- }
-
- // if it is a TException, use resultOnError to compute result.
- return resultOnError(castedException, args);
- }
-
- /**
- * This method handles the exceptions thrown by the checked method of this object, when called by its unchecked wrapper.
- *
- * @param ex The exception thrown
- * @param args The (typed) arguments passed, if any
- * @return The result to return, if any
- */
- default TResult resultOnError(TException ex, Object... args) {
- return null;
- }
-
- /**
- * Creates a new functional object that uses the given exception handler to handle exceptions.
- *
- * @param handler The handler to handle exceptions
- * @return a new functional object that uses the given exception handler to handle exceptions
- */
- CheckedFunctionalObject<TResult, TException> handleExceptionsWith(ExceptionHandler handler);
-
-}
+package io.dico.dicore.exceptions.checkedfunctions;
+
+import io.dico.dicore.exceptions.ExceptionHandler;
+
+/**
+ * Base interface for all checked functional interfaces
+ * Most subinterfaces will mimic interfaces in the package {@link java.util.function}
+ * <p>
+ * Checked functional interfaces are functions with throws declarations.
+ * The name comes from the fact that they can throw <b>checked</b> exceptions.
+ * <p>
+ * They extend their non-checked counterparts, whose methods are implemented by
+ * returning the result of {@link #resultOnError(TException, Object...)} when an exception is thrown
+ * <p>
+ * Made public to allow more specialized checked functional interfaces to subclass it.
+ * Making primitive versions shouldn't provide a significant performance increase because we're checking for exceptions,
+ * the performance impact of which is probably a few magnitudes larger. Don't quote me on this.
+ *
+ * @param <TResult> The return type of this functional interface's method
+ * @param <TException> The type of exception that might be thrown
+ */
+public interface CheckedFunctionalObject<TResult, TException extends Throwable> extends ExceptionHandler {
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param ex The exception to be handled
+ */
+ @Override
+ default void handle(Throwable ex) {
+ handleGenericException(ex);
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * Method to handle exceptions thrown by the default implementations of subinterfaces.
+ * Since you can't catch a type parameter as exception, this code is in place to take care of it.
+ * <p>
+ * If the thrown exception is not a TException, an unchecked version is thrown by calling this method.
+ *
+ * @param thrown The thrown exception
+ * @param args the arguments supplied to the method that threw the exception, if any. These are not guaranteed to be given if parameters are present.
+ * @return The result computed by {@link #resultOnError(Throwable, Object...)}
+ * @see #resultOnError(Throwable, Object...)
+ */
+ @Override
+ @SuppressWarnings("unchecked")
+ default TResult handleGenericException(Throwable thrown, Object... args) {
+
+ // check if the throwable is a TException
+ TException castedException;
+ try {
+ castedException = (TException) thrown;
+ } catch (ClassCastException ex) {
+ // if not, throw an unchecked version of it
+ ExceptionHandler.UNCHECKED.handleGenericException(thrown);
+ // this code is never reached.
+ return null;
+ }
+
+ // if it is a TException, use resultOnError to compute result.
+ return resultOnError(castedException, args);
+ }
+
+ /**
+ * This method handles the exceptions thrown by the checked method of this object, when called by its unchecked wrapper.
+ *
+ * @param ex The exception thrown
+ * @param args The (typed) arguments passed, if any
+ * @return The result to return, if any
+ */
+ default TResult resultOnError(TException ex, Object... args) {
+ return null;
+ }
+
+ /**
+ * Creates a new functional object that uses the given exception handler to handle exceptions.
+ *
+ * @param handler The handler to handle exceptions
+ * @return a new functional object that uses the given exception handler to handle exceptions
+ */
+ CheckedFunctionalObject<TResult, TException> handleExceptionsWith(ExceptionHandler handler);
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedRunnable.java b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedRunnable.java
index 55de6f8..110c988 100644
--- a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedRunnable.java
+++ b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedRunnable.java
@@ -1,62 +1,62 @@
-package io.dico.dicore.exceptions.checkedfunctions;
-
-import io.dico.dicore.exceptions.ExceptionHandler;
-
-/**
- * checked mimic of {@link Runnable}
- *
- * @param <TException>
- */
-@FunctionalInterface
-public interface CheckedRunnable<TException extends Throwable>
- extends CheckedFunctionalObject<Void, TException>, Runnable {
-
- /**
- * The runnable action
- *
- * @throws TException if an exception occurs
- */
- void checkedRun() throws TException;
-
- /**
- * Unchecked version of {@link #checkedRun()}
- * If a {@link TException} occurs, an unchecked one might be thrown by {@link #resultOnError(Throwable, Object...)}
- *
- * @see #checkedRun()
- * @see #resultOnError(Throwable, Object...)
- */
- @Override
- default void run() {
- try {
- checkedRun();
- } catch (Throwable ex) {
- handleGenericException(ex);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- default CheckedRunnable<TException> handleExceptionsWith(ExceptionHandler handler) {
- return new CheckedRunnable<TException>() {
- @Override
- public void checkedRun() throws TException {
- CheckedRunnable.this.checkedRun();
- }
-
- @Override
- @SuppressWarnings("unchecked")
- public Void handleGenericException(Throwable thrown, Object... args) {
- handler.handleGenericException(thrown, args);
- return null;
- }
-
- @Override
- public CheckedRunnable<TException> handleExceptionsWith(ExceptionHandler handler) {
- return CheckedRunnable.this.handleExceptionsWith(handler);
- }
- };
- }
-
-}
+package io.dico.dicore.exceptions.checkedfunctions;
+
+import io.dico.dicore.exceptions.ExceptionHandler;
+
+/**
+ * checked mimic of {@link Runnable}
+ *
+ * @param <TException>
+ */
+@FunctionalInterface
+public interface CheckedRunnable<TException extends Throwable>
+ extends CheckedFunctionalObject<Void, TException>, Runnable {
+
+ /**
+ * The runnable action
+ *
+ * @throws TException if an exception occurs
+ */
+ void checkedRun() throws TException;
+
+ /**
+ * Unchecked version of {@link #checkedRun()}
+ * If a {@link TException} occurs, an unchecked one might be thrown by {@link #resultOnError(Throwable, Object...)}
+ *
+ * @see #checkedRun()
+ * @see #resultOnError(Throwable, Object...)
+ */
+ @Override
+ default void run() {
+ try {
+ checkedRun();
+ } catch (Throwable ex) {
+ handleGenericException(ex);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ default CheckedRunnable<TException> handleExceptionsWith(ExceptionHandler handler) {
+ return new CheckedRunnable<TException>() {
+ @Override
+ public void checkedRun() throws TException {
+ CheckedRunnable.this.checkedRun();
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Void handleGenericException(Throwable thrown, Object... args) {
+ handler.handleGenericException(thrown, args);
+ return null;
+ }
+
+ @Override
+ public CheckedRunnable<TException> handleExceptionsWith(ExceptionHandler handler) {
+ return CheckedRunnable.this.handleExceptionsWith(handler);
+ }
+ };
+ }
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedSupplier.java b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedSupplier.java
index dec2e7e..7820428 100644
--- a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedSupplier.java
+++ b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedSupplier.java
@@ -1,70 +1,70 @@
-package io.dico.dicore.exceptions.checkedfunctions;
-
-import io.dico.dicore.exceptions.ExceptionHandler;
-
-import java.util.function.Supplier;
-
-/**
- * checked mimic of {@link Supplier}
- *
- * @param <TResult>
- * @param <TException>
- */
-@FunctionalInterface
-public interface CheckedSupplier<TResult, TException extends Throwable>
- extends CheckedFunctionalObject<TResult, TException>, Supplier<TResult> {
-
- /**
- * The computation
- *
- * @return the result of this computation
- * @throws TException if an error occurs
- */
- TResult checkedGet() throws TException;
-
- /**
- * Unchecked version of {@link #checkedGet()}
- * If a {@link TException} occurs, an unchecked one might be thrown by {@link #resultOnError(Throwable, Object...)}
- *
- * @return the result of this computation
- * @see #checkedGet()
- * @see #resultOnError(Throwable, Object...)
- */
- @Override
- default TResult get() {
- try {
- return checkedGet();
- } catch (Throwable ex) {
- return handleGenericException(ex);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- default CheckedSupplier<TResult, TException> handleExceptionsWith(ExceptionHandler handler) {
- return new CheckedSupplier<TResult, TException>() {
- @Override
- public TResult checkedGet() throws TException {
- return CheckedSupplier.this.checkedGet();
- }
-
- @Override
- @SuppressWarnings("unchecked")
- public TResult handleGenericException(Throwable thrown, Object... args) {
- Object result = handler.handleGenericException(thrown, args);
- try {
- return (TResult) result;
- } catch (Exception ex) {
- return null;
- }
- }
-
- @Override
- public CheckedSupplier<TResult, TException> handleExceptionsWith(ExceptionHandler handler) {
- return CheckedSupplier.this.handleExceptionsWith(handler);
- }
- };
- }
-}
+package io.dico.dicore.exceptions.checkedfunctions;
+
+import io.dico.dicore.exceptions.ExceptionHandler;
+
+import java.util.function.Supplier;
+
+/**
+ * checked mimic of {@link Supplier}
+ *
+ * @param <TResult>
+ * @param <TException>
+ */
+@FunctionalInterface
+public interface CheckedSupplier<TResult, TException extends Throwable>
+ extends CheckedFunctionalObject<TResult, TException>, Supplier<TResult> {
+
+ /**
+ * The computation
+ *
+ * @return the result of this computation
+ * @throws TException if an error occurs
+ */
+ TResult checkedGet() throws TException;
+
+ /**
+ * Unchecked version of {@link #checkedGet()}
+ * If a {@link TException} occurs, an unchecked one might be thrown by {@link #resultOnError(Throwable, Object...)}
+ *
+ * @return the result of this computation
+ * @see #checkedGet()
+ * @see #resultOnError(Throwable, Object...)
+ */
+ @Override
+ default TResult get() {
+ try {
+ return checkedGet();
+ } catch (Throwable ex) {
+ return handleGenericException(ex);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ default CheckedSupplier<TResult, TException> handleExceptionsWith(ExceptionHandler handler) {
+ return new CheckedSupplier<TResult, TException>() {
+ @Override
+ public TResult checkedGet() throws TException {
+ return CheckedSupplier.this.checkedGet();
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public TResult handleGenericException(Throwable thrown, Object... args) {
+ Object result = handler.handleGenericException(thrown, args);
+ try {
+ return (TResult) result;
+ } catch (Exception ex) {
+ return null;
+ }
+ }
+
+ @Override
+ public CheckedSupplier<TResult, TException> handleExceptionsWith(ExceptionHandler handler) {
+ return CheckedSupplier.this.handleExceptionsWith(handler);
+ }
+ };
+ }
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/task/BaseTask.java b/dicore3/core/src/main/java/io/dico/dicore/task/BaseTask.java
index b400cce..43db459 100644
--- a/dicore3/core/src/main/java/io/dico/dicore/task/BaseTask.java
+++ b/dicore3/core/src/main/java/io/dico/dicore/task/BaseTask.java
@@ -1,108 +1,108 @@
-package io.dico.dicore.task;
-
-import org.bukkit.Bukkit;
-import org.bukkit.plugin.Plugin;
-
-import java.util.NoSuchElementException;
-
-public abstract class BaseTask<T> {
- private boolean running = false;
- private Integer taskId = null;
- private long workTime = 5L;
- private int workCount;
-
- public void start(Plugin plugin, int delay, int period, long workTime) {
- doStartChecks();
- this.workTime = workTime;
- workCount = 0;
- running = true;
-
- if (delay == -1) {
- run();
- if (!running) {
- return;
- }
- delay = period;
- }
-
- taskId = plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, this::run, delay, period);
- }
-
- public void startImmediately(Plugin plugin, int period, long workTime) {
- start(plugin, -1, period, workTime);
- }
-
- protected void doStartChecks() {
- if (isRunning()) {
- throw new IllegalStateException("Can't start when already running");
- }
- }
-
- public void start(Plugin plugin) {
- start(plugin, -1, 20, 5L);
- }
-
- protected void onFinish(boolean early) {
- }
-
- protected long getWorkTime() {
- return workTime;
- }
-
- protected abstract boolean process(T object);
-
- private void run() {
- workCount++;
- final long stop = System.currentTimeMillis() + getWorkTime();
- do {
- if (!processNext()) {
- return;
- }
- } while (System.currentTimeMillis() < stop);
- }
-
- public int getTaskId() {
- return running ? taskId : -1;
- }
-
- public int getWorkCount() {
- return workCount;
- }
-
- public boolean isRunning() {
- return running;
- }
-
- protected abstract T supply() throws NoSuchElementException;
-
- private void cancelTask(boolean early) {
- if (taskId != null) {
- Bukkit.getScheduler().cancelTask(taskId);
- }
- running = false;
- taskId = null;
- onFinish(early);
- }
-
- private boolean processNext() {
- T object;
- try {
- object = supply();
- } catch (NoSuchElementException e) {
- cancelTask(false);
- return false;
- }
-
- try {
- if (process(object)) {
- return true;
- }
- } catch (RuntimeException e) {
- e.printStackTrace();
- }
-
- cancelTask(true);
- return false;
- }
-
-}
+package io.dico.dicore.task;
+
+import org.bukkit.Bukkit;
+import org.bukkit.plugin.Plugin;
+
+import java.util.NoSuchElementException;
+
+public abstract class BaseTask<T> {
+ private boolean running = false;
+ private Integer taskId = null;
+ private long workTime = 5L;
+ private int workCount;
+
+ public void start(Plugin plugin, int delay, int period, long workTime) {
+ doStartChecks();
+ this.workTime = workTime;
+ workCount = 0;
+ running = true;
+
+ if (delay == -1) {
+ run();
+ if (!running) {
+ return;
+ }
+ delay = period;
+ }
+
+ taskId = plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, this::run, delay, period);
+ }
+
+ public void startImmediately(Plugin plugin, int period, long workTime) {
+ start(plugin, -1, period, workTime);
+ }
+
+ protected void doStartChecks() {
+ if (isRunning()) {
+ throw new IllegalStateException("Can't start when already running");
+ }
+ }
+
+ public void start(Plugin plugin) {
+ start(plugin, -1, 20, 5L);
+ }
+
+ protected void onFinish(boolean early) {
+ }
+
+ protected long getWorkTime() {
+ return workTime;
+ }
+
+ protected abstract boolean process(T object);
+
+ private void run() {
+ workCount++;
+ final long stop = System.currentTimeMillis() + getWorkTime();
+ do {
+ if (!processNext()) {
+ return;
+ }
+ } while (System.currentTimeMillis() < stop);
+ }
+
+ public int getTaskId() {
+ return running ? taskId : -1;
+ }
+
+ public int getWorkCount() {
+ return workCount;
+ }
+
+ public boolean isRunning() {
+ return running;
+ }
+
+ protected abstract T supply() throws NoSuchElementException;
+
+ private void cancelTask(boolean early) {
+ if (taskId != null) {
+ Bukkit.getScheduler().cancelTask(taskId);
+ }
+ running = false;
+ taskId = null;
+ onFinish(early);
+ }
+
+ private boolean processNext() {
+ T object;
+ try {
+ object = supply();
+ } catch (NoSuchElementException e) {
+ cancelTask(false);
+ return false;
+ }
+
+ try {
+ if (process(object)) {
+ return true;
+ }
+ } catch (RuntimeException e) {
+ e.printStackTrace();
+ }
+
+ cancelTask(true);
+ return false;
+ }
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/task/IteratorTask.java b/dicore3/core/src/main/java/io/dico/dicore/task/IteratorTask.java
index e58814c..3ff7be7 100644
--- a/dicore3/core/src/main/java/io/dico/dicore/task/IteratorTask.java
+++ b/dicore3/core/src/main/java/io/dico/dicore/task/IteratorTask.java
@@ -1,120 +1,120 @@
-package io.dico.dicore.task;
-
-import java.util.*;
-import java.util.function.BiConsumer;
-import java.util.function.BiPredicate;
-import java.util.function.Consumer;
-import java.util.function.Predicate;
-
-public abstract class IteratorTask<T> extends BaseTask<T> {
-
- private Iterator<? extends T> iterator;
-
- public IteratorTask() {
- }
-
- @SuppressWarnings("unchecked")
- public IteratorTask(Iterable<? extends T> iterable, boolean clone) {
- refresh(iterable, clone);
- }
-
- public IteratorTask(Iterable<? extends T> iterable) {
- this(iterable, false);
- }
-
- public IteratorTask(Iterator<? extends T> iterator) {
- refresh(iterator);
- }
-
- @Override
- protected void doStartChecks() {
- super.doStartChecks();
- if (iterator == null) {
- throw new IllegalStateException("An iterator must be supplied first");
- }
- }
-
- protected final void refresh(Iterable<? extends T> iterable, boolean clone) {
- if (clone) {
- Collection<T> collection;
- if (!(iterable instanceof Collection)) {
- collection = new LinkedList<>();
- for (T next : iterable) {
- collection.add(next);
- }
- } else {
- collection = new ArrayList((Collection) iterable);
- }
- iterator = collection.iterator();
- } else {
- iterator = iterable.iterator();
- }
- }
-
- protected final void refresh(Iterator<? extends T> iterator) {
- Objects.requireNonNull(iterator);
- this.iterator = iterator;
- }
-
- @Override
- protected T supply() {
- return iterator.next();
- }
-
- protected void remove() {
- iterator.remove();
- }
-
- // One argument: The processed object
-
- public static <T> IteratorTask<T> create(Iterable<? extends T> iterable, Consumer<T> processor) {
- return create(iterable, false, processor);
- }
-
- public static <T> IteratorTask<T> create(Iterable<? extends T> iterable, boolean clone, Consumer<T> processor) {
- return create(iterable, clone, object -> {
- processor.accept(object);
- return true;
- });
- }
-
- public static <T> IteratorTask<T> create(Iterable<? extends T> iterable, Predicate<T> processor) {
- return create(iterable, false, processor);
- }
-
- public static <T> IteratorTask<T> create(Iterable<? extends T> iterable, boolean clone, Predicate<T> processor) {
- return new IteratorTask<T>(iterable, clone) {
- @Override
- protected boolean process(T object) {
- return processor.test(object);
- }
- };
- }
-
- // Two arguments: the processed object, and a runnable to remove it from the iterator.
-
- public static <T> IteratorTask<T> create(Iterable<? extends T> iterable, BiConsumer<T, Runnable> processor) {
- return create(iterable, false, processor);
- }
-
- public static <T> IteratorTask<T> create(Iterable<? extends T> iterable, boolean clone, BiConsumer<T, Runnable> processor) {
- return create(iterable, clone, (object, runnable) -> {
- processor.accept(object, runnable);
- return true;
- });
- }
-
- public static <T> IteratorTask<T> create(Iterable<? extends T> iterable, BiPredicate<T, Runnable> processor) {
- return create(iterable, false, processor);
- }
-
- public static <T> IteratorTask<T> create(Iterable<? extends T> iterable, boolean clone, BiPredicate<T, Runnable> processor) {
- return new IteratorTask<T>(iterable, clone) {
- @Override
- protected boolean process(T object) {
- return processor.test(object, this::remove);
- }
- };
- }
-
-}
+package io.dico.dicore.task;
+
+import java.util.*;
+import java.util.function.BiConsumer;
+import java.util.function.BiPredicate;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+public abstract class IteratorTask<T> extends BaseTask<T> {
+
+ private Iterator<? extends T> iterator;
+
+ public IteratorTask() {
+ }
+
+ @SuppressWarnings("unchecked")
+ public IteratorTask(Iterable<? extends T> iterable, boolean clone) {
+ refresh(iterable, clone);
+ }
+
+ public IteratorTask(Iterable<? extends T> iterable) {
+ this(iterable, false);
+ }
+
+ public IteratorTask(Iterator<? extends T> iterator) {
+ refresh(iterator);
+ }
+
+ @Override
+ protected void doStartChecks() {
+ super.doStartChecks();
+ if (iterator == null) {
+ throw new IllegalStateException("An iterator must be supplied first");
+ }
+ }
+
+ protected final void refresh(Iterable<? extends T> iterable, boolean clone) {
+ if (clone) {
+ Collection<T> collection;
+ if (!(iterable instanceof Collection)) {
+ collection = new LinkedList<>();
+ for (T next : iterable) {
+ collection.add(next);
+ }
+ } else {
+ collection = new ArrayList((Collection) iterable);
+ }
+ iterator = collection.iterator();
+ } else {
+ iterator = iterable.iterator();
+ }
+ }
+
+ protected final void refresh(Iterator<? extends T> iterator) {
+ Objects.requireNonNull(iterator);
+ this.iterator = iterator;
+ }
+
+ @Override
+ protected T supply() {
+ return iterator.next();
+ }
+
+ protected void remove() {
+ iterator.remove();
+ }
+
+ // One argument: The processed object
+
+ public static <T> IteratorTask<T> create(Iterable<? extends T> iterable, Consumer<T> processor) {
+ return create(iterable, false, processor);
+ }
+
+ public static <T> IteratorTask<T> create(Iterable<? extends T> iterable, boolean clone, Consumer<T> processor) {
+ return create(iterable, clone, object -> {
+ processor.accept(object);
+ return true;
+ });
+ }
+
+ public static <T> IteratorTask<T> create(Iterable<? extends T> iterable, Predicate<T> processor) {
+ return create(iterable, false, processor);
+ }
+
+ public static <T> IteratorTask<T> create(Iterable<? extends T> iterable, boolean clone, Predicate<T> processor) {
+ return new IteratorTask<T>(iterable, clone) {
+ @Override
+ protected boolean process(T object) {
+ return processor.test(object);
+ }
+ };
+ }
+
+ // Two arguments: the processed object, and a runnable to remove it from the iterator.
+
+ public static <T> IteratorTask<T> create(Iterable<? extends T> iterable, BiConsumer<T, Runnable> processor) {
+ return create(iterable, false, processor);
+ }
+
+ public static <T> IteratorTask<T> create(Iterable<? extends T> iterable, boolean clone, BiConsumer<T, Runnable> processor) {
+ return create(iterable, clone, (object, runnable) -> {
+ processor.accept(object, runnable);
+ return true;
+ });
+ }
+
+ public static <T> IteratorTask<T> create(Iterable<? extends T> iterable, BiPredicate<T, Runnable> processor) {
+ return create(iterable, false, processor);
+ }
+
+ public static <T> IteratorTask<T> create(Iterable<? extends T> iterable, boolean clone, BiPredicate<T, Runnable> processor) {
+ return new IteratorTask<T>(iterable, clone) {
+ @Override
+ protected boolean process(T object) {
+ return processor.test(object, this::remove);
+ }
+ };
+ }
+
+}
diff --git a/dicore3/core/src/main/java/io/dico/dicore/task/VoidTask.java b/dicore3/core/src/main/java/io/dico/dicore/task/VoidTask.java
index 1298c4c..6204bee 100644
--- a/dicore3/core/src/main/java/io/dico/dicore/task/VoidTask.java
+++ b/dicore3/core/src/main/java/io/dico/dicore/task/VoidTask.java
@@ -1,19 +1,19 @@
-package io.dico.dicore.task;
-
-import java.util.NoSuchElementException;
-
-public abstract class VoidTask extends BaseTask<Void> {
-
- @Override
- protected final boolean process(Void object) {
- return process();
- }
-
- @Override
- protected final Void supply() throws NoSuchElementException {
- return null;
- }
-
- protected abstract boolean process();
-
-}
+package io.dico.dicore.task;
+
+import java.util.NoSuchElementException;
+
+public abstract class VoidTask extends BaseTask<Void> {
+
+ @Override
+ protected final boolean process(Void object) {
+ return process();
+ }
+
+ @Override
+ protected final Void supply() throws NoSuchElementException {
+ return null;
+ }
+
+ protected abstract boolean process();
+
+}
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index d2c45a4..4f19e53 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
-distributionBase=GRADLE_USER_HOME
-distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.8-bin.zip
-zipStoreBase=GRADLE_USER_HOME
-zipStorePath=wrapper/dists
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.8-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
index cccdd3d..518efff 100644
--- a/gradlew
+++ b/gradlew
@@ -1,172 +1,172 @@
-#!/usr/bin/env sh
-
-##############################################################################
-##
-## Gradle start up script for UN*X
-##
-##############################################################################
-
-# Attempt to set APP_HOME
-# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
- ls=`ls -ld "$PRG"`
- link=`expr "$ls" : '.*-> \(.*\)$'`
- if expr "$link" : '/.*' > /dev/null; then
- PRG="$link"
- else
- PRG=`dirname "$PRG"`"/$link"
- fi
-done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >/dev/null
-APP_HOME="`pwd -P`"
-cd "$SAVED" >/dev/null
-
-APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
-
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS=""
-
-# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
-
-warn () {
- echo "$*"
-}
-
-die () {
- echo
- echo "$*"
- echo
- exit 1
-}
-
-# OS specific support (must be 'true' or 'false').
-cygwin=false
-msys=false
-darwin=false
-nonstop=false
-case "`uname`" in
- CYGWIN* )
- cygwin=true
- ;;
- Darwin* )
- darwin=true
- ;;
- MINGW* )
- msys=true
- ;;
- NONSTOP* )
- nonstop=true
- ;;
-esac
-
-CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
-
-# Determine the Java command to use to start the JVM.
-if [ -n "$JAVA_HOME" ] ; then
- if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
- # IBM's JDK on AIX uses strange locations for the executables
- JAVACMD="$JAVA_HOME/jre/sh/java"
- else
- JAVACMD="$JAVA_HOME/bin/java"
- fi
- if [ ! -x "$JAVACMD" ] ; then
- die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
- fi
-else
- JAVACMD="java"
- which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
-fi
-
-# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
- MAX_FD_LIMIT=`ulimit -H -n`
- if [ $? -eq 0 ] ; then
- if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
- MAX_FD="$MAX_FD_LIMIT"
- fi
- ulimit -n $MAX_FD
- if [ $? -ne 0 ] ; then
- warn "Could not set maximum file descriptor limit: $MAX_FD"
- fi
- else
- warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
- fi
-fi
-
-# For Darwin, add options to specify how the application appears in the dock
-if $darwin; then
- GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
-fi
-
-# For Cygwin, switch paths to Windows format before running java
-if $cygwin ; then
- APP_HOME=`cygpath --path --mixed "$APP_HOME"`
- CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
- JAVACMD=`cygpath --unix "$JAVACMD"`
-
- # We build the pattern for arguments to be converted via cygpath
- ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
- SEP=""
- for dir in $ROOTDIRSRAW ; do
- ROOTDIRS="$ROOTDIRS$SEP$dir"
- SEP="|"
- done
- OURCYGPATTERN="(^($ROOTDIRS))"
- # Add a user-defined pattern to the cygpath arguments
- if [ "$GRADLE_CYGPATTERN" != "" ] ; then
- OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
- fi
- # Now convert the arguments - kludge to limit ourselves to /bin/sh
- i=0
- for arg in "$@" ; do
- CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
- CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
-
- if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
- eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
- else
- eval `echo args$i`="\"$arg\""
- fi
- i=$((i+1))
- done
- case $i in
- (0) set -- ;;
- (1) set -- "$args0" ;;
- (2) set -- "$args0" "$args1" ;;
- (3) set -- "$args0" "$args1" "$args2" ;;
- (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
- (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
- (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
- (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
- (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
- (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
- esac
-fi
-
-# Escape application args
-save () {
- for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
- echo " "
-}
-APP_ARGS=$(save "$@")
-
-# Collect all arguments for the java command, following the shell quoting and substitution rules
-eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
-
-# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
-if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
- cd "$(dirname "$0")"
-fi
-
-exec "$JAVACMD" "$@"
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
index f955316..e95643d 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -1,84 +1,84 @@
-@if "%DEBUG%" == "" @echo off
-@rem ##########################################################################
-@rem
-@rem Gradle startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
-set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto init
-
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto init
-
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:init
-@rem Get command-line arguments, handling Windows variants
-
-if not "%OS%" == "Windows_NT" goto win9xME_args
-
-:win9xME_args
-@rem Slurp the command line arguments.
-set CMD_LINE_ARGS=
-set _SKIP=2
-
-:win9xME_args_slurp
-if "x%~1" == "x" goto execute
-
-set CMD_LINE_ARGS=%*
-
-:execute
-@rem Setup the command line
-
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
-
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
-
-:end
-@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
-
-:fail
-rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/javaStyle.xml b/javaStyle.xml
index ec482f0..3a6a183 100644
--- a/javaStyle.xml
+++ b/javaStyle.xml
@@ -1,39 +1,39 @@
-<code_scheme name="Dico" version="173">
- <option name="RIGHT_MARGIN" value="150" />
- <option name="FORMATTER_TAGS_ENABLED" value="true" />
- <JavaCodeStyleSettings>
- <option name="REPLACE_INSTANCEOF" value="true" />
- <option name="REPLACE_CAST" value="true" />
- <option name="ANNOTATION_PARAMETER_WRAP" value="1" />
- <option name="ALIGN_MULTILINE_ANNOTATION_PARAMETERS" value="true" />
- </JavaCodeStyleSettings>
- <XML>
- <option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
- </XML>
- <codeStyleSettings language="JAVA">
- <option name="METHOD_ANNOTATION_WRAP" value="1" />
- <option name="FIELD_ANNOTATION_WRAP" value="1" />
- <option name="VARIABLE_ANNOTATION_WRAP" value="1" />
- <arrangement>
- <groups>
- <group>
- <type>GETTERS_AND_SETTERS</type>
- <order>KEEP</order>
- </group>
- <group>
- <type>OVERRIDDEN_METHODS</type>
- <order>KEEP</order>
- </group>
- <group>
- <type>DEPENDENT_METHODS</type>
- <order>DEPTH_FIRST</order>
- </group>
- </groups>
- </arrangement>
- </codeStyleSettings>
- <codeStyleSettings language="Scala">
- <indentOptions>
- <option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
- </indentOptions>
- </codeStyleSettings>
+<code_scheme name="Dico" version="173">
+ <option name="RIGHT_MARGIN" value="150" />
+ <option name="FORMATTER_TAGS_ENABLED" value="true" />
+ <JavaCodeStyleSettings>
+ <option name="REPLACE_INSTANCEOF" value="true" />
+ <option name="REPLACE_CAST" value="true" />
+ <option name="ANNOTATION_PARAMETER_WRAP" value="1" />
+ <option name="ALIGN_MULTILINE_ANNOTATION_PARAMETERS" value="true" />
+ </JavaCodeStyleSettings>
+ <XML>
+ <option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
+ </XML>
+ <codeStyleSettings language="JAVA">
+ <option name="METHOD_ANNOTATION_WRAP" value="1" />
+ <option name="FIELD_ANNOTATION_WRAP" value="1" />
+ <option name="VARIABLE_ANNOTATION_WRAP" value="1" />
+ <arrangement>
+ <groups>
+ <group>
+ <type>GETTERS_AND_SETTERS</type>
+ <order>KEEP</order>
+ </group>
+ <group>
+ <type>OVERRIDDEN_METHODS</type>
+ <order>KEEP</order>
+ </group>
+ <group>
+ <type>DEPENDENT_METHODS</type>
+ <order>DEPTH_FIRST</order>
+ </group>
+ </groups>
+ </arrangement>
+ </codeStyleSettings>
+ <codeStyleSettings language="Scala">
+ <indentOptions>
+ <option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
+ </indentOptions>
+ </codeStyleSettings>
</code_scheme> \ No newline at end of file
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 2977cab..ef05e0d 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -1,5 +1,6 @@
pluginManagement.repositories {
- maven("http://dl.bintray.com/kotlin/kotlin-eap")
+ maven("https://dl.bintray.com/kotlin/kotlin-eap/")
+ maven("https://dl.bintray.com/kotlin/kotlin-dev/")
mavenCentral()
maven("https://plugins.gradle.org/m2/")
}
diff --git a/src/main/kotlin/io/dico/parcels2/Interactable.kt b/src/main/kotlin/io/dico/parcels2/Interactable.kt
index c301340..f25b796 100644
--- a/src/main/kotlin/io/dico/parcels2/Interactable.kt
+++ b/src/main/kotlin/io/dico/parcels2/Interactable.kt
@@ -1,174 +1,174 @@
-package io.dico.parcels2
-
-import io.dico.parcels2.util.math.ceilDiv
-import io.dico.parcels2.util.ext.getMaterialsWithWoodTypePrefix
-import org.bukkit.Material
-import java.util.EnumMap
-
-class Interactables
-private constructor(
- val id: Int,
- val name: String,
- val interactableByDefault: Boolean,
- vararg val materials: Material
-) {
-
- companion object {
- val classesById: List<Interactables>
- val classesByName: Map<String, Interactables>
- val listedMaterials: Map<Material, Int>
-
- init {
- val array = getClassesArray()
- classesById = array.asList()
- classesByName = mapOf(*array.map { it.name to it }.toTypedArray())
- listedMaterials = EnumMap(mapOf(*array.flatMap { clazz -> clazz.materials.map { it to clazz.id } }.toTypedArray()))
- }
-
- operator fun get(material: Material): Interactables? {
- val id = listedMaterials[material] ?: return null
- return classesById[id]
- }
-
- operator fun get(name: String): Interactables {
- return classesByName[name] ?: throw IllegalArgumentException("Interactables class does not exist: $name")
- }
-
- operator fun get(id: Int): Interactables {
- return classesById[id]
- }
-
- private fun getClassesArray() = run {
- var id = 0
- @Suppress("UNUSED_CHANGED_VALUE")
- arrayOf(
- Interactables(
- id++, "buttons", true,
- Material.STONE_BUTTON,
- *getMaterialsWithWoodTypePrefix("BUTTON")
- ),
-
- Interactables(
- id++, "levers", true,
- Material.LEVER
- ),
-
- Interactables(
- id++, "pressure_plates", true,
- Material.STONE_PRESSURE_PLATE,
- *getMaterialsWithWoodTypePrefix("PRESSURE_PLATE"),
- Material.HEAVY_WEIGHTED_PRESSURE_PLATE,
- Material.LIGHT_WEIGHTED_PRESSURE_PLATE
- ),
-
- Interactables(
- id++, "redstone", false,
- Material.COMPARATOR,
- Material.REPEATER
- ),
-
- Interactables(
- id++, "containers", false,
- Material.CHEST,
- Material.TRAPPED_CHEST,
- Material.DISPENSER,
- Material.DROPPER,
- Material.HOPPER,
- Material.FURNACE
- ),
-
- Interactables(
- id++, "gates", true,
- *getMaterialsWithWoodTypePrefix("DOOR"),
- *getMaterialsWithWoodTypePrefix("TRAPDOOR"),
- *getMaterialsWithWoodTypePrefix("FENCE_GATE")
- )
- )
- }
-
- }
-
-}
-
-val Parcel?.effectiveInteractableConfig: InteractableConfiguration
- get() = this?.interactableConfig ?: pathInteractableConfig
-
-val pathInteractableConfig: InteractableConfiguration = run {
- val data = BitmaskInteractableConfiguration().apply {
- Interactables.classesById.forEach {
- setInteractable(it, false)
- }
- }
- object : InteractableConfiguration by data {
- override fun setInteractable(clazz: Interactables, interactable: Boolean) =
- throw IllegalStateException("pathInteractableConfig is immutable")
-
- override fun clear() =
- throw IllegalStateException("pathInteractableConfig is immutable")
-
- override fun copyFrom(other: InteractableConfiguration) =
- throw IllegalStateException("pathInteractableConfig is immutable")
- }
-}
-
-interface InteractableConfiguration {
- val interactableClasses: List<Interactables> get() = Interactables.classesById.filter { isInteractable(it) }
-
- fun isInteractable(material: Material): Boolean
- fun isInteractable(clazz: Interactables): Boolean
- fun isDefault(): Boolean
-
- fun setInteractable(clazz: Interactables, interactable: Boolean): Boolean
- fun clear(): Boolean
- fun copyFrom(other: InteractableConfiguration) =
- Interactables.classesById.fold(false) { cur, elem -> setInteractable(elem, other.isInteractable(elem) || cur) }
-
- operator fun invoke(material: Material) = isInteractable(material)
- operator fun invoke(className: String) = isInteractable(Interactables[className])
-}
-
-fun InteractableConfiguration.isInteractable(clazz: Interactables?) = clazz != null && isInteractable(clazz)
-
-class BitmaskInteractableConfiguration : InteractableConfiguration {
- val bitmaskArray = IntArray(Interactables.classesById.size ceilDiv Int.SIZE_BITS)
-
- private fun isBitSet(classId: Int): Boolean {
- val idx = classId.ushr(5)
- return idx < bitmaskArray.size && bitmaskArray[idx].and(0x1.shl(classId.and(0x1F))) != 0
- }
-
- override fun isInteractable(material: Material): Boolean {
- val classId = Interactables.listedMaterials[material] ?: return false
- return isBitSet(classId) != Interactables.classesById[classId].interactableByDefault
- }
-
- override fun isInteractable(clazz: Interactables): Boolean {
- return isBitSet(clazz.id) != clazz.interactableByDefault
- }
-
- override fun isDefault(): Boolean {
- for (x in bitmaskArray) {
- if (x != 0) return false
- }
- return true
- }
-
- override fun setInteractable(clazz: Interactables, interactable: Boolean): Boolean {
- val idx = clazz.id.ushr(5)
- if (idx >= bitmaskArray.size) return false
- val bit = 0x1.shl(clazz.id.and(0x1F))
- val oldBitmask = bitmaskArray[idx]
- bitmaskArray[idx] = if (interactable != clazz.interactableByDefault) oldBitmask.or(bit) else oldBitmask.and(bit.inv())
- return bitmaskArray[idx] != oldBitmask
- }
-
- override fun clear(): Boolean {
- var change = false
- for (i in bitmaskArray.indices) {
- if (!change && bitmaskArray[i] != 0) change = true
- bitmaskArray[i] = 0
- }
- return change
- }
-
+package io.dico.parcels2
+
+import io.dico.parcels2.util.math.ceilDiv
+import io.dico.parcels2.util.ext.getMaterialsWithWoodTypePrefix
+import org.bukkit.Material
+import java.util.EnumMap
+
+class Interactables
+private constructor(
+ val id: Int,
+ val name: String,
+ val interactableByDefault: Boolean,
+ vararg val materials: Material
+) {
+
+ companion object {
+ val classesById: List<Interactables>
+ val classesByName: Map<String, Interactables>
+ val listedMaterials: Map<Material, Int>
+
+ init {
+ val array = getClassesArray()
+ classesById = array.asList()
+ classesByName = mapOf(*array.map { it.name to it }.toTypedArray())
+ listedMaterials = EnumMap(mapOf(*array.flatMap { clazz -> clazz.materials.map { it to clazz.id } }.toTypedArray()))
+ }
+
+ operator fun get(material: Material): Interactables? {
+ val id = listedMaterials[material] ?: return null
+ return classesById[id]
+ }
+
+ operator fun get(name: String): Interactables {
+ return classesByName[name] ?: throw IllegalArgumentException("Interactables class does not exist: $name")
+ }
+
+ operator fun get(id: Int): Interactables {
+ return classesById[id]
+ }
+
+ private fun getClassesArray() = run {
+ var id = 0
+ @Suppress("UNUSED_CHANGED_VALUE")
+ arrayOf(
+ Interactables(
+ id++, "buttons", true,
+ Material.STONE_BUTTON,
+ *getMaterialsWithWoodTypePrefix("BUTTON")
+ ),
+
+ Interactables(
+ id++, "levers", true,
+ Material.LEVER
+ ),
+
+ Interactables(
+ id++, "pressure_plates", true,
+ Material.STONE_PRESSURE_PLATE,
+ *getMaterialsWithWoodTypePrefix("PRESSURE_PLATE"),
+ Material.HEAVY_WEIGHTED_PRESSURE_PLATE,
+ Material.LIGHT_WEIGHTED_PRESSURE_PLATE
+ ),
+
+ Interactables(
+ id++, "redstone", false,
+ Material.COMPARATOR,
+ Material.REPEATER
+ ),
+
+ Interactables(
+ id++, "containers", false,
+ Material.CHEST,
+ Material.TRAPPED_CHEST,
+ Material.DISPENSER,
+ Material.DROPPER,
+ Material.HOPPER,
+ Material.FURNACE
+ ),
+
+ Interactables(
+ id++, "gates", true,
+ *getMaterialsWithWoodTypePrefix("DOOR"),
+ *getMaterialsWithWoodTypePrefix("TRAPDOOR"),
+ *getMaterialsWithWoodTypePrefix("FENCE_GATE")
+ )
+ )
+ }
+
+ }
+
+}
+
+val Parcel?.effectiveInteractableConfig: InteractableConfiguration
+ get() = this?.interactableConfig ?: pathInteractableConfig
+
+val pathInteractableConfig: InteractableConfiguration = run {
+ val data = BitmaskInteractableConfiguration().apply {
+ Interactables.classesById.forEach {
+ setInteractable(it, false)
+ }
+ }
+ object : InteractableConfiguration by data {
+ override fun setInteractable(clazz: Interactables, interactable: Boolean) =
+ throw IllegalStateException("pathInteractableConfig is immutable")
+
+ override fun clear() =
+ throw IllegalStateException("pathInteractableConfig is immutable")
+
+ override fun copyFrom(other: InteractableConfiguration) =
+ throw IllegalStateException("pathInteractableConfig is immutable")
+ }
+}
+
+interface InteractableConfiguration {
+ val interactableClasses: List<Interactables> get() = Interactables.classesById.filter { isInteractable(it) }
+
+ fun isInteractable(material: Material): Boolean
+ fun isInteractable(clazz: Interactables): Boolean
+ fun isDefault(): Boolean
+
+ fun setInteractable(clazz: Interactables, interactable: Boolean): Boolean
+ fun clear(): Boolean
+ fun copyFrom(other: InteractableConfiguration) =
+ Interactables.classesById.fold(false) { cur, elem -> setInteractable(elem, other.isInteractable(elem) || cur) }
+
+ operator fun invoke(material: Material) = isInteractable(material)
+ operator fun invoke(className: String) = isInteractable(Interactables[className])
+}
+
+fun InteractableConfiguration.isInteractable(clazz: Interactables?) = clazz != null && isInteractable(clazz)
+
+class BitmaskInteractableConfiguration : InteractableConfiguration {
+ val bitmaskArray = IntArray(Interactables.classesById.size ceilDiv Int.SIZE_BITS)
+
+ private fun isBitSet(classId: Int): Boolean {
+ val idx = classId.ushr(5)
+ return idx < bitmaskArray.size && bitmaskArray[idx].and(0x1.shl(classId.and(0x1F))) != 0
+ }
+
+ override fun isInteractable(material: Material): Boolean {
+ val classId = Interactables.listedMaterials[material] ?: return false
+ return isBitSet(classId) != Interactables.classesById[classId].interactableByDefault
+ }
+
+ override fun isInteractable(clazz: Interactables): Boolean {
+ return isBitSet(clazz.id) != clazz.interactableByDefault
+ }
+
+ override fun isDefault(): Boolean {
+ for (x in bitmaskArray) {
+ if (x != 0) return false
+ }
+ return true
+ }
+
+ override fun setInteractable(clazz: Interactables, interactable: Boolean): Boolean {
+ val idx = clazz.id.ushr(5)
+ if (idx >= bitmaskArray.size) return false
+ val bit = 0x1.shl(clazz.id.and(0x1F))
+ val oldBitmask = bitmaskArray[idx]
+ bitmaskArray[idx] = if (interactable != clazz.interactableByDefault) oldBitmask.or(bit) else oldBitmask.and(bit.inv())
+ return bitmaskArray[idx] != oldBitmask
+ }
+
+ override fun clear(): Boolean {
+ var change = false
+ for (i in bitmaskArray.indices) {
+ if (!change && bitmaskArray[i] != 0) change = true
+ bitmaskArray[i] = 0
+ }
+ return change
+ }
+
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/JobDispatcher.kt b/src/main/kotlin/io/dico/parcels2/JobDispatcher.kt
index 10da0da..12be89a 100644
--- a/src/main/kotlin/io/dico/parcels2/JobDispatcher.kt
+++ b/src/main/kotlin/io/dico/parcels2/JobDispatcher.kt
@@ -1,337 +1,337 @@
-package io.dico.parcels2
-
-import io.dico.parcels2.util.math.clampMin
-import kotlinx.coroutines.CancellationException
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.CoroutineStart.LAZY
-import kotlinx.coroutines.Job as CoroutineJob
-import kotlinx.coroutines.launch
-import org.bukkit.scheduler.BukkitTask
-import java.lang.System.currentTimeMillis
-import java.util.LinkedList
-import kotlin.coroutines.Continuation
-import kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED
-import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn
-import kotlin.coroutines.resume
-
-typealias JobFunction = suspend JobScope.() -> Unit
-typealias JobUpdateLister = Job.(Double, Long) -> Unit
-
-data class TickJobtimeOptions(var jobTime: Int, var tickInterval: Int)
-
-interface JobDispatcher {
- /**
- * Submit a [function] that should be run synchronously, but limited such that it does not stall the server
- */
- fun dispatch(function: JobFunction): Job
-
- /**
- * Get a list of all jobs
- */
- val jobs: List<Job>
-
- /**
- * Attempts to complete any remaining tasks immediately, without suspension.
- */
- fun completeAllTasks()
-}
-
-interface JobAndScopeMembersUnion {
- /**
- * The time that elapsed since this job was dispatched, in milliseconds
- */
- val elapsedTime: Long
-
- /**
- * A value indicating the progress of this job, in the range 0.0 <= progress <= 1.0
- * with no guarantees to its accuracy.
- */
- val progress: Double
-}
-
-interface Job : JobAndScopeMembersUnion {
- /**
- * The coroutine associated with this job
- */
- val coroutine: CoroutineJob
-
- /**
- * true if this job has completed
- */
- val isComplete: Boolean
-
- /**
- * If an exception was thrown during the execution of this task,
- * returns that exception. Returns null otherwise.
- */
- val completionException: Throwable?
-
- /**
- * Calls the given [block] whenever the progress of this job is updated,
- * if [minInterval] milliseconds expired since the last call.
- * The first call occurs after at least [minDelay] milliseconds in a likewise manner.
- * Repeated invocations of this method result in an [IllegalStateException]
- *
- * if [asCompletionListener] is true, [onCompleted] is called with the same [block]
- */
- fun onProgressUpdate(minDelay: Int, minInterval: Int, asCompletionListener: Boolean = true, block: JobUpdateLister): Job
-
- /**
- * Calls the given [block] when this job completes, with the progress value 1.0.
- * Multiple listeners may be registered to this function.
- */
- fun onCompleted(block: JobUpdateLister): Job
-
- /**
- * Await completion of this job
- */
- suspend fun awaitCompletion()
-}
-
-interface JobScope : JobAndScopeMembersUnion {
- /**
- * A task should call this frequently during its execution, such that the timer can suspend it when necessary.
- */
- suspend fun markSuspensionPoint()
-
- /**
- * A task should call this method to indicate its progress
- */
- fun setProgress(progress: Double)
-
- /**
- * Indicate that this job is complete
- */
- fun markComplete() = setProgress(1.0)
-
- /**
- * Get a [JobScope] that is responsible for [portion] part of the progress
- * If [portion] is negative, the remaining progress is used
- */
- fun delegateProgress(portion: Double = -1.0): JobScope
-}
-
-inline fun <T> JobScope.delegateWork(portion: Double = -1.0, block: JobScope.() -> T): T {
- delegateProgress(portion).apply {
- val result = block()
- markComplete()
- return result
- }
-}
-
-interface JobInternal : Job, JobScope {
- /**
- * Start or resumes the execution of this job
- * and returns true if the job completed
- *
- * [worktime] is the maximum amount of time, in milliseconds,
- * that this job may run for until suspension.
- *
- * If [worktime] is not positive, the job will complete
- * without suspension and this method will always return true.
- */
- fun resume(worktime: Long): Boolean
-}
-
-/**
- * An object that controls one or more jobs, ensuring that they don't stall the server too much.
- * There is a configurable maxiumum amount of milliseconds that can be allocated to all jobs together in each server tick
- * This object attempts to split that maximum amount of milliseconds equally between all jobs
- */
-class BukkitJobDispatcher(private val plugin: ParcelsPlugin, var options: TickJobtimeOptions) : JobDispatcher {
- // The currently registered bukkit scheduler task
- private var bukkitTask: BukkitTask? = null
- // The jobs.
- private val _jobs = LinkedList<JobInternal>()
- override val jobs: List<Job> = _jobs
-
- override fun dispatch(function: JobFunction): Job {
- val job: JobInternal = JobImpl(plugin, function)
-
- if (bukkitTask == null) {
- val completed = job.resume(options.jobTime.toLong())
- if (completed) return job
- bukkitTask = plugin.scheduleRepeating(0, options.tickInterval) { tickCoroutineJobs() }
- }
- _jobs.addFirst(job)
- return job
- }
-
- private fun tickCoroutineJobs() {
- val jobs = _jobs
- if (jobs.isEmpty()) return
- val tickStartTime = System.currentTimeMillis()
-
- val iterator = jobs.listIterator(index = 0)
- while (iterator.hasNext()) {
- val time = System.currentTimeMillis()
- val timeElapsed = time - tickStartTime
- val timeLeft = options.jobTime - timeElapsed
- if (timeLeft <= 0) return
-
- val count = jobs.size - iterator.nextIndex()
- val timePerJob = (timeLeft + count - 1) / count
- val job = iterator.next()
- val completed = job.resume(timePerJob)
- if (completed) {
- iterator.remove()
- }
- }
-
- if (jobs.isEmpty()) {
- bukkitTask?.cancel()
- bukkitTask = null
- }
- }
-
- override fun completeAllTasks() {
- _jobs.forEach {
- it.resume(-1)
- }
- _jobs.clear()
- bukkitTask?.cancel()
- bukkitTask = null
- }
-
-}
-
-private class JobImpl(scope: CoroutineScope, task: JobFunction) : JobInternal {
- override val coroutine: CoroutineJob = scope.launch(start = LAZY) { task() }
-
- private var continuation: Continuation<Unit>? = null
- private var nextSuspensionTime: Long = 0L
- private var completeForcefully = false
- private var isStarted = false
-
- override val elapsedTime
- get() =
- if (coroutine.isCompleted) startTimeOrElapsedTime
- else currentTimeMillis() - startTimeOrElapsedTime
-
- override val isComplete get() = coroutine.isCompleted
-
- private var _progress = 0.0
- override val progress get() = _progress
- override var completionException: Throwable? = null; private set
-
- private var startTimeOrElapsedTime: Long = 0L // startTime before completed, elapsed time otherwise
- private var onProgressUpdate: JobUpdateLister? = null
- private var progressUpdateInterval: Int = 0
- private var lastUpdateTime: Long = 0L
- private var onCompleted: JobUpdateLister? = null
-
- init {
- coroutine.invokeOnCompletion { exception ->
- // report any error that occurred
- completionException = exception?.also {
- if (it !is CancellationException)
- logger.error("JobFunction generated an exception", it)
- }
-
- // convert to elapsed time here
- startTimeOrElapsedTime = System.currentTimeMillis() - startTimeOrElapsedTime
- onCompleted?.let { it(1.0, elapsedTime) }
-
- onCompleted = null
- onProgressUpdate = { prog, el -> }
- }
- }
-
- override fun onProgressUpdate(minDelay: Int, minInterval: Int, asCompletionListener: Boolean, block: JobUpdateLister): Job {
- onProgressUpdate?.let { throw IllegalStateException() }
- if (asCompletionListener) onCompleted(block)
- if (isComplete) return this
- onProgressUpdate = block
- progressUpdateInterval = minInterval
- lastUpdateTime = System.currentTimeMillis() + minDelay - minInterval
-
- return this
- }
-
- override fun onCompleted(block: JobUpdateLister): Job {
- if (isComplete) {
- block(1.0, startTimeOrElapsedTime)
- return this
- }
-
- val cur = onCompleted
- onCompleted = if (cur == null) {
- block
- } else {
- fun Job.(prog: Double, el: Long) {
- cur(prog, el)
- block(prog, el)
- }
- }
- return this
- }
-
- override suspend fun markSuspensionPoint() {
- if (System.currentTimeMillis() >= nextSuspensionTime && !completeForcefully)
- suspendCoroutineUninterceptedOrReturn { cont: Continuation<Unit> ->
- continuation = cont
- COROUTINE_SUSPENDED
- }
- }
-
- override fun setProgress(progress: Double) {
- this._progress = progress
- val onProgressUpdate = onProgressUpdate ?: return
- val time = System.currentTimeMillis()
- if (time > lastUpdateTime + progressUpdateInterval) {
- onProgressUpdate(progress, elapsedTime)
- lastUpdateTime = time
- }
- }
-
- override fun resume(worktime: Long): Boolean {
- if (isComplete) return true
-
- if (worktime > 0) {
- nextSuspensionTime = currentTimeMillis() + worktime
- } else {
- completeForcefully = true
- }
-
- if (isStarted) {
- continuation?.let {
- continuation = null
- it.resume(Unit)
- return continuation == null
- }
- return true
- }
-
- isStarted = true
- startTimeOrElapsedTime = System.currentTimeMillis()
- coroutine.start()
-
- return continuation == null
- }
-
- override suspend fun awaitCompletion() {
- coroutine.join()
- }
-
- private fun delegateProgress(curPortion: Double, portion: Double): JobScope =
- DelegateScope(progress, curPortion * (if (portion < 0) 1.0 - progress else portion).clampMin(0.0))
-
- override fun delegateProgress(portion: Double): JobScope = delegateProgress(1.0, portion)
-
- private inner class DelegateScope(val progressStart: Double, val portion: Double) : JobScope {
- override val elapsedTime: Long
- get() = this@JobImpl.elapsedTime
-
- override suspend fun markSuspensionPoint() =
- this@JobImpl.markSuspensionPoint()
-
- override val progress: Double
- get() = (this@JobImpl.progress - progressStart) / portion
-
- override fun setProgress(progress: Double) =
- this@JobImpl.setProgress(progressStart + progress * portion)
-
- override fun delegateProgress(portion: Double): JobScope =
- this@JobImpl.delegateProgress(this.portion, portion)
- }
-}
+package io.dico.parcels2
+
+import io.dico.parcels2.util.math.clampMin
+import kotlinx.coroutines.CancellationException
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.CoroutineStart.LAZY
+import kotlinx.coroutines.Job as CoroutineJob
+import kotlinx.coroutines.launch
+import org.bukkit.scheduler.BukkitTask
+import java.lang.System.currentTimeMillis
+import java.util.LinkedList
+import kotlin.coroutines.Continuation
+import kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED
+import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn
+import kotlin.coroutines.resume
+
+typealias JobFunction = suspend JobScope.() -> Unit
+typealias JobUpdateLister = Job.(Double, Long) -> Unit
+
+data class TickJobtimeOptions(var jobTime: Int, var tickInterval: Int)
+
+interface JobDispatcher {
+ /**
+ * Submit a [function] that should be run synchronously, but limited such that it does not stall the server
+ */
+ fun dispatch(function: JobFunction): Job
+
+ /**
+ * Get a list of all jobs
+ */
+ val jobs: List<Job>
+
+ /**
+ * Attempts to complete any remaining tasks immediately, without suspension.
+ */
+ fun completeAllTasks()
+}
+
+interface JobAndScopeMembersUnion {
+ /**
+ * The time that elapsed since this job was dispatched, in milliseconds
+ */
+ val elapsedTime: Long
+
+ /**
+ * A value indicating the progress of this job, in the range 0.0 <= progress <= 1.0
+ * with no guarantees to its accuracy.
+ */
+ val progress: Double
+}
+
+interface Job : JobAndScopeMembersUnion {
+ /**
+ * The coroutine associated with this job
+ */
+ val coroutine: CoroutineJob
+
+ /**
+ * true if this job has completed
+ */
+ val isComplete: Boolean
+
+ /**
+ * If an exception was thrown during the execution of this task,
+ * returns that exception. Returns null otherwise.
+ */
+ val completionException: Throwable?
+
+ /**
+ * Calls the given [block] whenever the progress of this job is updated,
+ * if [minInterval] milliseconds expired since the last call.
+ * The first call occurs after at least [minDelay] milliseconds in a likewise manner.
+ * Repeated invocations of this method result in an [IllegalStateException]
+ *
+ * if [asCompletionListener] is true, [onCompleted] is called with the same [block]
+ */
+ fun onProgressUpdate(minDelay: Int, minInterval: Int, asCompletionListener: Boolean = true, block: JobUpdateLister): Job
+
+ /**
+ * Calls the given [block] when this job completes, with the progress value 1.0.
+ * Multiple listeners may be registered to this function.
+ */
+ fun onCompleted(block: JobUpdateLister): Job
+
+ /**
+ * Await completion of this job
+ */
+ suspend fun awaitCompletion()
+}
+
+interface JobScope : JobAndScopeMembersUnion {
+ /**
+ * A task should call this frequently during its execution, such that the timer can suspend it when necessary.
+ */
+ suspend fun markSuspensionPoint()
+
+ /**
+ * A task should call this method to indicate its progress
+ */
+ fun setProgress(progress: Double)
+
+ /**
+ * Indicate that this job is complete
+ */
+ fun markComplete() = setProgress(1.0)
+
+ /**
+ * Get a [JobScope] that is responsible for [portion] part of the progress
+ * If [portion] is negative, the remaining progress is used
+ */
+ fun delegateProgress(portion: Double = -1.0): JobScope
+}
+
+inline fun <T> JobScope.delegateWork(portion: Double = -1.0, block: JobScope.() -> T): T {
+ delegateProgress(portion).apply {
+ val result = block()
+ markComplete()
+ return result
+ }
+}
+
+interface JobInternal : Job, JobScope {
+ /**
+ * Start or resumes the execution of this job
+ * and returns true if the job completed
+ *
+ * [worktime] is the maximum amount of time, in milliseconds,
+ * that this job may run for until suspension.
+ *
+ * If [worktime] is not positive, the job will complete
+ * without suspension and this method will always return true.
+ */
+ fun resume(worktime: Long): Boolean
+}
+
+/**
+ * An object that controls one or more jobs, ensuring that they don't stall the server too much.
+ * There is a configurable maxiumum amount of milliseconds that can be allocated to all jobs together in each server tick
+ * This object attempts to split that maximum amount of milliseconds equally between all jobs
+ */
+class BukkitJobDispatcher(private val plugin: ParcelsPlugin, var options: TickJobtimeOptions) : JobDispatcher {
+ // The currently registered bukkit scheduler task
+ private var bukkitTask: BukkitTask? = null
+ // The jobs.
+ private val _jobs = LinkedList<JobInternal>()
+ override val jobs: List<Job> = _jobs
+
+ override fun dispatch(function: JobFunction): Job {
+ val job: JobInternal = JobImpl(plugin, function)
+
+ if (bukkitTask == null) {
+ val completed = job.resume(options.jobTime.toLong())
+ if (completed) return job
+ bukkitTask = plugin.scheduleRepeating(0, options.tickInterval) { tickCoroutineJobs() }
+ }
+ _jobs.addFirst(job)
+ return job
+ }
+
+ private fun tickCoroutineJobs() {
+ val jobs = _jobs
+ if (jobs.isEmpty()) return
+ val tickStartTime = System.currentTimeMillis()
+
+ val iterator = jobs.listIterator(index = 0)
+ while (iterator.hasNext()) {
+ val time = System.currentTimeMillis()
+ val timeElapsed = time - tickStartTime
+ val timeLeft = options.jobTime - timeElapsed
+ if (timeLeft <= 0) return
+
+ val count = jobs.size - iterator.nextIndex()
+ val timePerJob = (timeLeft + count - 1) / count
+ val job = iterator.next()
+ val completed = job.resume(timePerJob)
+ if (completed) {
+ iterator.remove()
+ }
+ }
+
+ if (jobs.isEmpty()) {
+ bukkitTask?.cancel()
+ bukkitTask = null
+ }
+ }
+
+ override fun completeAllTasks() {
+ _jobs.forEach {
+ it.resume(-1)
+ }
+ _jobs.clear()
+ bukkitTask?.cancel()
+ bukkitTask = null
+ }
+
+}
+
+private class JobImpl(scope: CoroutineScope, task: JobFunction) : JobInternal {
+ override val coroutine: CoroutineJob = scope.launch(start = LAZY) { task() }
+
+ private var continuation: Continuation<Unit>? = null
+ private var nextSuspensionTime: Long = 0L
+ private var completeForcefully = false
+ private var isStarted = false
+
+ override val elapsedTime
+ get() =
+ if (coroutine.isCompleted) startTimeOrElapsedTime
+ else currentTimeMillis() - startTimeOrElapsedTime
+
+ override val isComplete get() = coroutine.isCompleted
+
+ private var _progress = 0.0
+ override val progress get() = _progress
+ override var completionException: Throwable? = null; private set
+
+ private var startTimeOrElapsedTime: Long = 0L // startTime before completed, elapsed time otherwise
+ private var onProgressUpdate: JobUpdateLister? = null
+ private var progressUpdateInterval: Int = 0
+ private var lastUpdateTime: Long = 0L
+ private var onCompleted: JobUpdateLister? = null
+
+ init {
+ coroutine.invokeOnCompletion { exception ->
+ // report any error that occurred
+ completionException = exception?.also {
+ if (it !is CancellationException)
+ logger.error("JobFunction generated an exception", it)
+ }
+
+ // convert to elapsed time here
+ startTimeOrElapsedTime = System.currentTimeMillis() - startTimeOrElapsedTime
+ onCompleted?.let { it(1.0, elapsedTime) }
+
+ onCompleted = null
+ onProgressUpdate = { prog, el -> }
+ }
+ }
+
+ override fun onProgressUpdate(minDelay: Int, minInterval: Int, asCompletionListener: Boolean, block: JobUpdateLister): Job {
+ onProgressUpdate?.let { throw IllegalStateException() }
+ if (asCompletionListener) onCompleted(block)
+ if (isComplete) return this
+ onProgressUpdate = block
+ progressUpdateInterval = minInterval
+ lastUpdateTime = System.currentTimeMillis() + minDelay - minInterval
+
+ return this
+ }
+
+ override fun onCompleted(block: JobUpdateLister): Job {
+ if (isComplete) {
+ block(1.0, startTimeOrElapsedTime)
+ return this
+ }
+
+ val cur = onCompleted
+ onCompleted = if (cur == null) {
+ block
+ } else {
+ fun Job.(prog: Double, el: Long) {
+ cur(prog, el)
+ block(prog, el)
+ }
+ }
+ return this
+ }
+
+ override suspend fun markSuspensionPoint() {
+ if (System.currentTimeMillis() >= nextSuspensionTime && !completeForcefully)
+ suspendCoroutineUninterceptedOrReturn { cont: Continuation<Unit> ->
+ continuation = cont
+ COROUTINE_SUSPENDED
+ }
+ }
+
+ override fun setProgress(progress: Double) {
+ this._progress = progress
+ val onProgressUpdate = onProgressUpdate ?: return
+ val time = System.currentTimeMillis()
+ if (time > lastUpdateTime + progressUpdateInterval) {
+ onProgressUpdate(progress, elapsedTime)
+ lastUpdateTime = time
+ }
+ }
+
+ override fun resume(worktime: Long): Boolean {
+ if (isComplete) return true
+
+ if (worktime > 0) {
+ nextSuspensionTime = currentTimeMillis() + worktime
+ } else {
+ completeForcefully = true
+ }
+
+ if (isStarted) {
+ continuation?.let {
+ continuation = null
+ it.resume(Unit)
+ return continuation == null
+ }
+ return true
+ }
+
+ isStarted = true
+ startTimeOrElapsedTime = System.currentTimeMillis()
+ coroutine.start()
+
+ return continuation == null
+ }
+
+ override suspend fun awaitCompletion() {
+ coroutine.join()
+ }
+
+ private fun delegateProgress(curPortion: Double, portion: Double): JobScope =
+ DelegateScope(progress, curPortion * (if (portion < 0) 1.0 - progress else portion).clampMin(0.0))
+
+ override fun delegateProgress(portion: Double): JobScope = delegateProgress(1.0, portion)
+
+ private inner class DelegateScope(val progressStart: Double, val portion: Double) : JobScope {
+ override val elapsedTime: Long
+ get() = this@JobImpl.elapsedTime
+
+ override suspend fun markSuspensionPoint() =
+ this@JobImpl.markSuspensionPoint()
+
+ override val progress: Double
+ get() = (this@JobImpl.progress - progressStart) / portion
+
+ override fun setProgress(progress: Double) =
+ this@JobImpl.setProgress(progressStart + progress * portion)
+
+ override fun delegateProgress(portion: Double): JobScope =
+ this@JobImpl.delegateProgress(this.portion, portion)
+ }
+}
diff --git a/src/main/kotlin/io/dico/parcels2/Parcel.kt b/src/main/kotlin/io/dico/parcels2/Parcel.kt
index 4f89fe0..3527e15 100644
--- a/src/main/kotlin/io/dico/parcels2/Parcel.kt
+++ b/src/main/kotlin/io/dico/parcels2/Parcel.kt
@@ -1,66 +1,66 @@
-package io.dico.parcels2
-
-import io.dico.parcels2.util.math.Vec2i
-import io.dico.parcels2.util.math.Vec3i
-import org.bukkit.Location
-import org.joda.time.DateTime
-import java.util.UUID
-
-/**
- * Parcel implementation of ParcelData will update the database when changes are made.
- * To change the data without updating the database, defer to the data delegate instance.
- *
- * This should be used for example in database query callbacks.
- * However, this implementation is intentionally not thread-safe.
- * Therefore, database query callbacks should schedule their updates using the bukkit scheduler.
- */
-interface Parcel : ParcelData, Privileges {
- val id: ParcelId
- val world: ParcelWorld
- val pos: Vec2i
- val x: Int
- val z: Int
- val data: ParcelDataHolder
- val infoString: String
- val hasBlockVisitors: Boolean
- val globalPrivileges: GlobalPrivileges?
-
- override val keyOfOwner: PlayerProfile.Real?
- get() = owner as? PlayerProfile.Real
-
- fun copyData(newData: ParcelDataHolder, callerIsDatabase: Boolean = false)
-
- fun dispose() = copyData(ParcelDataHolder())
-
- fun updateOwnerSign(force: Boolean = false)
-
- val homeLocation: Location get() = world.blockManager.getHomeLocation(id)
-}
-
-
-
-interface ParcelData : RawPrivileges {
- var owner: PlayerProfile?
- val lastClaimTime: DateTime?
- var isOwnerSignOutdated: Boolean
- var interactableConfig: InteractableConfiguration
-
- //fun canBuild(player: OfflinePlayer, checkAdmin: Boolean = true, checkGlobal: Boolean = true): Boolean
-
- fun isOwner(uuid: UUID): Boolean {
- return owner?.uuid == uuid
- }
-
- fun isOwner(profile: PlayerProfile?): Boolean {
- return owner == profile
- }
-}
-
-class ParcelDataHolder(addedMap: MutablePrivilegeMap = mutableMapOf())
- : ParcelData, PrivilegesHolder(addedMap) {
- override var owner: PlayerProfile? = null
- override var lastClaimTime: DateTime? = null
- override var isOwnerSignOutdated = false
- override var interactableConfig: InteractableConfiguration = BitmaskInteractableConfiguration()
-}
-
+package io.dico.parcels2
+
+import io.dico.parcels2.util.math.Vec2i
+import io.dico.parcels2.util.math.Vec3i
+import org.bukkit.Location
+import org.joda.time.DateTime
+import java.util.UUID
+
+/**
+ * Parcel implementation of ParcelData will update the database when changes are made.
+ * To change the data without updating the database, defer to the data delegate instance.
+ *
+ * This should be used for example in database query callbacks.
+ * However, this implementation is intentionally not thread-safe.
+ * Therefore, database query callbacks should schedule their updates using the bukkit scheduler.
+ */
+interface Parcel : ParcelData, Privileges {
+ val id: ParcelId
+ val world: ParcelWorld
+ val pos: Vec2i
+ val x: Int
+ val z: Int
+ val data: ParcelDataHolder
+ val infoString: String
+ val hasBlockVisitors: Boolean
+ val globalPrivileges: GlobalPrivileges?
+
+ override val keyOfOwner: PlayerProfile.Real?
+ get() = owner as? PlayerProfile.Real
+
+ fun copyData(newData: ParcelDataHolder, callerIsDatabase: Boolean = false)
+
+ fun dispose() = copyData(ParcelDataHolder())
+
+ fun updateOwnerSign(force: Boolean = false)
+
+ val homeLocation: Location get() = world.blockManager.getHomeLocation(id)
+}
+
+
+
+interface ParcelData : RawPrivileges {
+ var owner: PlayerProfile?
+ val lastClaimTime: DateTime?
+ var isOwnerSignOutdated: Boolean
+ var interactableConfig: InteractableConfiguration
+
+ //fun canBuild(player: OfflinePlayer, checkAdmin: Boolean = true, checkGlobal: Boolean = true): Boolean
+
+ fun isOwner(uuid: UUID): Boolean {
+ return owner?.uuid == uuid
+ }
+
+ fun isOwner(profile: PlayerProfile?): Boolean {
+ return owner == profile
+ }
+}
+
+class ParcelDataHolder(addedMap: MutablePrivilegeMap = mutableMapOf())
+ : ParcelData, PrivilegesHolder(addedMap) {
+ override var owner: PlayerProfile? = null
+ override var lastClaimTime: DateTime? = null
+ override var isOwnerSignOutdated = false
+ override var interactableConfig: InteractableConfiguration = BitmaskInteractableConfiguration()
+}
+
diff --git a/src/main/kotlin/io/dico/parcels2/ParcelGenerator.kt b/src/main/kotlin/io/dico/parcels2/ParcelGenerator.kt
index 109c5dc..63ec02c 100644
--- a/src/main/kotlin/io/dico/parcels2/ParcelGenerator.kt
+++ b/src/main/kotlin/io/dico/parcels2/ParcelGenerator.kt
@@ -1,103 +1,103 @@
-package io.dico.parcels2
-
-import io.dico.parcels2.blockvisitor.RegionTraverser
-import io.dico.parcels2.util.math.Region
-import io.dico.parcels2.util.math.Vec2i
-import io.dico.parcels2.util.math.Vec3i
-import io.dico.parcels2.util.math.get
-import kotlinx.coroutines.CoroutineScope
-import org.bukkit.Chunk
-import org.bukkit.Location
-import org.bukkit.Material
-import org.bukkit.World
-import org.bukkit.block.Biome
-import org.bukkit.block.Block
-import org.bukkit.block.BlockFace
-import org.bukkit.entity.Entity
-import org.bukkit.generator.BlockPopulator
-import org.bukkit.generator.ChunkGenerator
-import java.util.Random
-
-abstract class ParcelGenerator : ChunkGenerator() {
- abstract val worldName: String
-
- abstract val world: World
-
- abstract override fun generateChunkData(world: World?, random: Random?, chunkX: Int, chunkZ: Int, biome: BiomeGrid?): ChunkData
-
- abstract fun populate(world: World?, random: Random?, chunk: Chunk?)
-
- abstract override fun getFixedSpawnLocation(world: World?, random: Random?): Location
-
- override fun getDefaultPopulators(world: World?): MutableList<BlockPopulator> {
- return mutableListOf(object : BlockPopulator() {
- override fun populate(world: World?, random: Random?, chunk: Chunk?) {
- this@ParcelGenerator.populate(world, random, chunk)
- }
- })
- }
-
- abstract fun makeParcelLocatorAndBlockManager(
- parcelProvider: ParcelProvider,
- container: ParcelContainer,
- coroutineScope: CoroutineScope,
- jobDispatcher: JobDispatcher
- ): Pair<ParcelLocator, ParcelBlockManager>
-}
-
-interface ParcelBlockManager {
- val world: World
- val jobDispatcher: JobDispatcher
- val parcelTraverser: RegionTraverser
-
- fun getRegionOrigin(parcel: ParcelId) = getRegion(parcel).origin.toVec2i()
-
- fun getHomeLocation(parcel: ParcelId): Location
-
- fun getRegion(parcel: ParcelId): Region
-
- fun getEntities(parcel: ParcelId): Collection<Entity>
-
- fun isParcelInfoSectionLoaded(parcel: ParcelId): Boolean
-
- fun updateParcelInfo(parcel: ParcelId, owner: PlayerProfile?)
-
- fun getParcelForInfoBlockInteraction(block: Vec3i, type: Material, face: BlockFace): Parcel?
-
- fun setBiome(parcel: ParcelId, biome: Biome): Job?
-
- fun clearParcel(parcel: ParcelId): Job?
-
- /**
- * Used to update owner blocks in the corner of the parcel
- */
- fun getParcelsWithOwnerBlockIn(chunk: Chunk): Collection<Vec2i>
-}
-
-inline fun ParcelBlockManager.tryDoBlockOperation(
- parcelProvider: ParcelProvider,
- parcel: ParcelId,
- traverser: RegionTraverser,
- crossinline operation: suspend JobScope.(Block) -> Unit
-) = parcelProvider.trySubmitBlockVisitor(Permit(), arrayOf(parcel)) {
- val region = getRegion(parcel)
- val blockCount = region.blockCount.toDouble()
- val blocks = traverser.traverseRegion(region)
- for ((index, vec) in blocks.withIndex()) {
- markSuspensionPoint()
- operation(world[vec])
- setProgress((index + 1) / blockCount)
- }
-}
-
-abstract class ParcelBlockManagerBase : ParcelBlockManager {
-
- override fun getEntities(parcel: ParcelId): Collection<Entity> {
- val region = getRegion(parcel)
- val center = region.center
- val centerLoc = Location(world, center.x, center.y, center.z)
- val centerDist = (center - region.origin).add(0.2, 0.2, 0.2)
- return world.getNearbyEntities(centerLoc, centerDist.x, centerDist.y, centerDist.z)
- }
-
-}
+package io.dico.parcels2
+
+import io.dico.parcels2.blockvisitor.RegionTraverser
+import io.dico.parcels2.util.math.Region
+import io.dico.parcels2.util.math.Vec2i
+import io.dico.parcels2.util.math.Vec3i
+import io.dico.parcels2.util.math.get
+import kotlinx.coroutines.CoroutineScope
+import org.bukkit.Chunk
+import org.bukkit.Location
+import org.bukkit.Material
+import org.bukkit.World
+import org.bukkit.block.Biome
+import org.bukkit.block.Block
+import org.bukkit.block.BlockFace
+import org.bukkit.entity.Entity
+import org.bukkit.generator.BlockPopulator
+import org.bukkit.generator.ChunkGenerator
+import java.util.Random
+
+abstract class ParcelGenerator : ChunkGenerator() {
+ abstract val worldName: String
+
+ abstract val world: World
+
+ abstract override fun generateChunkData(world: World?, random: Random?, chunkX: Int, chunkZ: Int, biome: BiomeGrid?): ChunkData
+
+ abstract fun populate(world: World?, random: Random?, chunk: Chunk?)
+
+ abstract override fun getFixedSpawnLocation(world: World?, random: Random?): Location
+
+ override fun getDefaultPopulators(world: World?): MutableList<BlockPopulator> {
+ return mutableListOf(object : BlockPopulator() {
+ override fun populate(world: World?, random: Random?, chunk: Chunk?) {
+ this@ParcelGenerator.populate(world, random, chunk)
+ }
+ })
+ }
+
+ abstract fun makeParcelLocatorAndBlockManager(
+ parcelProvider: ParcelProvider,
+ container: ParcelContainer,
+ coroutineScope: CoroutineScope,
+ jobDispatcher: JobDispatcher
+ ): Pair<ParcelLocator, ParcelBlockManager>
+}
+
+interface ParcelBlockManager {
+ val world: World
+ val jobDispatcher: JobDispatcher
+ val parcelTraverser: RegionTraverser
+
+ fun getRegionOrigin(parcel: ParcelId) = getRegion(parcel).origin.toVec2i()
+
+ fun getHomeLocation(parcel: ParcelId): Location
+
+ fun getRegion(parcel: ParcelId): Region
+
+ fun getEntities(parcel: ParcelId): Collection<Entity>
+
+ fun isParcelInfoSectionLoaded(parcel: ParcelId): Boolean
+
+ fun updateParcelInfo(parcel: ParcelId, owner: PlayerProfile?)
+
+ fun getParcelForInfoBlockInteraction(block: Vec3i, type: Material, face: BlockFace): Parcel?
+
+ fun setBiome(parcel: ParcelId, biome: Biome): Job?
+
+ fun clearParcel(parcel: ParcelId): Job?
+
+ /**
+ * Used to update owner blocks in the corner of the parcel
+ */
+ fun getParcelsWithOwnerBlockIn(chunk: Chunk): Collection<Vec2i>
+}
+
+inline fun ParcelBlockManager.tryDoBlockOperation(
+ parcelProvider: ParcelProvider,
+ parcel: ParcelId,
+ traverser: RegionTraverser,
+ crossinline operation: suspend JobScope.(Block) -> Unit
+) = parcelProvider.trySubmitBlockVisitor(Permit(), arrayOf(parcel)) {
+ val region = getRegion(parcel)
+ val blockCount = region.blockCount.toDouble()
+ val blocks = traverser.traverseRegion(region)
+ for ((index, vec) in blocks.withIndex()) {
+ markSuspensionPoint()
+ operation(world[vec])
+ setProgress((index + 1) / blockCount)
+ }
+}
+
+abstract class ParcelBlockManagerBase : ParcelBlockManager {
+
+ override fun getEntities(parcel: ParcelId): Collection<Entity> {
+ val region = getRegion(parcel)
+ val center = region.center
+ val centerLoc = Location(world, center.x, center.y, center.z)
+ val centerDist = (center - region.origin).add(0.2, 0.2, 0.2)
+ return world.getNearbyEntities(centerLoc, centerDist.x, centerDist.y, centerDist.z)
+ }
+
+}
diff --git a/src/main/kotlin/io/dico/parcels2/ParcelId.kt b/src/main/kotlin/io/dico/parcels2/ParcelId.kt
index eef7129..77a1835 100644
--- a/src/main/kotlin/io/dico/parcels2/ParcelId.kt
+++ b/src/main/kotlin/io/dico/parcels2/ParcelId.kt
@@ -1,56 +1,56 @@
-@file:Suppress("FunctionName")
-
-package io.dico.parcels2
-
-import io.dico.parcels2.util.math.Vec2i
-import org.bukkit.Bukkit
-import org.bukkit.World
-import java.util.UUID
-
-/**
- * Used by storage backing options to encompass the identity of a world
- * Does NOT support equality operator.
- */
-interface ParcelWorldId {
- val name: String
- val uid: UUID?
- fun equals(id: ParcelWorldId): Boolean = name == id.name || (uid != null && uid == id.uid)
-
- val bukkitWorld: World? get() = Bukkit.getWorld(name) ?: uid?.let { Bukkit.getWorld(it) }
-}
-
-fun ParcelWorldId.parcelWorldIdToString() = "ParcelWorld($name)"
-
-fun ParcelWorldId(worldName: String, worldUid: UUID? = null): ParcelWorldId = ParcelWorldIdImpl(worldName, worldUid)
-fun ParcelWorldId(world: World) = ParcelWorldId(world.name, world.uid)
-
-/**
- * Used by storage backing options to encompass the location of a parcel
- * Does NOT support equality operator.
- */
-interface ParcelId {
- val worldId: ParcelWorldId
- val x: Int
- val z: Int
- val pos: Vec2i get() = Vec2i(x, z)
- val idString get() = "$x,$z"
- fun equals(id: ParcelId): Boolean = x == id.x && z == id.z && worldId.equals(id.worldId)
-}
-
-fun ParcelId.parcelIdToString() = "Parcel(${worldId.name},$idString)"
-
-fun ParcelId(worldId: ParcelWorldId, pos: Vec2i) = ParcelId(worldId, pos.x, pos.z)
-fun ParcelId(worldName: String, worldUid: UUID?, pos: Vec2i) = ParcelId(worldName, worldUid, pos.x, pos.z)
-fun ParcelId(worldName: String, worldUid: UUID?, x: Int, z: Int) = ParcelId(ParcelWorldId(worldName, worldUid), x, z)
-fun ParcelId(worldId: ParcelWorldId, x: Int, z: Int): ParcelId = ParcelIdImpl(worldId, x, z)
-
-private class ParcelWorldIdImpl(override val name: String,
- override val uid: UUID?) : ParcelWorldId {
- override fun toString() = parcelWorldIdToString()
-}
-
-private class ParcelIdImpl(override val worldId: ParcelWorldId,
- override val x: Int,
- override val z: Int) : ParcelId {
- override fun toString() = parcelIdToString()
-}
+@file:Suppress("FunctionName")
+
+package io.dico.parcels2
+
+import io.dico.parcels2.util.math.Vec2i
+import org.bukkit.Bukkit
+import org.bukkit.World
+import java.util.UUID
+
+/**
+ * Used by storage backing options to encompass the identity of a world
+ * Does NOT support equality operator.
+ */
+interface ParcelWorldId {
+ val name: String
+ val uid: UUID?
+ fun equals(id: ParcelWorldId): Boolean = name == id.name || (uid != null && uid == id.uid)
+
+ val bukkitWorld: World? get() = Bukkit.getWorld(name) ?: uid?.let { Bukkit.getWorld(it) }
+}
+
+fun ParcelWorldId.parcelWorldIdToString() = "ParcelWorld($name)"
+
+fun ParcelWorldId(worldName: String, worldUid: UUID? = null): ParcelWorldId = ParcelWorldIdImpl(worldName, worldUid)
+fun ParcelWorldId(world: World) = ParcelWorldId(world.name, world.uid)
+
+/**
+ * Used by storage backing options to encompass the location of a parcel
+ * Does NOT support equality operator.
+ */
+interface ParcelId {
+ val worldId: ParcelWorldId
+ val x: Int
+ val z: Int
+ val pos: Vec2i get() = Vec2i(x, z)
+ val idString get() = "$x,$z"
+ fun equals(id: ParcelId): Boolean = x == id.x && z == id.z && worldId.equals(id.worldId)
+}
+
+fun ParcelId.parcelIdToString() = "Parcel(${worldId.name},$idString)"
+
+fun ParcelId(worldId: ParcelWorldId, pos: Vec2i) = ParcelId(worldId, pos.x, pos.z)
+fun ParcelId(worldName: String, worldUid: UUID?, pos: Vec2i) = ParcelId(worldName, worldUid, pos.x, pos.z)
+fun ParcelId(worldName: String, worldUid: UUID?, x: Int, z: Int) = ParcelId(ParcelWorldId(worldName, worldUid), x, z)
+fun ParcelId(worldId: ParcelWorldId, x: Int, z: Int): ParcelId = ParcelIdImpl(worldId, x, z)
+
+private class ParcelWorldIdImpl(override val name: String,
+ override val uid: UUID?) : ParcelWorldId {
+ override fun toString() = parcelWorldIdToString()
+}
+
+private class ParcelIdImpl(override val worldId: ParcelWorldId,
+ override val x: Int,
+ override val z: Int) : ParcelId {
+ override fun toString() = parcelIdToString()
+}
diff --git a/src/main/kotlin/io/dico/parcels2/ParcelWorld.kt b/src/main/kotlin/io/dico/parcels2/ParcelWorld.kt
index c31b11a..36dfe1c 100644
--- a/src/main/kotlin/io/dico/parcels2/ParcelWorld.kt
+++ b/src/main/kotlin/io/dico/parcels2/ParcelWorld.kt
@@ -1,103 +1,103 @@
-package io.dico.parcels2
-
-import io.dico.parcels2.options.RuntimeWorldOptions
-import io.dico.parcels2.storage.Storage
-import io.dico.parcels2.util.math.Vec2i
-import io.dico.parcels2.util.math.floor
-import org.bukkit.Location
-import org.bukkit.World
-import org.bukkit.block.Block
-import org.bukkit.entity.Entity
-import org.joda.time.DateTime
-import java.lang.IllegalStateException
-import java.util.UUID
-
-class Permit
-
-interface ParcelProvider {
- val worlds: Map<String, ParcelWorld>
-
- fun getWorldById(id: ParcelWorldId): ParcelWorld?
-
- fun getParcelById(id: ParcelId): Parcel?
-
- fun getWorld(name: String): ParcelWorld?
-
- fun getWorld(world: World): ParcelWorld? = getWorld(world.name)
-
- fun getWorld(block: Block): ParcelWorld? = getWorld(block.world)
-
- fun getWorld(loc: Location): ParcelWorld? = getWorld(loc.world)
-
- fun getWorld(entity: Entity): ParcelWorld? = getWorld(entity.location)
-
- fun getParcelAt(worldName: String, x: Int, z: Int): Parcel? = getWorld(worldName)?.locator?.getParcelAt(x, z)
-
- fun getParcelAt(world: World, x: Int, z: Int): Parcel? = getParcelAt(world.name, x, z)
-
- fun getParcelAt(world: World, vec: Vec2i): Parcel? = getParcelAt(world, vec.x, vec.z)
-
- fun getParcelAt(loc: Location): Parcel? = getParcelAt(loc.world, loc.x.floor(), loc.z.floor())
-
- fun getParcelAt(entity: Entity): Parcel? = getParcelAt(entity.location)
-
- fun getParcelAt(block: Block): Parcel? = getParcelAt(block.world, block.x, block.z)
-
- fun getWorldGenerator(worldName: String): ParcelGenerator?
-
- fun loadWorlds()
-
- fun acquireBlockVisitorPermit(parcelId: ParcelId, with: Permit): Boolean
-
- @Throws(IllegalStateException::class)
- fun releaseBlockVisitorPermit(parcelId: ParcelId, with: Permit)
-
- fun trySubmitBlockVisitor(permit: Permit, parcelIds: Array<out ParcelId>, function: JobFunction): Job?
-
- fun swapParcels(parcelId1: ParcelId, parcelId2: ParcelId): Job?
-}
-
-interface ParcelLocator {
- val world: World
-
- fun getParcelIdAt(x: Int, z: Int): ParcelId?
-
- fun getParcelAt(x: Int, z: Int): Parcel?
-
- fun getParcelAt(vec: Vec2i): Parcel? = getParcelAt(vec.x, vec.z)
-
- fun getParcelAt(loc: Location): Parcel? = getParcelAt(loc.x.floor(), loc.z.floor()).takeIf { loc.world == world }
-
- fun getParcelAt(entity: Entity): Parcel? = getParcelAt(entity.location).takeIf { entity.world == world }
-
- fun getParcelAt(block: Block): Parcel? = getParcelAt(block.x, block.z).takeIf { block.world == world }
-}
-
-typealias ParcelContainerFactory = (ParcelWorld) -> ParcelContainer
-
-interface ParcelContainer {
-
- fun getParcelById(x: Int, z: Int): Parcel?
-
- fun getParcelById(id: Vec2i): Parcel? = getParcelById(id.x, id.z)
-
- fun getParcelById(id: ParcelId): Parcel?
-
- fun nextEmptyParcel(): Parcel?
-
-}
-
-interface ParcelWorld : ParcelLocator, ParcelContainer {
- val id: ParcelWorldId
- val name: String
- val uid: UUID?
- val options: RuntimeWorldOptions
- val generator: ParcelGenerator
- val storage: Storage
- val container: ParcelContainer
- val locator: ParcelLocator
- val blockManager: ParcelBlockManager
- val globalPrivileges: GlobalPrivilegesManager
-
- val creationTime: DateTime?
-}
+package io.dico.parcels2
+
+import io.dico.parcels2.options.RuntimeWorldOptions
+import io.dico.parcels2.storage.Storage
+import io.dico.parcels2.util.math.Vec2i
+import io.dico.parcels2.util.math.floor
+import org.bukkit.Location
+import org.bukkit.World
+import org.bukkit.block.Block
+import org.bukkit.entity.Entity
+import org.joda.time.DateTime
+import java.lang.IllegalStateException
+import java.util.UUID
+
+class Permit
+
+interface ParcelProvider {
+ val worlds: Map<String, ParcelWorld>
+
+ fun getWorldById(id: ParcelWorldId): ParcelWorld?
+
+ fun getParcelById(id: ParcelId): Parcel?
+
+ fun getWorld(name: String): ParcelWorld?
+
+ fun getWorld(world: World): ParcelWorld? = getWorld(world.name)
+
+ fun getWorld(block: Block): ParcelWorld? = getWorld(block.world)
+
+ fun getWorld(loc: Location): ParcelWorld? = getWorld(loc.world)
+
+ fun getWorld(entity: Entity): ParcelWorld? = getWorld(entity.location)
+
+ fun getParcelAt(worldName: String, x: Int, z: Int): Parcel? = getWorld(worldName)?.locator?.getParcelAt(x, z)
+
+ fun getParcelAt(world: World, x: Int, z: Int): Parcel? = getParcelAt(world.name, x, z)
+
+ fun getParcelAt(world: World, vec: Vec2i): Parcel? = getParcelAt(world, vec.x, vec.z)
+
+ fun getParcelAt(loc: Location): Parcel? = getParcelAt(loc.world, loc.x.floor(), loc.z.floor())
+
+ fun getParcelAt(entity: Entity): Parcel? = getParcelAt(entity.location)
+
+ fun getParcelAt(block: Block): Parcel? = getParcelAt(block.world, block.x, block.z)
+
+ fun getWorldGenerator(worldName: String): ParcelGenerator?
+
+ fun loadWorlds()
+
+ fun acquireBlockVisitorPermit(parcelId: ParcelId, with: Permit): Boolean
+
+ @Throws(IllegalStateException::class)
+ fun releaseBlockVisitorPermit(parcelId: ParcelId, with: Permit)
+
+ fun trySubmitBlockVisitor(permit: Permit, parcelIds: Array<out ParcelId>, function: JobFunction): Job?
+
+ fun swapParcels(parcelId1: ParcelId, parcelId2: ParcelId): Job?
+}
+
+interface ParcelLocator {
+ val world: World
+
+ fun getParcelIdAt(x: Int, z: Int): ParcelId?
+
+ fun getParcelAt(x: Int, z: Int): Parcel?
+
+ fun getParcelAt(vec: Vec2i): Parcel? = getParcelAt(vec.x, vec.z)
+
+ fun getParcelAt(loc: Location): Parcel? = getParcelAt(loc.x.floor(), loc.z.floor()).takeIf { loc.world == world }
+
+ fun getParcelAt(entity: Entity): Parcel? = getParcelAt(entity.location).takeIf { entity.world == world }
+
+ fun getParcelAt(block: Block): Parcel? = getParcelAt(block.x, block.z).takeIf { block.world == world }
+}
+
+typealias ParcelContainerFactory = (ParcelWorld) -> ParcelContainer
+
+interface ParcelContainer {
+
+ fun getParcelById(x: Int, z: Int): Parcel?
+
+ fun getParcelById(id: Vec2i): Parcel? = getParcelById(id.x, id.z)
+
+ fun getParcelById(id: ParcelId): Parcel?
+
+ fun nextEmptyParcel(): Parcel?
+
+}
+
+interface ParcelWorld : ParcelLocator, ParcelContainer {
+ val id: ParcelWorldId
+ val name: String
+ val uid: UUID?
+ val options: RuntimeWorldOptions
+ val generator: ParcelGenerator
+ val storage: Storage
+ val container: ParcelContainer
+ val locator: ParcelLocator
+ val blockManager: ParcelBlockManager
+ val globalPrivileges: GlobalPrivilegesManager
+
+ val creationTime: DateTime?
+}
diff --git a/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt b/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt
index a6ebcd8..b2d52a9 100644
--- a/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt
+++ b/src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt
@@ -1,153 +1,153 @@
-package io.dico.parcels2
-
-import io.dico.dicore.Registrator
-import io.dico.dicore.command.EOverridePolicy
-import io.dico.dicore.command.ICommandDispatcher
-import io.dico.parcels2.command.getParcelCommands
-import io.dico.parcels2.defaultimpl.GlobalPrivilegesManagerImpl
-import io.dico.parcels2.defaultimpl.ParcelProviderImpl
-import io.dico.parcels2.listener.ParcelEntityTracker
-import io.dico.parcels2.listener.ParcelListeners
-import io.dico.parcels2.listener.WorldEditListener
-import io.dico.parcels2.options.Options
-import io.dico.parcels2.options.optionsMapper
-import io.dico.parcels2.storage.Storage
-import io.dico.parcels2.util.MainThreadDispatcher
-import io.dico.parcels2.util.PluginScheduler
-import io.dico.parcels2.util.ext.tryCreate
-import io.dico.parcels2.util.isServerThread
-import kotlinx.coroutines.CoroutineScope
-import org.bukkit.Bukkit
-import org.bukkit.generator.ChunkGenerator
-import org.bukkit.plugin.Plugin
-import org.bukkit.plugin.java.JavaPlugin
-import org.slf4j.Logger
-import org.slf4j.LoggerFactory
-import java.io.File
-import kotlin.coroutines.CoroutineContext
-
-val logger: Logger = LoggerFactory.getLogger("ParcelsPlugin")
-private inline val plogger get() = logger
-
-class ParcelsPlugin : JavaPlugin(), CoroutineScope, PluginScheduler {
- lateinit var optionsFile: File; private set
- lateinit var options: Options; private set
- lateinit var parcelProvider: ParcelProvider; private set
- lateinit var storage: Storage; private set
- lateinit var globalPrivileges: GlobalPrivilegesManager; private set
-
- val registrator = Registrator(this)
- lateinit var entityTracker: ParcelEntityTracker; private set
- private var listeners: ParcelListeners? = null
- private var cmdDispatcher: ICommandDispatcher? = null
-
- override val coroutineContext: CoroutineContext = MainThreadDispatcher(this)
- override val plugin: Plugin get() = this
- val jobDispatcher: JobDispatcher by lazy { BukkitJobDispatcher(this, options.tickJobtime) }
-
- override fun onEnable() {
- plogger.info("Is server thread: ${isServerThread()}")
- plogger.info("Debug enabled: ${plogger.isDebugEnabled}")
- plogger.debug(System.getProperty("user.dir"))
- if (!init()) {
- Bukkit.getPluginManager().disablePlugin(this)
- }
- }
-
- override fun onDisable() {
- val hasWorkers = jobDispatcher.jobs.isNotEmpty()
- if (hasWorkers) {
- plogger.warn("Parcels is attempting to complete all ${jobDispatcher.jobs.size} remaining jobs before shutdown...")
- }
- jobDispatcher.completeAllTasks()
- if (hasWorkers) {
- plogger.info("Parcels has completed the remaining jobs.")
- }
-
- cmdDispatcher?.unregisterFromCommandMap()
- }
-
- private fun init(): Boolean {
- optionsFile = File(dataFolder, "options.yml")
- options = Options()
- parcelProvider = ParcelProviderImpl(this)
-
- try {
- if (!loadOptions()) return false
-
- try {
- storage = options.storage.newInstance()
- storage.init()
- } catch (ex: Exception) {
- plogger.error("Failed to connect to database", ex)
- return false
- }
-
- globalPrivileges = GlobalPrivilegesManagerImpl(this)
- entityTracker = ParcelEntityTracker(parcelProvider)
- } catch (ex: Exception) {
- plogger.error("Error loading options", ex)
- return false
- }
-
- registerListeners()
- registerCommands()
-
- parcelProvider.loadWorlds()
- return true
- }
-
- fun loadOptions(): Boolean {
- when {
- optionsFile.exists() -> optionsMapper.readerForUpdating(options).readValue<Options>(optionsFile)
- else -> run {
- options.addWorld("parcels")
- if (saveOptions()) {
- plogger.warn("Created options file with a world template. Please review it before next start.")
- } else {
- plogger.error("Failed to save options file ${optionsFile.canonicalPath}")
- }
- return false
- }
- }
- return true
- }
-
- fun saveOptions(): Boolean {
- if (optionsFile.tryCreate()) {
- try {
- optionsMapper.writeValue(optionsFile, options)
- } catch (ex: Throwable) {
- optionsFile.delete()
- throw ex
- }
- return true
- }
- return false
- }
-
- override fun getDefaultWorldGenerator(worldName: String, generatorId: String?): ChunkGenerator? {
- return parcelProvider.getWorldGenerator(worldName)
- }
-
- private fun registerCommands() {
- cmdDispatcher = getParcelCommands(this).apply {
- registerToCommandMap("parcels:", EOverridePolicy.FALLBACK_ONLY)
- }
- }
-
- private fun registerListeners() {
- if (listeners == null) {
- listeners = ParcelListeners(parcelProvider, entityTracker, storage)
- registrator.registerListeners(listeners!!)
-
- val worldEditPlugin = server.pluginManager.getPlugin("WorldEdit")
- if (worldEditPlugin != null) {
- WorldEditListener.register(this, worldEditPlugin)
- }
- }
-
- scheduleRepeating(100, 5, entityTracker::tick)
- }
-
+package io.dico.parcels2
+
+import io.dico.dicore.Registrator
+import io.dico.dicore.command.EOverridePolicy
+import io.dico.dicore.command.ICommandDispatcher
+import io.dico.parcels2.command.getParcelCommands
+import io.dico.parcels2.defaultimpl.GlobalPrivilegesManagerImpl
+import io.dico.parcels2.defaultimpl.ParcelProviderImpl
+import io.dico.parcels2.listener.ParcelEntityTracker
+import io.dico.parcels2.listener.ParcelListeners
+import io.dico.parcels2.listener.WorldEditListener
+import io.dico.parcels2.options.Options
+import io.dico.parcels2.options.optionsMapper
+import io.dico.parcels2.storage.Storage
+import io.dico.parcels2.util.MainThreadDispatcher
+import io.dico.parcels2.util.PluginScheduler
+import io.dico.parcels2.util.ext.tryCreate
+import io.dico.parcels2.util.isServerThread
+import kotlinx.coroutines.CoroutineScope
+import org.bukkit.Bukkit
+import org.bukkit.generator.ChunkGenerator
+import org.bukkit.plugin.Plugin
+import org.bukkit.plugin.java.JavaPlugin
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+import java.io.File
+import kotlin.coroutines.CoroutineContext
+
+val logger: Logger = LoggerFactory.getLogger("ParcelsPlugin")
+private inline val plogger get() = logger
+
+class ParcelsPlugin : JavaPlugin(), CoroutineScope, PluginScheduler {
+ lateinit var optionsFile: File; private set
+ lateinit var options: Options; private set
+ lateinit var parcelProvider: ParcelProvider; private set
+ lateinit var storage: Storage; private set
+ lateinit var globalPrivileges: GlobalPrivilegesManager; private set
+
+ val registrator = Registrator(this)
+ lateinit var entityTracker: ParcelEntityTracker; private set
+ private var listeners: ParcelListeners? = null
+ private var cmdDispatcher: ICommandDispatcher? = null
+
+ override val coroutineContext: CoroutineContext = MainThreadDispatcher(this)
+ override val plugin: Plugin get() = this
+ val jobDispatcher: JobDispatcher by lazy { BukkitJobDispatcher(this, options.tickJobtime) }
+
+ override fun onEnable() {
+ plogger.info("Is server thread: ${isServerThread()}")
+ plogger.info("Debug enabled: ${plogger.isDebugEnabled}")
+ plogger.debug(System.getProperty("user.dir"))
+ if (!init()) {
+ Bukkit.getPluginManager().disablePlugin(this)
+ }
+ }
+
+ override fun onDisable() {
+ val hasWorkers = jobDispatcher.jobs.isNotEmpty()
+ if (hasWorkers) {
+ plogger.warn("Parcels is attempting to complete all ${jobDispatcher.jobs.size} remaining jobs before shutdown...")
+ }
+ jobDispatcher.completeAllTasks()
+ if (hasWorkers) {
+ plogger.info("Parcels has completed the remaining jobs.")
+ }
+
+ cmdDispatcher?.unregisterFromCommandMap()
+ }
+
+ private fun init(): Boolean {
+ optionsFile = File(dataFolder, "options.yml")
+ options = Options()
+ parcelProvider = ParcelProviderImpl(this)
+
+ try {
+ if (!loadOptions()) return false
+
+ try {
+ storage = options.storage.newInstance()
+ storage.init()
+ } catch (ex: Exception) {
+ plogger.error("Failed to connect to database", ex)
+ return false
+ }
+
+ globalPrivileges = GlobalPrivilegesManagerImpl(this)
+ entityTracker = ParcelEntityTracker(parcelProvider)
+ } catch (ex: Exception) {
+ plogger.error("Error loading options", ex)
+ return false
+ }
+
+ registerListeners()
+ registerCommands()
+
+ parcelProvider.loadWorlds()
+ return true
+ }
+
+ fun loadOptions(): Boolean {
+ when {
+ optionsFile.exists() -> optionsMapper.readerForUpdating(options).readValue<Options>(optionsFile)
+ else -> run {
+ options.addWorld("parcels")
+ if (saveOptions()) {
+ plogger.warn("Created options file with a world template. Please review it before next start.")
+ } else {
+ plogger.error("Failed to save options file ${optionsFile.canonicalPath}")
+ }
+ return false
+ }
+ }
+ return true
+ }
+
+ fun saveOptions(): Boolean {
+ if (optionsFile.tryCreate()) {
+ try {
+ optionsMapper.writeValue(optionsFile, options)
+ } catch (ex: Throwable) {
+ optionsFile.delete()
+ throw ex
+ }
+ return true
+ }
+ return false
+ }
+
+ override fun getDefaultWorldGenerator(worldName: String, generatorId: String?): ChunkGenerator? {
+ return parcelProvider.getWorldGenerator(worldName)
+ }
+
+ private fun registerCommands() {
+ cmdDispatcher = getParcelCommands(this).apply {
+ registerToCommandMap("parcels:", EOverridePolicy.FALLBACK_ONLY)
+ }
+ }
+
+ private fun registerListeners() {
+ if (listeners == null) {
+ listeners = ParcelListeners(parcelProvider, entityTracker, storage)
+ registrator.registerListeners(listeners!!)
+
+ val worldEditPlugin = server.pluginManager.getPlugin("WorldEdit")
+ if (worldEditPlugin != null) {
+ WorldEditListener.register(this, worldEditPlugin)
+ }
+ }
+
+ scheduleRepeating(100, 5, entityTracker::tick)
+ }
+
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/PlayerProfile.kt b/src/main/kotlin/io/dico/parcels2/PlayerProfile.kt
index e3e0f55..6c30c27 100644
--- a/src/main/kotlin/io/dico/parcels2/PlayerProfile.kt
+++ b/src/main/kotlin/io/dico/parcels2/PlayerProfile.kt
@@ -1,184 +1,184 @@
-@file:Suppress("unused", "UsePropertyAccessSyntax", "DEPRECATION")
-
-package io.dico.parcels2
-
-import io.dico.parcels2.storage.Storage
-import io.dico.parcels2.util.ext.PLAYER_NAME_PLACEHOLDER
-import io.dico.parcels2.util.ext.isValid
-import io.dico.parcels2.util.ext.uuid
-import io.dico.parcels2.util.getOfflinePlayer
-import io.dico.parcels2.util.getPlayerName
-import org.bukkit.Bukkit
-import org.bukkit.OfflinePlayer
-import java.util.UUID
-
-interface PlayerProfile {
- val uuid: UUID? get() = null
- val name: String?
- val nameOrBukkitName: String?
- val notNullName: String
- val isStar: Boolean get() = this is Star
- val exists: Boolean get() = this is RealImpl
-
- fun matches(player: OfflinePlayer, allowNameMatch: Boolean = false): Boolean
-
- fun equals(other: PlayerProfile): Boolean
-
- override fun equals(other: Any?): Boolean
- override fun hashCode(): Int
-
- val isFake: Boolean get() = this is Fake
- val isReal: Boolean get() = this is Real
-
- companion object {
- fun safe(uuid: UUID?, name: String?): PlayerProfile? {
- if (uuid != null) return Real(uuid, name)
- if (name != null) return invoke(name)
- return null
- }
-
- operator fun invoke(uuid: UUID?, name: String?): PlayerProfile {
- return safe(uuid, name) ?: throw IllegalArgumentException("One of uuid and name must not be null")
- }
-
- operator fun invoke(uuid: UUID): Real {
- if (uuid == Star.uuid) return Star
- return RealImpl(uuid, null)
- }
-
- operator fun invoke(name: String): PlayerProfile {
- if (name == Star.name) return Star
- return Fake(name)
- }
-
- operator fun invoke(player: OfflinePlayer): PlayerProfile {
- return if (player.isValid) Real(player.uuid, player.name) else Fake(player.name)
- }
-
- fun nameless(player: OfflinePlayer): Real {
- if (!player.isValid) throw IllegalArgumentException("The given OfflinePlayer is not valid")
- return RealImpl(player.uuid, null)
- }
-
- fun byName(input: String, allowReal: Boolean = true, allowFake: Boolean = false): PlayerProfile {
- if (!allowReal) {
- if (!allowFake) throw IllegalArgumentException("at least one of allowReal and allowFake must be true")
- return Fake(input)
- }
-
- if (input == Star.name) return Star
-
- return getOfflinePlayer(input)?.let { PlayerProfile(it) } ?: Unresolved(input)
- }
- }
-
- interface Real : PlayerProfile {
- override val uuid: UUID
- override val nameOrBukkitName: String?
- // If a player is online, their name is prioritized to get name changes right immediately
- get() = Bukkit.getPlayer(uuid)?.name ?: name ?: getPlayerName(uuid)
- override val notNullName: String
- get() = nameOrBukkitName ?: PLAYER_NAME_PLACEHOLDER
-
- val player: OfflinePlayer? get() = getOfflinePlayer(uuid)
-
- override fun matches(player: OfflinePlayer, allowNameMatch: Boolean): Boolean {
- return uuid == player.uuid || (allowNameMatch && name?.let { it == player.name } == true)
- }
-
- override fun equals(other: PlayerProfile): Boolean {
- return other is Real && uuid == other.uuid
- }
-
- companion object {
- fun byName(name: String): PlayerProfile {
- if (name == Star.name) return Star
- return Unresolved(name)
- }
-
- operator fun invoke(uuid: UUID, name: String?): Real {
- if (name == Star.name || uuid == Star.uuid) return Star
- return RealImpl(uuid, name)
- }
-
- fun safe(uuid: UUID?, name: String?): Real? {
- if (name == Star.name || uuid == Star.uuid) return Star
- if (uuid == null) return null
- return RealImpl(uuid, name)
- }
-
- }
- }
-
- object Star : BaseImpl(), Real {
- override val name get() = "*"
- override val nameOrBukkitName get() = name
- override val notNullName get() = name
-
- // hopefully nobody will have this random UUID :)
- override val uuid: UUID = UUID.fromString("7d09c4c6-117d-4f36-9778-c4d24618cee1")
-
- override fun matches(player: OfflinePlayer, allowNameMatch: Boolean): Boolean {
- return true
- }
-
- override fun toString() = "Star"
- }
-
- abstract class NameOnly(override val name: String) : BaseImpl() {
- override val notNullName get() = name
- override val nameOrBukkitName: String get() = name
-
- override fun matches(player: OfflinePlayer, allowNameMatch: Boolean): Boolean {
- return allowNameMatch && player.name == name
- }
-
- override fun toString() = "${javaClass.simpleName}($name)"
- }
-
- class Fake(name: String) : NameOnly(name) {
- override fun equals(other: PlayerProfile): Boolean {
- return other is Fake && other.name == name
- }
- }
-
- class Unresolved(name: String) : NameOnly(name) {
- override fun equals(other: PlayerProfile): Boolean {
- return other is Unresolved && name == other.name
- }
-
- suspend fun tryResolveSuspendedly(storage: Storage): Real? {
- return storage.getPlayerUuidForName(name).await()?.let { resolve(it) }
- }
-
- fun resolve(uuid: UUID): Real {
- return RealImpl(uuid, name)
- }
-
- fun throwException(): Nothing {
- throw IllegalArgumentException("A UUID for the player $name can not be found")
- }
- }
-
- abstract class BaseImpl : PlayerProfile {
- override fun equals(other: Any?): Boolean {
- return this === other || (other is PlayerProfile && equals(other))
- }
-
- override fun hashCode(): Int {
- return uuid?.hashCode() ?: name!!.hashCode()
- }
- }
-
- private class RealImpl(override val uuid: UUID, override val name: String?) : BaseImpl(), Real {
- override fun toString() = "Real($notNullName)"
- }
-
-}
-
-suspend fun PlayerProfile.resolved(storage: Storage, resolveToFake: Boolean = false): PlayerProfile? =
- when (this) {
- is PlayerProfile.Unresolved -> tryResolveSuspendedly(storage)
- ?: if (resolveToFake) PlayerProfile.Fake(name) else null
- else -> this
+@file:Suppress("unused", "UsePropertyAccessSyntax", "DEPRECATION")
+
+package io.dico.parcels2
+
+import io.dico.parcels2.storage.Storage
+import io.dico.parcels2.util.ext.PLAYER_NAME_PLACEHOLDER
+import io.dico.parcels2.util.ext.isValid
+import io.dico.parcels2.util.ext.uuid
+import io.dico.parcels2.util.getOfflinePlayer
+import io.dico.parcels2.util.getPlayerName
+import org.bukkit.Bukkit
+import org.bukkit.OfflinePlayer
+import java.util.UUID
+
+interface PlayerProfile {
+ val uuid: UUID? get() = null
+ val name: String?
+ val nameOrBukkitName: String?
+ val notNullName: String
+ val isStar: Boolean get() = this is Star
+ val exists: Boolean get() = this is RealImpl
+
+ fun matches(player: OfflinePlayer, allowNameMatch: Boolean = false): Boolean
+
+ fun equals(other: PlayerProfile): Boolean
+
+ override fun equals(other: Any?): Boolean
+ override fun hashCode(): Int
+
+ val isFake: Boolean get() = this is Fake
+ val isReal: Boolean get() = this is Real
+
+ companion object {
+ fun safe(uuid: UUID?, name: String?): PlayerProfile? {
+ if (uuid != null) return Real(uuid, name)
+ if (name != null) return invoke(name)
+ return null
+ }
+
+ operator fun invoke(uuid: UUID?, name: String?): PlayerProfile {
+ return safe(uuid, name) ?: throw IllegalArgumentException("One of uuid and name must not be null")
+ }
+
+ operator fun invoke(uuid: UUID): Real {
+ if (uuid == Star.uuid) return Star
+ return RealImpl(uuid, null)
+ }
+
+ operator fun invoke(name: String): PlayerProfile {
+ if (name == Star.name) return Star
+ return Fake(name)
+ }
+
+ operator fun invoke(player: OfflinePlayer): PlayerProfile {
+ return if (player.isValid) Real(player.uuid, player.name) else Fake(player.name)
+ }
+
+ fun nameless(player: OfflinePlayer): Real {
+ if (!player.isValid) throw IllegalArgumentException("The given OfflinePlayer is not valid")
+ return RealImpl(player.uuid, null)
+ }
+
+ fun byName(input: String, allowReal: Boolean = true, allowFake: Boolean = false): PlayerProfile {
+ if (!allowReal) {
+ if (!allowFake) throw IllegalArgumentException("at least one of allowReal and allowFake must be true")
+ return Fake(input)
+ }
+
+ if (input == Star.name) return Star
+
+ return getOfflinePlayer(input)?.let { PlayerProfile(it) } ?: Unresolved(input)
+ }
+ }
+
+ interface Real : PlayerProfile {
+ override val uuid: UUID
+ override val nameOrBukkitName: String?
+ // If a player is online, their name is prioritized to get name changes right immediately
+ get() = Bukkit.getPlayer(uuid)?.name ?: name ?: getPlayerName(uuid)
+ override val notNullName: String
+ get() = nameOrBukkitName ?: PLAYER_NAME_PLACEHOLDER
+
+ val player: OfflinePlayer? get() = getOfflinePlayer(uuid)
+
+ override fun matches(player: OfflinePlayer, allowNameMatch: Boolean): Boolean {
+ return uuid == player.uuid || (allowNameMatch && name?.let { it == player.name } == true)
+ }
+
+ override fun equals(other: PlayerProfile): Boolean {
+ return other is Real && uuid == other.uuid
+ }
+
+ companion object {
+ fun byName(name: String): PlayerProfile {
+ if (name == Star.name) return Star
+ return Unresolved(name)
+ }
+
+ operator fun invoke(uuid: UUID, name: String?): Real {
+ if (name == Star.name || uuid == Star.uuid) return Star
+ return RealImpl(uuid, name)
+ }
+
+ fun safe(uuid: UUID?, name: String?): Real? {
+ if (name == Star.name || uuid == Star.uuid) return Star
+ if (uuid == null) return null
+ return RealImpl(uuid, name)
+ }
+
+ }
+ }
+
+ object Star : BaseImpl(), Real {
+ override val name get() = "*"
+ override val nameOrBukkitName get() = name
+ override val notNullName get() = name
+
+ // hopefully nobody will have this random UUID :)
+ override val uuid: UUID = UUID.fromString("7d09c4c6-117d-4f36-9778-c4d24618cee1")
+
+ override fun matches(player: OfflinePlayer, allowNameMatch: Boolean): Boolean {
+ return true
+ }
+
+ override fun toString() = "Star"
+ }
+
+ abstract class NameOnly(override val name: String) : BaseImpl() {
+ override val notNullName get() = name
+ override val nameOrBukkitName: String get() = name
+
+ override fun matches(player: OfflinePlayer, allowNameMatch: Boolean): Boolean {
+ return allowNameMatch && player.name == name
+ }
+
+ override fun toString() = "${javaClass.simpleName}($name)"
+ }
+
+ class Fake(name: String) : NameOnly(name) {
+ override fun equals(other: PlayerProfile): Boolean {
+ return other is Fake && other.name == name
+ }
+ }
+
+ class Unresolved(name: String) : NameOnly(name) {
+ override fun equals(other: PlayerProfile): Boolean {
+ return other is Unresolved && name == other.name
+ }
+
+ suspend fun tryResolveSuspendedly(storage: Storage): Real? {
+ return storage.getPlayerUuidForName(name).await()?.let { resolve(it) }
+ }
+
+ fun resolve(uuid: UUID): Real {
+ return RealImpl(uuid, name)
+ }
+
+ fun throwException(): Nothing {
+ throw IllegalArgumentException("A UUID for the player $name can not be found")
+ }
+ }
+
+ abstract class BaseImpl : PlayerProfile {
+ override fun equals(other: Any?): Boolean {
+ return this === other || (other is PlayerProfile && equals(other))
+ }
+
+ override fun hashCode(): Int {
+ return uuid?.hashCode() ?: name!!.hashCode()
+ }
+ }
+
+ private class RealImpl(override val uuid: UUID, override val name: String?) : BaseImpl(), Real {
+ override fun toString() = "Real($notNullName)"
+ }
+
+}
+
+suspend fun PlayerProfile.resolved(storage: Storage, resolveToFake: Boolean = false): PlayerProfile? =
+ when (this) {
+ is PlayerProfile.Unresolved -> tryResolveSuspendedly(storage)
+ ?: if (resolveToFake) PlayerProfile.Fake(name) else null
+ else -> this
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/Privilege.kt b/src/main/kotlin/io/dico/parcels2/Privilege.kt
index 135d7c1..892ed4c 100644
--- a/src/main/kotlin/io/dico/parcels2/Privilege.kt
+++ b/src/main/kotlin/io/dico/parcels2/Privilege.kt
@@ -1,125 +1,125 @@
-package io.dico.parcels2
-
-import io.dico.parcels2.Privilege.DEFAULT
-import java.util.Collections
-
-typealias PrivilegeKey = PlayerProfile.Real
-typealias PrivilegeMap = Map<PrivilegeKey, Privilege>
-typealias MutablePrivilegeMap = MutableMap<PrivilegeKey, Privilege>
-
-@Suppress("FunctionName")
-fun MutablePrivilegeMap(): MutablePrivilegeMap = hashMapOf()
-
-@Suppress("UNCHECKED_CAST")
-val EmptyPrivilegeMap = Collections.emptyMap<Any, Any>() as MutablePrivilegeMap
-
-enum class Privilege(
- val number: Int,
- val transient: Boolean = false
-) {
- BANNED(1),
- DEFAULT(2),
- CAN_BUILD(3),
- CAN_MANAGE(4),
-
- OWNER(-1, transient = true),
- ADMIN(-1, transient = true);
-
- fun implies(other: Privilege): Boolean =
- when {
- other > DEFAULT -> this >= other
- other == DEFAULT -> this == other
- else -> this <= other
- }
-
- fun isChangeInDirection(positiveDirection: Boolean, update: Privilege): Boolean =
- if (positiveDirection) update > this
- else update < this
-
- fun requireNonTransient(): Privilege {
- if (transient) {
- throw IllegalArgumentException("Transient privilege $this is invalid")
- }
- return this
- }
-
- companion object {
- fun getByNumber(id: Int) =
- when (id) {
- 1 -> BANNED
- 2 -> DEFAULT
- 3 -> CAN_BUILD
- 4 -> CAN_MANAGE
- else -> null
- }
- }
-}
-
-interface RawPrivileges {
- val privilegeMap: PrivilegeMap
- var privilegeOfStar: Privilege
-
- fun getRawStoredPrivilege(key: PrivilegeKey): Privilege
- fun setRawStoredPrivilege(key: PrivilegeKey, privilege: Privilege): Boolean
- fun hasAnyDeclaredPrivileges(): Boolean
-}
-
-open class PrivilegesHolder(override var privilegeMap: MutablePrivilegeMap = EmptyPrivilegeMap) : RawPrivileges {
- private var _privilegeOfStar: Privilege = DEFAULT
-
- override /*open*/ var privilegeOfStar: Privilege
- get() = _privilegeOfStar
- set(value) = run { _privilegeOfStar = value }
-
- protected val isEmpty
- inline get() = privilegeMap === EmptyPrivilegeMap
-
- override fun getRawStoredPrivilege(key: PrivilegeKey) =
- if (key.isStar) _privilegeOfStar
- else privilegeMap.getOrDefault(key, _privilegeOfStar)
-
- override fun setRawStoredPrivilege(key: PrivilegeKey, privilege: Privilege): Boolean {
- privilege.requireNonTransient()
-
- if (key.isStar) {
- if (_privilegeOfStar == privilege) return false
- _privilegeOfStar = privilege
- return true
- }
-
- if (isEmpty) {
- if (privilege == DEFAULT) return false
- privilegeMap = MutablePrivilegeMap()
- }
-
- return if (privilege == DEFAULT) privilegeMap.remove(key) != null
- else privilegeMap.put(key, privilege) != privilege
- }
-
- override fun hasAnyDeclaredPrivileges(): Boolean {
- return privilegeMap.isNotEmpty() || privilegeOfStar != DEFAULT
- }
-
- fun copyPrivilegesFrom(other: PrivilegesHolder) {
- privilegeMap = other.privilegeMap
- privilegeOfStar = other.privilegeOfStar
- }
-
-}
-
-private fun <K, V> MutableMap<K, V>.put(key: K, value: V, override: Boolean) {
- if (override) this[key] = value
- else putIfAbsent(key, value)
-}
-
-fun RawPrivileges.filterProfilesWithPrivilegeTo(map: MutableMap<PrivilegeKey, Privilege>, privilege: Privilege) {
- if (privilegeOfStar.implies(privilege)) {
- map.putIfAbsent(PlayerProfile.Star, privilegeOfStar)
- }
-
- for ((profile, declaredPrivilege) in privilegeMap) {
- if (declaredPrivilege.implies(privilege)) {
- map.putIfAbsent(profile, declaredPrivilege)
- }
- }
-}
+package io.dico.parcels2
+
+import io.dico.parcels2.Privilege.DEFAULT
+import java.util.Collections
+
+typealias PrivilegeKey = PlayerProfile.Real
+typealias PrivilegeMap = Map<PrivilegeKey, Privilege>
+typealias MutablePrivilegeMap = MutableMap<PrivilegeKey, Privilege>
+
+@Suppress("FunctionName")
+fun MutablePrivilegeMap(): MutablePrivilegeMap = hashMapOf()
+
+@Suppress("UNCHECKED_CAST")
+val EmptyPrivilegeMap = Collections.emptyMap<Any, Any>() as MutablePrivilegeMap
+
+enum class Privilege(
+ val number: Int,
+ val transient: Boolean = false
+) {
+ BANNED(1),
+ DEFAULT(2),
+ CAN_BUILD(3),
+ CAN_MANAGE(4),
+
+ OWNER(-1, transient = true),
+ ADMIN(-1, transient = true);
+
+ fun implies(other: Privilege): Boolean =
+ when {
+ other > DEFAULT -> this >= other
+ other == DEFAULT -> this == other
+ else -> this <= other
+ }
+
+ fun isChangeInDirection(positiveDirection: Boolean, update: Privilege): Boolean =
+ if (positiveDirection) update > this
+ else update < this
+
+ fun requireNonTransient(): Privilege {
+ if (transient) {
+ throw IllegalArgumentException("Transient privilege $this is invalid")
+ }
+ return this
+ }
+
+ companion object {
+ fun getByNumber(id: Int) =
+ when (id) {
+ 1 -> BANNED
+ 2 -> DEFAULT
+ 3 -> CAN_BUILD
+ 4 -> CAN_MANAGE
+ else -> null
+ }
+ }
+}
+
+interface RawPrivileges {
+ val privilegeMap: PrivilegeMap
+ var privilegeOfStar: Privilege
+
+ fun getRawStoredPrivilege(key: PrivilegeKey): Privilege
+ fun setRawStoredPrivilege(key: PrivilegeKey, privilege: Privilege): Boolean
+ fun hasAnyDeclaredPrivileges(): Boolean
+}
+
+open class PrivilegesHolder(override var privilegeMap: MutablePrivilegeMap = EmptyPrivilegeMap) : RawPrivileges {
+ private var _privilegeOfStar: Privilege = DEFAULT
+
+ override /*open*/ var privilegeOfStar: Privilege
+ get() = _privilegeOfStar
+ set(value) = run { _privilegeOfStar = value }
+
+ protected val isEmpty
+ inline get() = privilegeMap === EmptyPrivilegeMap
+
+ override fun getRawStoredPrivilege(key: PrivilegeKey) =
+ if (key.isStar) _privilegeOfStar
+ else privilegeMap.getOrDefault(key, _privilegeOfStar)
+
+ override fun setRawStoredPrivilege(key: PrivilegeKey, privilege: Privilege): Boolean {
+ privilege.requireNonTransient()
+
+ if (key.isStar) {
+ if (_privilegeOfStar == privilege) return false
+ _privilegeOfStar = privilege
+ return true
+ }
+
+ if (isEmpty) {
+ if (privilege == DEFAULT) return false
+ privilegeMap = MutablePrivilegeMap()
+ }
+
+ return if (privilege == DEFAULT) privilegeMap.remove(key) != null
+ else privilegeMap.put(key, privilege) != privilege
+ }
+
+ override fun hasAnyDeclaredPrivileges(): Boolean {
+ return privilegeMap.isNotEmpty() || privilegeOfStar != DEFAULT
+ }
+
+ fun copyPrivilegesFrom(other: PrivilegesHolder) {
+ privilegeMap = other.privilegeMap
+ privilegeOfStar = other.privilegeOfStar
+ }
+
+}
+
+private fun <K, V> MutableMap<K, V>.put(key: K, value: V, override: Boolean) {
+ if (override) this[key] = value
+ else putIfAbsent(key, value)
+}
+
+fun RawPrivileges.filterProfilesWithPrivilegeTo(map: MutableMap<PrivilegeKey, Privilege>, privilege: Privilege) {
+ if (privilegeOfStar.implies(privilege)) {
+ map.putIfAbsent(PlayerProfile.Star, privilegeOfStar)
+ }
+
+ for ((profile, declaredPrivilege) in privilegeMap) {
+ if (declaredPrivilege.implies(privilege)) {
+ map.putIfAbsent(profile, declaredPrivilege)
+ }
+ }
+}
diff --git a/src/main/kotlin/io/dico/parcels2/Privileges.kt b/src/main/kotlin/io/dico/parcels2/Privileges.kt
index 632f1a4..6cdd6d0 100644
--- a/src/main/kotlin/io/dico/parcels2/Privileges.kt
+++ b/src/main/kotlin/io/dico/parcels2/Privileges.kt
@@ -1,64 +1,64 @@
-package io.dico.parcels2
-
-import io.dico.parcels2.PrivilegeChangeResult.*
-import io.dico.parcels2.util.ext.PERM_ADMIN_MANAGE
-import io.dico.parcels2.util.ext.PERM_BAN_BYPASS
-import io.dico.parcels2.util.ext.PERM_BUILD_ANYWHERE
-import org.bukkit.OfflinePlayer
-import org.bukkit.entity.Player
-
-interface Privileges : RawPrivileges {
- val keyOfOwner: PlayerProfile.Real?
-
- fun getStoredPrivilege(key: PrivilegeKey): Privilege {
- return if (key == keyOfOwner) Privilege.OWNER
- else getRawStoredPrivilege(key)
- }
-
- override var privilegeOfStar: Privilege
- get() = getStoredPrivilege(PlayerProfile.Star)
- set(value) {
- setRawStoredPrivilege(PlayerProfile.Star, value)
- }
-}
-
-val OfflinePlayer.privilegeKey: PrivilegeKey
- inline get() = PlayerProfile.nameless(this)
-
-fun Privileges.getEffectivePrivilege(player: OfflinePlayer, adminPerm: String): Privilege =
- if (player is Player && player.hasPermission(adminPerm)) Privilege.ADMIN
- else getStoredPrivilege(player.privilegeKey)
-
-fun Privileges.canManage(player: OfflinePlayer) = getEffectivePrivilege(player, PERM_ADMIN_MANAGE) >= Privilege.CAN_MANAGE
-fun Privileges.canManageFast(player: OfflinePlayer) = getStoredPrivilege(player.privilegeKey) >= Privilege.CAN_MANAGE
-fun Privileges.canBuild(player: OfflinePlayer) = getEffectivePrivilege(player, PERM_BUILD_ANYWHERE) >= Privilege.CAN_BUILD
-fun Privileges.canBuildFast(player: OfflinePlayer) = getStoredPrivilege(player.privilegeKey) >= Privilege.CAN_BUILD
-fun Privileges.canEnter(player: OfflinePlayer) = getEffectivePrivilege(player, PERM_BAN_BYPASS) >= Privilege.DEFAULT
-fun Privileges.canEnterFast(player: OfflinePlayer) = getStoredPrivilege(player.privilegeKey) >= Privilege.DEFAULT
-
-enum class PrivilegeChangeResult {
- SUCCESS, FAIL, FAIL_OWNER
-}
-
-fun Privileges.changePrivilege(key: PrivilegeKey, positive: Boolean, update: Privilege): PrivilegeChangeResult =
- when {
- key == keyOfOwner -> FAIL_OWNER
- getRawStoredPrivilege(key).isChangeInDirection(positive, update) && setRawStoredPrivilege(key, update) -> SUCCESS
- else -> FAIL
- }
-
-fun Privileges.allowManage(key: PrivilegeKey) = changePrivilege(key, true, Privilege.CAN_MANAGE)
-fun Privileges.disallowManage(key: PrivilegeKey) = changePrivilege(key, false, Privilege.CAN_BUILD)
-fun Privileges.allowBuild(key: PrivilegeKey) = changePrivilege(key, true, Privilege.CAN_BUILD)
-fun Privileges.disallowBuild(key: PrivilegeKey) = changePrivilege(key, false, Privilege.DEFAULT)
-fun Privileges.allowEnter(key: PrivilegeKey) = changePrivilege(key, true, Privilege.DEFAULT)
-fun Privileges.disallowEnter(key: PrivilegeKey) = changePrivilege(key, false, Privilege.BANNED)
-
-interface GlobalPrivileges : RawPrivileges, Privileges {
- override val keyOfOwner: PlayerProfile.Real
-}
-
-interface GlobalPrivilegesManager {
- operator fun get(owner: PlayerProfile.Real): GlobalPrivileges
- operator fun get(owner: OfflinePlayer): GlobalPrivileges = get(owner.privilegeKey)
-}
+package io.dico.parcels2
+
+import io.dico.parcels2.PrivilegeChangeResult.*
+import io.dico.parcels2.util.ext.PERM_ADMIN_MANAGE
+import io.dico.parcels2.util.ext.PERM_BAN_BYPASS
+import io.dico.parcels2.util.ext.PERM_BUILD_ANYWHERE
+import org.bukkit.OfflinePlayer
+import org.bukkit.entity.Player
+
+interface Privileges : RawPrivileges {
+ val keyOfOwner: PlayerProfile.Real?
+
+ fun getStoredPrivilege(key: PrivilegeKey): Privilege {
+ return if (key == keyOfOwner) Privilege.OWNER
+ else getRawStoredPrivilege(key)
+ }
+
+ override var privilegeOfStar: Privilege
+ get() = getStoredPrivilege(PlayerProfile.Star)
+ set(value) {
+ setRawStoredPrivilege(PlayerProfile.Star, value)
+ }
+}
+
+val OfflinePlayer.privilegeKey: PrivilegeKey
+ inline get() = PlayerProfile.nameless(this)
+
+fun Privileges.getEffectivePrivilege(player: OfflinePlayer, adminPerm: String): Privilege =
+ if (player is Player && player.hasPermission(adminPerm)) Privilege.ADMIN
+ else getStoredPrivilege(player.privilegeKey)
+
+fun Privileges.canManage(player: OfflinePlayer) = getEffectivePrivilege(player, PERM_ADMIN_MANAGE) >= Privilege.CAN_MANAGE
+fun Privileges.canManageFast(player: OfflinePlayer) = getStoredPrivilege(player.privilegeKey) >= Privilege.CAN_MANAGE
+fun Privileges.canBuild(player: OfflinePlayer) = getEffectivePrivilege(player, PERM_BUILD_ANYWHERE) >= Privilege.CAN_BUILD
+fun Privileges.canBuildFast(player: OfflinePlayer) = getStoredPrivilege(player.privilegeKey) >= Privilege.CAN_BUILD
+fun Privileges.canEnter(player: OfflinePlayer) = getEffectivePrivilege(player, PERM_BAN_BYPASS) >= Privilege.DEFAULT
+fun Privileges.canEnterFast(player: OfflinePlayer) = getStoredPrivilege(player.privilegeKey) >= Privilege.DEFAULT
+
+enum class PrivilegeChangeResult {
+ SUCCESS, FAIL, FAIL_OWNER
+}
+
+fun Privileges.changePrivilege(key: PrivilegeKey, positive: Boolean, update: Privilege): PrivilegeChangeResult =
+ when {
+ key == keyOfOwner -> FAIL_OWNER
+ getRawStoredPrivilege(key).isChangeInDirection(positive, update) && setRawStoredPrivilege(key, update) -> SUCCESS
+ else -> FAIL
+ }
+
+fun Privileges.allowManage(key: PrivilegeKey) = changePrivilege(key, true, Privilege.CAN_MANAGE)
+fun Privileges.disallowManage(key: PrivilegeKey) = changePrivilege(key, false, Privilege.CAN_BUILD)
+fun Privileges.allowBuild(key: PrivilegeKey) = changePrivilege(key, true, Privilege.CAN_BUILD)
+fun Privileges.disallowBuild(key: PrivilegeKey) = changePrivilege(key, false, Privilege.DEFAULT)
+fun Privileges.allowEnter(key: PrivilegeKey) = changePrivilege(key, true, Privilege.DEFAULT)
+fun Privileges.disallowEnter(key: PrivilegeKey) = changePrivilege(key, false, Privilege.BANNED)
+
+interface GlobalPrivileges : RawPrivileges, Privileges {
+ override val keyOfOwner: PlayerProfile.Real
+}
+
+interface GlobalPrivilegesManager {
+ operator fun get(owner: PlayerProfile.Real): GlobalPrivileges
+ operator fun get(owner: OfflinePlayer): GlobalPrivileges = get(owner.privilegeKey)
+}
diff --git a/src/main/kotlin/io/dico/parcels2/blockvisitor/Attachables.kt b/src/main/kotlin/io/dico/parcels2/blockvisitor/Attachables.kt
index 8f2c565..83567c5 100644
--- a/src/main/kotlin/io/dico/parcels2/blockvisitor/Attachables.kt
+++ b/src/main/kotlin/io/dico/parcels2/blockvisitor/Attachables.kt
@@ -1,61 +1,61 @@
-package io.dico.parcels2.blockvisitor
-
-import io.dico.parcels2.util.math.Vec3i
-import io.dico.parcels2.util.ext.getMaterialsWithWoodTypePrefix
-import io.dico.parcels2.util.ext.getMaterialsWithWoolColorPrefix
-import org.bukkit.Material
-import org.bukkit.Material.*
-import org.bukkit.block.BlockFace
-import org.bukkit.block.data.BlockData
-import org.bukkit.block.data.Directional
-import java.util.EnumSet
-
-private val attachables = EnumSet.of(
- REPEATER, COMPARATOR,
- *getMaterialsWithWoodTypePrefix("PRESSURE_PLATE"),
- STONE_PRESSURE_PLATE, LIGHT_WEIGHTED_PRESSURE_PLATE, HEAVY_WEIGHTED_PRESSURE_PLATE,
- *getMaterialsWithWoodTypePrefix("BUTTON"),
- STONE_BUTTON, LEVER,
- *getMaterialsWithWoodTypePrefix("DOOR"), IRON_DOOR,
- ACTIVATOR_RAIL, POWERED_RAIL, DETECTOR_RAIL, RAIL,
- PISTON, STICKY_PISTON,
- REDSTONE_TORCH, REDSTONE_WALL_TORCH, REDSTONE_WIRE,
- TRIPWIRE, TRIPWIRE_HOOK,
-
- BROWN_MUSHROOM, RED_MUSHROOM, CACTUS, CARROT, COCOA,
- WHEAT, DEAD_BUSH, CHORUS_FLOWER, DANDELION, SUGAR_CANE,
- TALL_GRASS, TALL_SEAGRASS, NETHER_WART, MELON_STEM,
- PUMPKIN_STEM, SUNFLOWER, POTATO, LILY_PAD, VINE,
- *getMaterialsWithWoodTypePrefix("SAPLING"),
-
- SAND, RED_SAND, DRAGON_EGG, ANVIL,
- *getMaterialsWithWoolColorPrefix("CONCRETE_POWDER"),
-
- *getMaterialsWithWoolColorPrefix("CARPET"),
- CAKE, FIRE,
- FLOWER_POT,
- LADDER,
- // NETHER_PORTAL, fuck nether portals
- FLOWER_POT,
- SNOW,
- TORCH, WALL_TORCH,
- *getMaterialsWithWoolColorPrefix("BANNER"),
- *getMaterialsWithWoolColorPrefix("WALL_BANNER"),
- SIGN, WALL_SIGN
-)
-
-fun isAttachable(type: Material) = attachables.contains(type)
-
-fun getSupportingBlock(data: BlockData): Vec3i = when (data) {
- //is MultipleFacing -> // fuck it xD this is good enough
-
- is Directional -> Vec3i.convert(when (data.material) {
- // exceptions
- COCOA -> data.facing
- OAK_DOOR, BIRCH_DOOR, SPRUCE_DOOR, JUNGLE_DOOR, ACACIA_DOOR, DARK_OAK_DOOR, IRON_DOOR -> BlockFace.DOWN
-
- else -> data.facing.oppositeFace
- })
-
- else -> Vec3i.down
-}
+package io.dico.parcels2.blockvisitor
+
+import io.dico.parcels2.util.math.Vec3i
+import io.dico.parcels2.util.ext.getMaterialsWithWoodTypePrefix
+import io.dico.parcels2.util.ext.getMaterialsWithWoolColorPrefix
+import org.bukkit.Material
+import org.bukkit.Material.*
+import org.bukkit.block.BlockFace
+import org.bukkit.block.data.BlockData
+import org.bukkit.block.data.Directional
+import java.util.EnumSet
+
+private val attachables = EnumSet.of(
+ REPEATER, COMPARATOR,
+ *getMaterialsWithWoodTypePrefix("PRESSURE_PLATE"),
+ STONE_PRESSURE_PLATE, LIGHT_WEIGHTED_PRESSURE_PLATE, HEAVY_WEIGHTED_PRESSURE_PLATE,
+ *getMaterialsWithWoodTypePrefix("BUTTON"),
+ STONE_BUTTON, LEVER,
+ *getMaterialsWithWoodTypePrefix("DOOR"), IRON_DOOR,
+ ACTIVATOR_RAIL, POWERED_RAIL, DETECTOR_RAIL, RAIL,
+ PISTON, STICKY_PISTON,
+ REDSTONE_TORCH, REDSTONE_WALL_TORCH, REDSTONE_WIRE,
+ TRIPWIRE, TRIPWIRE_HOOK,
+
+ BROWN_MUSHROOM, RED_MUSHROOM, CACTUS, CARROT, COCOA,
+ WHEAT, DEAD_BUSH, CHORUS_FLOWER, DANDELION, SUGAR_CANE,
+ TALL_GRASS, TALL_SEAGRASS, NETHER_WART, MELON_STEM,
+ PUMPKIN_STEM, SUNFLOWER, POTATO, LILY_PAD, VINE,
+ *getMaterialsWithWoodTypePrefix("SAPLING"),
+
+ SAND, RED_SAND, DRAGON_EGG, ANVIL,
+ *getMaterialsWithWoolColorPrefix("CONCRETE_POWDER"),
+
+ *getMaterialsWithWoolColorPrefix("CARPET"),
+ CAKE, FIRE,
+ FLOWER_POT,
+ LADDER,
+ // NETHER_PORTAL, fuck nether portals
+ FLOWER_POT,
+ SNOW,
+ TORCH, WALL_TORCH,
+ *getMaterialsWithWoolColorPrefix("BANNER"),
+ *getMaterialsWithWoolColorPrefix("WALL_BANNER"),
+ SIGN, WALL_SIGN
+)
+
+fun isAttachable(type: Material) = attachables.contains(type)
+
+fun getSupportingBlock(data: BlockData): Vec3i = when (data) {
+ //is MultipleFacing -> // fuck it xD this is good enough
+
+ is Directional -> Vec3i.convert(when (data.material) {
+ // exceptions
+ COCOA -> data.facing
+ OAK_DOOR, BIRCH_DOOR, SPRUCE_DOOR, JUNGLE_DOOR, ACACIA_DOOR, DARK_OAK_DOOR, IRON_DOOR -> BlockFace.DOWN
+
+ else -> data.facing.oppositeFace
+ })
+
+ else -> Vec3i.down
+}
diff --git a/src/main/kotlin/io/dico/parcels2/blockvisitor/ExtraBlockChange.kt b/src/main/kotlin/io/dico/parcels2/blockvisitor/ExtraBlockChange.kt
index 3f7e070..ddfec27 100644
--- a/src/main/kotlin/io/dico/parcels2/blockvisitor/ExtraBlockChange.kt
+++ b/src/main/kotlin/io/dico/parcels2/blockvisitor/ExtraBlockChange.kt
@@ -1,38 +1,38 @@
-package io.dico.parcels2.blockvisitor
-
-import org.bukkit.block.Block
-import org.bukkit.block.BlockState
-import org.bukkit.block.Sign
-import kotlin.reflect.KClass
-
-interface ExtraBlockChange {
- fun update(block: Block)
-}
-
-abstract class BlockStateChange<T : BlockState> : ExtraBlockChange {
- abstract val stateClass: KClass<T>
-
- abstract fun update(state: T)
-
- override fun update(block: Block) {
- val state = block.state
- if (stateClass.isInstance(state)) {
- @Suppress("UNCHECKED_CAST")
- update(state as T)
- }
- }
-}
-
-class SignStateChange(state: Sign) : BlockStateChange<Sign>() {
- val lines = state.lines
-
- override val stateClass: KClass<Sign>
- get() = Sign::class
-
- override fun update(state: Sign) {
- for (i in lines.indices) {
- val line = lines[i]
- state.setLine(i, line)
- }
- }
-}
+package io.dico.parcels2.blockvisitor
+
+import org.bukkit.block.Block
+import org.bukkit.block.BlockState
+import org.bukkit.block.Sign
+import kotlin.reflect.KClass
+
+interface ExtraBlockChange {
+ fun update(block: Block)
+}
+
+abstract class BlockStateChange<T : BlockState> : ExtraBlockChange {
+ abstract val stateClass: KClass<T>
+
+ abstract fun update(state: T)
+
+ override fun update(block: Block) {
+ val state = block.state
+ if (stateClass.isInstance(state)) {
+ @Suppress("UNCHECKED_CAST")
+ update(state as T)
+ }
+ }
+}
+
+class SignStateChange(state: Sign) : BlockStateChange<Sign>() {
+ val lines = state.lines
+
+ override val stateClass: KClass<Sign>
+ get() = Sign::class
+
+ override fun update(state: Sign) {
+ for (i in lines.indices) {
+ val line = lines[i]
+ state.setLine(i, line)
+ }
+ }
+}
diff --git a/src/main/kotlin/io/dico/parcels2/blockvisitor/RegionTraverser.kt b/src/main/kotlin/io/dico/parcels2/blockvisitor/RegionTraverser.kt
index b749b36..6525655 100644
--- a/src/main/kotlin/io/dico/parcels2/blockvisitor/RegionTraverser.kt
+++ b/src/main/kotlin/io/dico/parcels2/blockvisitor/RegionTraverser.kt
@@ -1,323 +1,323 @@
-package io.dico.parcels2.blockvisitor
-
-import io.dico.parcels2.util.math.Dimension
-import io.dico.parcels2.util.math.Region
-import io.dico.parcels2.util.math.Vec3i
-import io.dico.parcels2.util.math.clampMax
-
-private typealias Scope = SequenceScope<Vec3i>
-/*
-class ParcelTraverser(
- val parcelProvider: ParcelProvider,
- val delegate: RegionTraverser,
- scope: CoroutineScope
-) : RegionTraverser(), CoroutineScope by scope {
-
- class OccupiedException(parcelId: ParcelId) : Exception("Parcel $parcelId is occupied")
-
- /**
- * Traverse the blocks of parcel's land
- * The iterator must be exhausted, else the permit to traverse it will not be reclaimed.
- *
- * @throws OccupiedException if a parcel is maintained with the given parcel id and an
- * iterator exists for it that has not been exhausted
- */
- fun traverseParcel(parcelId: ParcelId): Iterator<Vec3i> {
- val world = parcelProvider.getWorldById(parcelId.worldId)
- ?: throw IllegalArgumentException()
- val parcel = parcelProvider.getParcelById(parcelId)
-
- val medium = if (parcel != null) {
- if (parcel.hasBlockVisitors || parcel !is ParcelImpl) {
- throw OccupiedException(parcelId)
- }
- parcel.hasBlockVisitors = true
- TraverserMedium { parcel.hasBlockVisitors = false }
- } else {
- TraverserMedium.DoNothing
- }
-
- val region = world.blockManager.getRegion(parcelId)
- return traverseRegion(region, world.world.maxHeight, medium)
- }
-
- override suspend fun Scope.build(region: Region, medium: TraverserMedium) {
- with(delegate) {
- return build(region, medium)
- }
- }
-
-}
-
-@Suppress("FunctionName")
-inline fun TraverserMedium(crossinline whenComplete: () -> Unit) =
- object : TraverserMedium {
- override fun iterationCompleted() {
- whenComplete()
- }
- }
-
-/**
- * An object that is able to communicate with an iterator returned by [RegionTraverser]
- *
- */
-interface TraverserMedium {
-
- /**
- * Called by the traverser during first [Iterator.hasNext] call that returns false
- */
- fun iterationCompleted()
-
- /**
- * The default [TraverserMedium], which does nothing.
- */
- object DoNothing : TraverserMedium {
- override fun iterationCompleted() {}
- }
-}*/
-
-sealed class RegionTraverser {
-
- /**
- * Get an iterator traversing [region] using this traverser.
- * Depending on the implementation, [region] might be traversed in a specific order and direction.
- */
- fun traverseRegion(
- region: Region,
- worldHeight: Int = 256/*,
- medium: TraverserMedium = TraverserMedium.DoNothing*/
- ): Iterator<Vec3i> = iterator { build(validify(region, worldHeight)/*, medium*/) }
-
- abstract suspend fun Scope.build(region: Region/*, medium: TraverserMedium = TraverserMedium.DoNothing*/)
-
- companion object {
- val upward = Directional(TraverseDirection(1, 1, 1), TraverseOrderFactory.createWith(Dimension.Y, Dimension.X))
- val downward = Directional(TraverseDirection(1, -1, 1), TraverseOrderFactory.createWith(Dimension.Y, Dimension.X))
- val toClear get() = downward
- val toFill get() = upward
-
- /**
- * The returned [RegionTraverser] will traverse the regions
- * * below and including absolute level [y] first, in [upward] direction.
- * * above absolute level [y] last, in [downward] direction.
- */
- fun convergingTo(y: Int) = Slicing(y, upward, downward, true)
-
- /**
- * The returned [RegionTraverser] will traverse the regions
- * * above absolute level [y] first, in [upward] direction.
- * * below and including absolute level [y] second, in [downward] direction.
- */
- fun separatingFrom(y: Int) = Slicing(y, downward, upward, false)
-
- private fun validify(region: Region, worldHeight: Int): Region {
- if (region.origin.y < 0) {
- val origin = region.origin withY 0
- val size = region.size.withY((region.size.y + region.origin.y).clampMax(worldHeight))
- return Region(origin, size)
- }
-
- if (region.origin.y + region.size.y > worldHeight) {
- val size = region.size.withY(worldHeight - region.origin.y)
- return Region(region.origin, size)
- }
-
- return region
- }
-
- }
-
- class Directional(
- val direction: TraverseDirection,
- val order: TraverseOrder
- ) : RegionTraverser() {
-
- private inline fun iterate(max: Int, increasing: Boolean, action: (Int) -> Unit) {
- for (i in 0..max) {
- action(if (increasing) i else max - i)
- }
- }
-
- override suspend fun Scope.build(region: Region/*, medium: TraverserMedium*/) {
- val order = order
- val (primary, secondary, tertiary) = order.toArray()
- val (origin, size) = region
-
- val maxOfPrimary = size[primary] - 1
- val maxOfSecondary = size[secondary] - 1
- val maxOfTertiary = size[tertiary] - 1
-
- val isPrimaryIncreasing = direction.isIncreasing(primary)
- val isSecondaryIncreasing = direction.isIncreasing(secondary)
- val isTertiaryIncreasing = direction.isIncreasing(tertiary)
-
- iterate(maxOfPrimary, isPrimaryIncreasing) { p ->
- iterate(maxOfSecondary, isSecondaryIncreasing) { s ->
- iterate(maxOfTertiary, isTertiaryIncreasing) { t ->
- yield(order.add(origin, p, s, t))
- }
- }
- }
-
- /*medium.iterationCompleted()*/
- }
-
- }
-
- class Slicing(
- val bottomSectionMaxY: Int,
- val bottomTraverser: RegionTraverser,
- val topTraverser: RegionTraverser,
- val bottomFirst: Boolean = true
- ) : RegionTraverser() {
-
- private fun slice(region: Region, atY: Int): Pair<Region, Region?> {
- if (atY < region.size.y + 1) {
- val bottom = Region(region.origin, region.size.withY(atY + 1))
- val top = Region(region.origin.withY(atY + 1), region.size.addY(-atY - 1))
- return bottom to top
- }
- return region to null
- }
-
- override suspend fun Scope.build(region: Region/*, medium: TraverserMedium*/) {
- val (bottom, top) = slice(region, bottomSectionMaxY)
-
- if (bottomFirst) {
- with(bottomTraverser) { build(bottom) }
- top?.let { with(topTraverser) { build(it) } }
- } else {
- top?.let { with(topTraverser) { build(it) } }
- with(bottomTraverser) { build(bottom) }
- }
-
- /*medium.iterationCompleted()*/
- }
- }
-
- /**
- * Returns [Directional] instance that would be responsible for
- * emitting the given position if it is contained in a region.
- * [Directional] instance has a set order and direction
- */
- fun childForPosition(position: Vec3i): Directional {
- var cur = this
- while (true) {
- when (cur) {
- /*is ParcelTraverser -> cur = cur.delegate*/
- is Directional -> return cur
- is Slicing ->
- cur =
- if (position.y <= cur.bottomSectionMaxY) cur.bottomTraverser
- else cur.topTraverser
- }
- }
- }
-
- /**
- * Returns true if and only if this traverser would visit the given
- * [block] position before the given [current] position.
- * If at least one of [block] and [current] is not contained in a
- * region being traversed the result is undefined.
- */
- fun comesFirst(current: Vec3i, block: Vec3i): Boolean {
- var cur = this
- while (true) {
- when (cur) {
- /*is ParcelTraverser -> cur = cur.delegate*/
- is Directional -> return cur.direction.comesFirst(current, block)
- is Slicing -> {
- val border = cur.bottomSectionMaxY
- cur = when {
- current.y <= border && block.y <= border -> cur.bottomTraverser
- current.y <= border -> return !cur.bottomFirst
- block.y <= border -> return cur.bottomFirst
- else -> cur.topTraverser
- }
- }
- }
- }
- }
-
-}
-
-object TraverseOrderFactory {
- private fun isSwap(primary: Dimension, secondary: Dimension) = secondary.ordinal != (primary.ordinal + 1) % 3
-
- fun createWith(primary: Dimension, secondary: Dimension): TraverseOrder {
- // tertiary is implicit
- if (primary == secondary) throw IllegalArgumentException()
- return TraverseOrder(primary, isSwap(primary, secondary))
- }
-}
-
-inline class TraverseOrder(val orderNum: Int) {
- constructor(first: Dimension, swap: Boolean)
- : this(if (swap) first.ordinal + 3 else first.ordinal)
-
- @Suppress("NOTHING_TO_INLINE")
- private inline fun element(index: Int) = Dimension[(orderNum + index) % 3]
-
- private val swap inline get() = orderNum >= 3
-
- /**
- * The slowest changing dimension
- */
- val primary: Dimension get() = element(0)
-
- /**
- * Second slowest changing dimension
- */
- val secondary: Dimension get() = element(if (swap) 2 else 1)
-
- /**
- * Dimension that changes every block
- */
- val tertiary: Dimension get() = element(if (swap) 1 else 2)
-
- /**
- * All 3 dimensions in this order
- */
- fun toArray() = arrayOf(primary, secondary, tertiary)
-
- fun add(vec: Vec3i, p: Int, s: Int, t: Int): Vec3i =
- // optimize this, will be called lots
- when (orderNum) {
- 0 -> vec.add(p, s, t) // xyz
- 1 -> vec.add(t, p, s) // yzx
- 2 -> vec.add(s, t, p) // zxy
- 3 -> vec.add(p, t, s) // xzy
- 4 -> vec.add(s, p, t) // yxz
- 5 -> vec.add(t, s, p) // zyx
- else -> error("Invalid orderNum $orderNum")
- }
-}
-
-inline class TraverseDirection(val bits: Int) {
- fun isIncreasing(dimension: Dimension) = (1 shl dimension.ordinal) and bits != 0
-
- fun comesFirst(current: Vec3i, block: Vec3i, dimension: Dimension): Boolean =
- if (isIncreasing(dimension))
- block[dimension] <= current[dimension]
- else
- block[dimension] >= current[dimension]
-
- fun comesFirst(current: Vec3i, block: Vec3i) =
- comesFirst(current, block, Dimension.X)
- && comesFirst(current, block, Dimension.Y)
- && comesFirst(current, block, Dimension.Z)
-
- companion object {
- operator fun invoke(x: Int, y: Int, z: Int) = invoke(Vec3i(x, y, z))
-
- operator fun invoke(block: Vec3i): TraverseDirection {
- if (block.x == 0 || block.y == 0 || block.z == 0) throw IllegalArgumentException()
- var bits = 0
- if (block.x > 0) bits = bits or 1
- if (block.y > 0) bits = bits or 2
- if (block.z > 0) bits = bits or 4
- return TraverseDirection(bits)
- }
- }
-
-}
+package io.dico.parcels2.blockvisitor
+
+import io.dico.parcels2.util.math.Dimension
+import io.dico.parcels2.util.math.Region
+import io.dico.parcels2.util.math.Vec3i
+import io.dico.parcels2.util.math.clampMax
+
+private typealias Scope = SequenceScope<Vec3i>
+/*
+class ParcelTraverser(
+ val parcelProvider: ParcelProvider,
+ val delegate: RegionTraverser,
+ scope: CoroutineScope
+) : RegionTraverser(), CoroutineScope by scope {
+
+ class OccupiedException(parcelId: ParcelId) : Exception("Parcel $parcelId is occupied")
+
+ /**
+ * Traverse the blocks of parcel's land
+ * The iterator must be exhausted, else the permit to traverse it will not be reclaimed.
+ *
+ * @throws OccupiedException if a parcel is maintained with the given parcel id and an
+ * iterator exists for it that has not been exhausted
+ */
+ fun traverseParcel(parcelId: ParcelId): Iterator<Vec3i> {
+ val world = parcelProvider.getWorldById(parcelId.worldId)
+ ?: throw IllegalArgumentException()
+ val parcel = parcelProvider.getParcelById(parcelId)
+
+ val medium = if (parcel != null) {
+ if (parcel.hasBlockVisitors || parcel !is ParcelImpl) {
+ throw OccupiedException(parcelId)
+ }
+ parcel.hasBlockVisitors = true
+ TraverserMedium { parcel.hasBlockVisitors = false }
+ } else {
+ TraverserMedium.DoNothing
+ }
+
+ val region = world.blockManager.getRegion(parcelId)
+ return traverseRegion(region, world.world.maxHeight, medium)
+ }
+
+ override suspend fun Scope.build(region: Region, medium: TraverserMedium) {
+ with(delegate) {
+ return build(region, medium)
+ }
+ }
+
+}
+
+@Suppress("FunctionName")
+inline fun TraverserMedium(crossinline whenComplete: () -> Unit) =
+ object : TraverserMedium {
+ override fun iterationCompleted() {
+ whenComplete()
+ }
+ }
+
+/**
+ * An object that is able to communicate with an iterator returned by [RegionTraverser]
+ *
+ */
+interface TraverserMedium {
+
+ /**
+ * Called by the traverser during first [Iterator.hasNext] call that returns false
+ */
+ fun iterationCompleted()
+
+ /**
+ * The default [TraverserMedium], which does nothing.
+ */
+ object DoNothing : TraverserMedium {
+ override fun iterationCompleted() {}
+ }
+}*/
+
+sealed class RegionTraverser {
+
+ /**
+ * Get an iterator traversing [region] using this traverser.
+ * Depending on the implementation, [region] might be traversed in a specific order and direction.
+ */
+ fun traverseRegion(
+ region: Region,
+ worldHeight: Int = 256/*,
+ medium: TraverserMedium = TraverserMedium.DoNothing*/
+ ): Iterator<Vec3i> = iterator { build(validify(region, worldHeight)/*, medium*/) }
+
+ abstract suspend fun Scope.build(region: Region/*, medium: TraverserMedium = TraverserMedium.DoNothing*/)
+
+ companion object {
+ val upward = Directional(TraverseDirection(1, 1, 1), TraverseOrderFactory.createWith(Dimension.Y, Dimension.X))
+ val downward = Directional(TraverseDirection(1, -1, 1), TraverseOrderFactory.createWith(Dimension.Y, Dimension.X))
+ val toClear get() = downward
+ val toFill get() = upward
+
+ /**
+ * The returned [RegionTraverser] will traverse the regions
+ * * below and including absolute level [y] first, in [upward] direction.
+ * * above absolute level [y] last, in [downward] direction.
+ */
+ fun convergingTo(y: Int) = Slicing(y, upward, downward, true)
+
+ /**
+ * The returned [RegionTraverser] will traverse the regions
+ * * above absolute level [y] first, in [upward] direction.
+ * * below and including absolute level [y] second, in [downward] direction.
+ */
+ fun separatingFrom(y: Int) = Slicing(y, downward, upward, false)
+
+ private fun validify(region: Region, worldHeight: Int): Region {
+ if (region.origin.y < 0) {
+ val origin = region.origin withY 0
+ val size = region.size.withY((region.size.y + region.origin.y).clampMax(worldHeight))
+ return Region(origin, size)
+ }
+
+ if (region.origin.y + region.size.y > worldHeight) {
+ val size = region.size.withY(worldHeight - region.origin.y)
+ return Region(region.origin, size)
+ }
+
+ return region
+ }
+
+ }
+
+ class Directional(
+ val direction: TraverseDirection,
+ val order: TraverseOrder
+ ) : RegionTraverser() {
+
+ private inline fun iterate(max: Int, increasing: Boolean, action: (Int) -> Unit) {
+ for (i in 0..max) {
+ action(if (increasing) i else max - i)
+ }
+ }
+
+ override suspend fun Scope.build(region: Region/*, medium: TraverserMedium*/) {
+ val order = order
+ val (primary, secondary, tertiary) = order.toArray()
+ val (origin, size) = region
+
+ val maxOfPrimary = size[primary] - 1
+ val maxOfSecondary = size[secondary] - 1
+ val maxOfTertiary = size[tertiary] - 1
+
+ val isPrimaryIncreasing = direction.isIncreasing(primary)
+ val isSecondaryIncreasing = direction.isIncreasing(secondary)
+ val isTertiaryIncreasing = direction.isIncreasing(tertiary)
+
+ iterate(maxOfPrimary, isPrimaryIncreasing) { p ->
+ iterate(maxOfSecondary, isSecondaryIncreasing) { s ->
+ iterate(maxOfTertiary, isTertiaryIncreasing) { t ->
+ yield(order.add(origin, p, s, t))
+ }
+ }
+ }
+
+ /*medium.iterationCompleted()*/
+ }
+
+ }
+
+ class Slicing(
+ val bottomSectionMaxY: Int,
+ val bottomTraverser: RegionTraverser,
+ val topTraverser: RegionTraverser,
+ val bottomFirst: Boolean = true
+ ) : RegionTraverser() {
+
+ private fun slice(region: Region, atY: Int): Pair<Region, Region?> {
+ if (atY < region.size.y + 1) {
+ val bottom = Region(region.origin, region.size.withY(atY + 1))
+ val top = Region(region.origin.withY(atY + 1), region.size.addY(-atY - 1))
+ return bottom to top
+ }
+ return region to null
+ }
+
+ override suspend fun Scope.build(region: Region/*, medium: TraverserMedium*/) {
+ val (bottom, top) = slice(region, bottomSectionMaxY)
+
+ if (bottomFirst) {
+ with(bottomTraverser) { build(bottom) }
+ top?.let { with(topTraverser) { build(it) } }
+ } else {
+ top?.let { with(topTraverser) { build(it) } }
+ with(bottomTraverser) { build(bottom) }
+ }
+
+ /*medium.iterationCompleted()*/
+ }
+ }
+
+ /**
+ * Returns [Directional] instance that would be responsible for
+ * emitting the given position if it is contained in a region.
+ * [Directional] instance has a set order and direction
+ */
+ fun childForPosition(position: Vec3i): Directional {
+ var cur = this
+ while (true) {
+ when (cur) {
+ /*is ParcelTraverser -> cur = cur.delegate*/
+ is Directional -> return cur
+ is Slicing ->
+ cur =
+ if (position.y <= cur.bottomSectionMaxY) cur.bottomTraverser
+ else cur.topTraverser
+ }
+ }
+ }
+
+ /**
+ * Returns true if and only if this traverser would visit the given
+ * [block] position before the given [current] position.
+ * If at least one of [block] and [current] is not contained in a
+ * region being traversed the result is undefined.
+ */
+ fun comesFirst(current: Vec3i, block: Vec3i): Boolean {
+ var cur = this
+ while (true) {
+ when (cur) {
+ /*is ParcelTraverser -> cur = cur.delegate*/
+ is Directional -> return cur.direction.comesFirst(current, block)
+ is Slicing -> {
+ val border = cur.bottomSectionMaxY
+ cur = when {
+ current.y <= border && block.y <= border -> cur.bottomTraverser
+ current.y <= border -> return !cur.bottomFirst
+ block.y <= border -> return cur.bottomFirst
+ else -> cur.topTraverser
+ }
+ }
+ }
+ }
+ }
+
+}
+
+object TraverseOrderFactory {
+ private fun isSwap(primary: Dimension, secondary: Dimension) = secondary.ordinal != (primary.ordinal + 1) % 3
+
+ fun createWith(primary: Dimension, secondary: Dimension): TraverseOrder {
+ // tertiary is implicit
+ if (primary == secondary) throw IllegalArgumentException()
+ return TraverseOrder(primary, isSwap(primary, secondary))
+ }
+}
+
+inline class TraverseOrder(val orderNum: Int) {
+ constructor(first: Dimension, swap: Boolean)
+ : this(if (swap) first.ordinal + 3 else first.ordinal)
+
+ @Suppress("NOTHING_TO_INLINE")
+ private inline fun element(index: Int) = Dimension[(orderNum + index) % 3]
+
+ private val swap inline get() = orderNum >= 3
+
+ /**
+ * The slowest changing dimension
+ */
+ val primary: Dimension get() = element(0)
+
+ /**
+ * Second slowest changing dimension
+ */
+ val secondary: Dimension get() = element(if (swap) 2 else 1)
+
+ /**
+ * Dimension that changes every block
+ */
+ val tertiary: Dimension get() = element(if (swap) 1 else 2)
+
+ /**
+ * All 3 dimensions in this order
+ */
+ fun toArray() = arrayOf(primary, secondary, tertiary)
+
+ fun add(vec: Vec3i, p: Int, s: Int, t: Int): Vec3i =
+ // optimize this, will be called lots
+ when (orderNum) {
+ 0 -> vec.add(p, s, t) // xyz
+ 1 -> vec.add(t, p, s) // yzx
+ 2 -> vec.add(s, t, p) // zxy
+ 3 -> vec.add(p, t, s) // xzy
+ 4 -> vec.add(s, p, t) // yxz
+ 5 -> vec.add(t, s, p) // zyx
+ else -> error("Invalid orderNum $orderNum")
+ }
+}
+
+inline class TraverseDirection(val bits: Int) {
+ fun isIncreasing(dimension: Dimension) = (1 shl dimension.ordinal) and bits != 0
+
+ fun comesFirst(current: Vec3i, block: Vec3i, dimension: Dimension): Boolean =
+ if (isIncreasing(dimension))
+ block[dimension] <= current[dimension]
+ else
+ block[dimension] >= current[dimension]
+
+ fun comesFirst(current: Vec3i, block: Vec3i) =
+ comesFirst(current, block, Dimension.X)
+ && comesFirst(current, block, Dimension.Y)
+ && comesFirst(current, block, Dimension.Z)
+
+ companion object {
+ operator fun invoke(x: Int, y: Int, z: Int) = invoke(Vec3i(x, y, z))
+
+ operator fun invoke(block: Vec3i): TraverseDirection {
+ if (block.x == 0 || block.y == 0 || block.z == 0) throw IllegalArgumentException()
+ var bits = 0
+ if (block.x > 0) bits = bits or 1
+ if (block.y > 0) bits = bits or 2
+ if (block.z > 0) bits = bits or 4
+ return TraverseDirection(bits)
+ }
+ }
+
+}
diff --git a/src/main/kotlin/io/dico/parcels2/blockvisitor/Schematic.kt b/src/main/kotlin/io/dico/parcels2/blockvisitor/Schematic.kt
index df3cfab..39b4345 100644
--- a/src/main/kotlin/io/dico/parcels2/blockvisitor/Schematic.kt
+++ b/src/main/kotlin/io/dico/parcels2/blockvisitor/Schematic.kt
@@ -1,121 +1,121 @@
-package io.dico.parcels2.blockvisitor
-
-import io.dico.parcels2.JobFunction
-import io.dico.parcels2.JobScope
-import io.dico.parcels2.util.math.Region
-import io.dico.parcels2.util.math.Vec3i
-import io.dico.parcels2.util.math.get
-import org.bukkit.Bukkit
-import org.bukkit.Material
-import org.bukkit.World
-import org.bukkit.block.Sign
-import org.bukkit.block.data.BlockData
-
-private val air = Bukkit.createBlockData(Material.AIR)
-
-class Schematic {
- val size: Vec3i get() = _size!!
- private var _size: Vec3i? = null
- set(value) {
- field?.let { throw IllegalStateException() }
- field = value
- }
-
- private var blockDatas: Array<BlockData?>? = null
- private val extra = mutableListOf<Pair<Vec3i, ExtraBlockChange>>()
- private var isLoaded = false; private set
- private val traverser: RegionTraverser = RegionTraverser.upward
-
- suspend fun JobScope.load(world: World, region: Region) {
- _size = region.size
-
- val data = arrayOfNulls<BlockData>(region.blockCount).also { blockDatas = it }
- val blocks = traverser.traverseRegion(region)
- val total = region.blockCount.toDouble()
-
- loop@ for ((index, vec) in blocks.withIndex()) {
- markSuspensionPoint()
- setProgress(index / total)
-
- val block = world[vec]
- if (block.y > 255) continue
- val blockData = block.blockData
- data[index] = blockData
-
- val extraChange = when (blockData.material) {
- Material.SIGN,
- Material.WALL_SIGN -> SignStateChange(block.state as Sign)
- else -> continue@loop
- }
-
- extra += (vec - region.origin) to extraChange
- }
-
- isLoaded = true
- }
-
- suspend fun JobScope.paste(world: World, position: Vec3i) {
- if (!isLoaded) throw IllegalStateException()
-
- val region = Region(position, _size!!)
- val blocks = traverser.traverseRegion(region, worldHeight = world.maxHeight)
- val blockDatas = blockDatas!!
- var postponed = hashMapOf<Vec3i, BlockData>()
-
- val total = region.blockCount.toDouble()
- var processed = 0
-
- for ((index, vec) in blocks.withIndex()) {
- markSuspensionPoint()
- setProgress(index / total)
-
- val block = world[vec]
- val type = blockDatas[index] ?: air
- if (type !== air && isAttachable(type.material)) {
- val supportingBlock = vec + getSupportingBlock(type)
-
- if (!postponed.containsKey(supportingBlock) && traverser.comesFirst(vec, supportingBlock)) {
- block.blockData = type
- setProgress(++processed / total)
- } else {
- postponed[vec] = type
- }
-
- } else {
- block.blockData = type
- setProgress(++processed / total)
- }
- }
-
- while (!postponed.isEmpty()) {
- markSuspensionPoint()
- val newMap = hashMapOf<Vec3i, BlockData>()
- for ((vec, type) in postponed) {
- val supportingBlock = vec + getSupportingBlock(type)
- if (supportingBlock in postponed && supportingBlock != vec) {
- newMap[vec] = type
- } else {
- world[vec].blockData = type
- setProgress(++processed / total)
- }
- }
- postponed = newMap
- }
-
- // Should be negligible so we don't track progress
- for ((vec, extraChange) in extra) {
- markSuspensionPoint()
- val block = world[position + vec]
- extraChange.update(block)
- }
- }
-
- fun getLoadTask(world: World, region: Region): JobFunction = {
- load(world, region)
- }
-
- fun getPasteTask(world: World, position: Vec3i): JobFunction = {
- paste(world, position)
- }
-
-}
+package io.dico.parcels2.blockvisitor
+
+import io.dico.parcels2.JobFunction
+import io.dico.parcels2.JobScope
+import io.dico.parcels2.util.math.Region
+import io.dico.parcels2.util.math.Vec3i
+import io.dico.parcels2.util.math.get
+import org.bukkit.Bukkit
+import org.bukkit.Material
+import org.bukkit.World
+import org.bukkit.block.Sign
+import org.bukkit.block.data.BlockData
+
+private val air = Bukkit.createBlockData(Material.AIR)
+
+class Schematic {
+ val size: Vec3i get() = _size!!
+ private var _size: Vec3i? = null
+ set(value) {
+ field?.let { throw IllegalStateException() }
+ field = value
+ }
+
+ private var blockDatas: Array<BlockData?>? = null
+ private val extra = mutableListOf<Pair<Vec3i, ExtraBlockChange>>()
+ private var isLoaded = false; private set
+ private val traverser: RegionTraverser = RegionTraverser.upward
+
+ suspend fun JobScope.load(world: World, region: Region) {
+ _size = region.size
+
+ val data = arrayOfNulls<BlockData>(region.blockCount).also { blockDatas = it }
+ val blocks = traverser.traverseRegion(region)
+ val total = region.blockCount.toDouble()
+
+ loop@ for ((index, vec) in blocks.withIndex()) {
+ markSuspensionPoint()
+ setProgress(index / total)
+
+ val block = world[vec]
+ if (block.y > 255) continue
+ val blockData = block.blockData
+ data[index] = blockData
+
+ val extraChange = when (blockData.material) {
+ Material.SIGN,
+ Material.WALL_SIGN -> SignStateChange(block.state as Sign)
+ else -> continue@loop
+ }
+
+ extra += (vec - region.origin) to extraChange
+ }
+
+ isLoaded = true
+ }
+
+ suspend fun JobScope.paste(world: World, position: Vec3i) {
+ if (!isLoaded) throw IllegalStateException()
+
+ val region = Region(position, _size!!)
+ val blocks = traverser.traverseRegion(region, worldHeight = world.maxHeight)
+ val blockDatas = blockDatas!!
+ var postponed = hashMapOf<Vec3i, BlockData>()
+
+ val total = region.blockCount.toDouble()
+ var processed = 0
+
+ for ((index, vec) in blocks.withIndex()) {
+ markSuspensionPoint()
+ setProgress(index / total)
+
+ val block = world[vec]
+ val type = blockDatas[index] ?: air
+ if (type !== air && isAttachable(type.material)) {
+ val supportingBlock = vec + getSupportingBlock(type)
+
+ if (!postponed.containsKey(supportingBlock) && traverser.comesFirst(vec, supportingBlock)) {
+ block.blockData = type
+ setProgress(++processed / total)
+ } else {
+ postponed[vec] = type
+ }
+
+ } else {
+ block.blockData = type
+ setProgress(++processed / total)
+ }
+ }
+
+ while (!postponed.isEmpty()) {
+ markSuspensionPoint()
+ val newMap = hashMapOf<Vec3i, BlockData>()
+ for ((vec, type) in postponed) {
+ val supportingBlock = vec + getSupportingBlock(type)
+ if (supportingBlock in postponed && supportingBlock != vec) {
+ newMap[vec] = type
+ } else {
+ world[vec].blockData = type
+ setProgress(++processed / total)
+ }
+ }
+ postponed = newMap
+ }
+
+ // Should be negligible so we don't track progress
+ for ((vec, extraChange) in extra) {
+ markSuspensionPoint()
+ val block = world[position + vec]
+ extraChange.update(block)
+ }
+ }
+
+ fun getLoadTask(world: World, region: Region): JobFunction = {
+ load(world, region)
+ }
+
+ fun getPasteTask(world: World, position: Vec3i): JobFunction = {
+ paste(world, position)
+ }
+
+}
diff --git a/src/main/kotlin/io/dico/parcels2/command/AbstractParcelCommands.kt b/src/main/kotlin/io/dico/parcels2/command/AbstractParcelCommands.kt
index eaa9f57..32f4299 100644
--- a/src/main/kotlin/io/dico/parcels2/command/AbstractParcelCommands.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/AbstractParcelCommands.kt
@@ -1,64 +1,64 @@
-package io.dico.parcels2.command
-
-import io.dico.dicore.command.*
-import io.dico.dicore.command.registration.reflect.ICommandInterceptor
-import io.dico.dicore.command.registration.reflect.ICommandReceiver
-import io.dico.parcels2.*
-import io.dico.parcels2.PlayerProfile.Real
-import io.dico.parcels2.PlayerProfile.Unresolved
-import io.dico.parcels2.util.ext.hasPermAdminManage
-import io.dico.parcels2.util.ext.parcelLimit
-import org.bukkit.entity.Player
-import org.bukkit.plugin.Plugin
-import java.lang.reflect.Method
-
-abstract class AbstractParcelCommands(val plugin: ParcelsPlugin) : ICommandInterceptor {
-
- override fun getReceiver(context: ExecutionContext, target: Method, cmdName: String): ICommandReceiver {
- return getParcelCommandReceiver(plugin.parcelProvider, context, target, cmdName)
- }
-
- override fun getCoroutineContext(context: ExecutionContext?, target: Method?, cmdName: String?): Any {
- return plugin.coroutineContext
- }
-
- protected fun checkConnected(action: String) {
- if (!plugin.storage.isConnected) err("Parcels cannot $action right now because of a database error")
- }
-
- protected suspend fun checkParcelLimit(player: Player, world: ParcelWorld) {
- if (player.hasPermAdminManage) return
- val numOwnedParcels = plugin.storage.getOwnedParcels(PlayerProfile(player)).await()
- .filter { it.worldId.equals(world.id) }.size
-
- val limit = player.parcelLimit
- if (numOwnedParcels >= limit) {
- err("You have enough plots for now")
- }
- }
-
- protected suspend fun toPrivilegeKey(profile: PlayerProfile): PrivilegeKey = when (profile) {
- is Real -> profile
- is Unresolved -> profile.tryResolveSuspendedly(plugin.storage)
- ?: throw CommandException()
- else -> throw CommandException()
- }
-
- protected fun areYouSureMessage(context: ExecutionContext): String {
- val command = (context.route + context.original).joinToString(" ") + " -sure"
- return "Are you sure? You cannot undo this action!\n" +
- "Run \"/$command\" if you want to go through with this."
- }
-
- protected fun Job.reportProgressUpdates(context: ExecutionContext, action: String): Job =
- onProgressUpdate(1000, 1000) { progress, elapsedTime ->
- val alt = context.getFormat(EMessageType.NUMBER)
- val main = context.getFormat(EMessageType.INFORMATIVE)
- context.sendMessage(
- EMessageType.INFORMATIVE, false, "$action progress: $alt%.02f$main%%, $alt%.2f${main}s elapsed"
- .format(progress * 100, elapsedTime / 1000.0)
- )
- }
-}
-
+package io.dico.parcels2.command
+
+import io.dico.dicore.command.*
+import io.dico.dicore.command.registration.reflect.ICommandInterceptor
+import io.dico.dicore.command.registration.reflect.ICommandReceiver
+import io.dico.parcels2.*
+import io.dico.parcels2.PlayerProfile.Real
+import io.dico.parcels2.PlayerProfile.Unresolved
+import io.dico.parcels2.util.ext.hasPermAdminManage
+import io.dico.parcels2.util.ext.parcelLimit
+import org.bukkit.entity.Player
+import org.bukkit.plugin.Plugin
+import java.lang.reflect.Method
+
+abstract class AbstractParcelCommands(val plugin: ParcelsPlugin) : ICommandInterceptor {
+
+ override fun getReceiver(context: ExecutionContext, target: Method, cmdName: String): ICommandReceiver {
+ return getParcelCommandReceiver(plugin.parcelProvider, context, target, cmdName)
+ }
+
+ override fun getCoroutineContext(context: ExecutionContext?, target: Method?, cmdName: String?): Any {
+ return plugin.coroutineContext
+ }
+
+ protected fun checkConnected(action: String) {
+ if (!plugin.storage.isConnected) err("Parcels cannot $action right now because of a database error")
+ }
+
+ protected suspend fun checkParcelLimit(player: Player, world: ParcelWorld) {
+ if (player.hasPermAdminManage) return
+ val numOwnedParcels = plugin.storage.getOwnedParcels(PlayerProfile(player)).await()
+ .filter { it.worldId.equals(world.id) }.size
+
+ val limit = player.parcelLimit
+ if (numOwnedParcels >= limit) {
+ err("You have enough plots for now")
+ }
+ }
+
+ protected suspend fun toPrivilegeKey(profile: PlayerProfile): PrivilegeKey = when (profile) {
+ is Real -> profile
+ is Unresolved -> profile.tryResolveSuspendedly(plugin.storage)
+ ?: throw CommandException()
+ else -> throw CommandException()
+ }
+
+ protected fun areYouSureMessage(context: ExecutionContext): String {
+ val command = (context.route + context.original).joinToString(" ") + " -sure"
+ return "Are you sure? You cannot undo this action!\n" +
+ "Run \"/$command\" if you want to go through with this."
+ }
+
+ protected fun Job.reportProgressUpdates(context: ExecutionContext, action: String): Job =
+ onProgressUpdate(1000, 1000) { progress, elapsedTime ->
+ val alt = context.getFormat(EMessageType.NUMBER)
+ val main = context.getFormat(EMessageType.INFORMATIVE)
+ context.sendMessage(
+ EMessageType.INFORMATIVE, false, "$action progress: $alt%.02f$main%%, $alt%.2f${main}s elapsed"
+ .format(progress * 100, elapsedTime / 1000.0)
+ )
+ }
+}
+
fun err(message: String): Nothing = throw CommandException(message) \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsAdmin.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsAdmin.kt
index e700d6d..38adc43 100644
--- a/src/main/kotlin/io/dico/parcels2/command/CommandsAdmin.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/CommandsAdmin.kt
@@ -1,95 +1,95 @@
-package io.dico.parcels2.command
-
-import io.dico.dicore.command.CommandException
-import io.dico.dicore.command.ExecutionContext
-import io.dico.dicore.command.Validate
-import io.dico.dicore.command.annotation.Cmd
-import io.dico.dicore.command.annotation.Flag
-import io.dico.parcels2.*
-import io.dico.parcels2.command.ParcelTarget.TargetKind
-import io.dico.parcels2.defaultimpl.DefaultParcelContainer
-import io.dico.parcels2.util.ext.PERM_ADMIN_MANAGE
-
-class CommandsAdmin(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
-
- @Cmd("setowner")
- @RequireParcelPrivilege(Privilege.ADMIN)
- suspend fun ParcelScope.cmdSetowner(@ProfileKind(ProfileKind.ANY) target: PlayerProfile): Any? {
- val profile = target.resolved(plugin.storage, resolveToFake = true)!!
- parcel.owner = profile
-
- val fakeString = if (profile.isFake) " (fake)" else ""
- return "${profile.notNullName}$fakeString is the new owner of (${parcel.id.idString})"
- }
-
- @Cmd("update_all_owner_signs")
- fun cmdUpdateAllOwnerSigns(context: ExecutionContext): Any? {
- Validate.isAuthorized(context.sender, PERM_ADMIN_MANAGE)
- plugin.jobDispatcher.dispatch {
- fun getParcelCount(world: ParcelWorld) = (world.options.axisLimit * 2 + 1).let { it * it }
- val parcelCount = plugin.parcelProvider.worlds.values.sumBy { getParcelCount(it) }.toDouble()
- var processed = 0
- for (world in plugin.parcelProvider.worlds.values) {
- markSuspensionPoint()
-
- val container = world.container as? DefaultParcelContainer
- if (container == null) {
- processed += getParcelCount(world)
- setProgress(processed / parcelCount)
- continue
- }
-
- for (parcel in container.getAllParcels()) {
- parcel.updateOwnerSign(force = true)
- processed++
- setProgress(processed / parcelCount)
- }
- }
- }.reportProgressUpdates(context, "Updating")
- return null
- }
-
- @Cmd("dispose")
- @RequireParcelPrivilege(Privilege.ADMIN)
- fun ParcelScope.cmdDispose(): Any? {
- parcel.dispose()
- return "Data of (${parcel.id.idString}) has been disposed"
- }
-
- @Cmd("reset")
- @RequireParcelPrivilege(Privilege.ADMIN)
- fun ParcelScope.cmdReset(context: ExecutionContext, @Flag sure: Boolean): Any? {
- Validate.isTrue(!parcel.hasBlockVisitors, "A process is already running in this parcel")
- if (!sure) return areYouSureMessage(context)
-
- parcel.dispose()
- world.blockManager.clearParcel(parcel.id)?.reportProgressUpdates(context, "Reset")
- return "Data of (${parcel.id.idString}) has been disposed"
- }
-
- @Cmd("swap")
- @RequireParcelPrivilege(Privilege.ADMIN)
- fun ParcelScope.cmdSwap(
- context: ExecutionContext,
- @TargetKind(TargetKind.ID) target: ParcelTarget,
- @Flag sure: Boolean
- ): Any? {
- Validate.isTrue(!parcel.hasBlockVisitors, "A process is already running in this parcel")
- if (!sure) return areYouSureMessage(context)
-
- val parcel2 = (target as ParcelTarget.ByID).getParcel()
- ?: throw CommandException("Invalid parcel target")
-
- // Validate.isTrue(parcel2.world == world, "Parcel must be in the same world")
- Validate.isTrue(!parcel2.hasBlockVisitors, "A process is already running in that parcel")
-
- val data = parcel.data
- parcel.copyData(parcel2.data)
- parcel2.copyData(data)
-
- val job = plugin.parcelProvider.swapParcels(parcel.id, parcel2.id)?.reportProgressUpdates(context, "Swap")
- Validate.notNull(job, "A process is already running in some parcel (internal error)")
- return null
- }
-
+package io.dico.parcels2.command
+
+import io.dico.dicore.command.CommandException
+import io.dico.dicore.command.ExecutionContext
+import io.dico.dicore.command.Validate
+import io.dico.dicore.command.annotation.Cmd
+import io.dico.dicore.command.annotation.Flag
+import io.dico.parcels2.*
+import io.dico.parcels2.command.ParcelTarget.TargetKind
+import io.dico.parcels2.defaultimpl.DefaultParcelContainer
+import io.dico.parcels2.util.ext.PERM_ADMIN_MANAGE
+
+class CommandsAdmin(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
+
+ @Cmd("setowner")
+ @RequireParcelPrivilege(Privilege.ADMIN)
+ suspend fun ParcelScope.cmdSetowner(@ProfileKind(ProfileKind.ANY) target: PlayerProfile): Any? {
+ val profile = target.resolved(plugin.storage, resolveToFake = true)!!
+ parcel.owner = profile
+
+ val fakeString = if (profile.isFake) " (fake)" else ""
+ return "${profile.notNullName}$fakeString is the new owner of (${parcel.id.idString})"
+ }
+
+ @Cmd("update_all_owner_signs")
+ fun cmdUpdateAllOwnerSigns(context: ExecutionContext): Any? {
+ Validate.isAuthorized(context.sender, PERM_ADMIN_MANAGE)
+ plugin.jobDispatcher.dispatch {
+ fun getParcelCount(world: ParcelWorld) = (world.options.axisLimit * 2 + 1).let { it * it }
+ val parcelCount = plugin.parcelProvider.worlds.values.sumBy { getParcelCount(it) }.toDouble()
+ var processed = 0
+ for (world in plugin.parcelProvider.worlds.values) {
+ markSuspensionPoint()
+
+ val container = world.container as? DefaultParcelContainer
+ if (container == null) {
+ processed += getParcelCount(world)
+ setProgress(processed / parcelCount)
+ continue
+ }
+
+ for (parcel in container.getAllParcels()) {
+ parcel.updateOwnerSign(force = true)
+ processed++
+ setProgress(processed / parcelCount)
+ }
+ }
+ }.reportProgressUpdates(context, "Updating")
+ return null
+ }
+
+ @Cmd("dispose")
+ @RequireParcelPrivilege(Privilege.ADMIN)
+ fun ParcelScope.cmdDispose(): Any? {
+ parcel.dispose()
+ return "Data of (${parcel.id.idString}) has been disposed"
+ }
+
+ @Cmd("reset")
+ @RequireParcelPrivilege(Privilege.ADMIN)
+ fun ParcelScope.cmdReset(context: ExecutionContext, @Flag sure: Boolean): Any? {
+ Validate.isTrue(!parcel.hasBlockVisitors, "A process is already running in this parcel")
+ if (!sure) return areYouSureMessage(context)
+
+ parcel.dispose()
+ world.blockManager.clearParcel(parcel.id)?.reportProgressUpdates(context, "Reset")
+ return "Data of (${parcel.id.idString}) has been disposed"
+ }
+
+ @Cmd("swap")
+ @RequireParcelPrivilege(Privilege.ADMIN)
+ fun ParcelScope.cmdSwap(
+ context: ExecutionContext,
+ @TargetKind(TargetKind.ID) target: ParcelTarget,
+ @Flag sure: Boolean
+ ): Any? {
+ Validate.isTrue(!parcel.hasBlockVisitors, "A process is already running in this parcel")
+ if (!sure) return areYouSureMessage(context)
+
+ val parcel2 = (target as ParcelTarget.ByID).getParcel()
+ ?: throw CommandException("Invalid parcel target")
+
+ // Validate.isTrue(parcel2.world == world, "Parcel must be in the same world")
+ Validate.isTrue(!parcel2.hasBlockVisitors, "A process is already running in that parcel")
+
+ val data = parcel.data
+ parcel.copyData(parcel2.data)
+ parcel2.copyData(data)
+
+ val job = plugin.parcelProvider.swapParcels(parcel.id, parcel2.id)?.reportProgressUpdates(context, "Swap")
+ Validate.notNull(job, "A process is already running in some parcel (internal error)")
+ return null
+ }
+
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsAdminPrivilegesGlobal.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsAdminPrivilegesGlobal.kt
index cee3e62..a2bd8d1 100644
--- a/src/main/kotlin/io/dico/parcels2/command/CommandsAdminPrivilegesGlobal.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/CommandsAdminPrivilegesGlobal.kt
@@ -1,132 +1,132 @@
-@file:Suppress("NON_EXHAUSTIVE_WHEN")
-
-package io.dico.parcels2.command
-
-import io.dico.dicore.command.ExecutionContext
-import io.dico.dicore.command.Validate
-import io.dico.dicore.command.annotation.Cmd
-import io.dico.dicore.command.annotation.Desc
-import io.dico.parcels2.*
-import io.dico.parcels2.Privilege.BANNED
-import io.dico.parcels2.Privilege.CAN_BUILD
-import io.dico.parcels2.PrivilegeChangeResult.*
-import io.dico.parcels2.defaultimpl.InfoBuilder
-import io.dico.parcels2.util.ext.PERM_ADMIN_MANAGE
-import org.bukkit.OfflinePlayer
-
-class CommandsAdminPrivilegesGlobal(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
- private val data
- inline get() = plugin.globalPrivileges
-
- private fun checkContext(context: ExecutionContext, owner: OfflinePlayer, changing: Boolean = true): OfflinePlayer {
- if (changing) {
- checkConnected("have privileges changed")
- }
- val sender = context.sender
- if (sender !== owner) {
- Validate.isAuthorized(sender, PERM_ADMIN_MANAGE)
- }
- return owner
- }
-
- @Cmd("list", aliases = ["l"])
- @Desc(
- "List globally declared privileges, players you",
- "allowed to build on or banned from all your parcels",
- shortVersion = "lists globally declared privileges"
- )
- fun cmdList(context: ExecutionContext, owner: OfflinePlayer): Any? {
- checkContext(context, owner, changing = false)
- val map = plugin.globalPrivileges[owner]
- Validate.isTrue(map.hasAnyDeclaredPrivileges(), "This user has not declared any global privileges")
-
- return StringBuilder().apply {
- with(InfoBuilder) {
- appendProfilesWithPrivilege("Globally Allowed", map, null, CAN_BUILD)
- appendProfilesWithPrivilege("Globally Banned", map, null, BANNED)
- }
- }.toString()
- }
-
- @Cmd("entrust")
- @Desc(
- "Allows a player to manage globally",
- shortVersion = "allows a player to manage globally"
- )
- suspend fun cmdEntrust(context: ExecutionContext, owner: OfflinePlayer, player: PlayerProfile): Any? =
- when (data[checkContext(context, owner)].allowManage(toPrivilegeKey(player))) {
- FAIL_OWNER -> err("The target cannot be the owner themselves")
- FAIL -> err("${player.name} is already allowed to manage globally")
- SUCCESS -> "${player.name} is now allowed to manage globally"
- }
-
- @Cmd("distrust")
- @Desc(
- "Disallows a player to manage globally,",
- "they will still be able to build",
- shortVersion = "disallows a player to manage globally"
- )
- suspend fun cmdDistrust(context: ExecutionContext, owner: OfflinePlayer, player: PlayerProfile): Any? =
- when (data[checkContext(context, owner)].disallowManage(toPrivilegeKey(player))) {
- FAIL_OWNER -> err("The target cannot be the owner themselves")
- FAIL -> err("${player.name} is not currently allowed to manage globally")
- SUCCESS -> "${player.name} is not allowed to manage globally anymore"
- }
-
- @Cmd("allow", aliases = ["add", "permit"])
- @Desc(
- "Globally allows a player to build on all",
- "the parcels that you own.",
- shortVersion = "globally allows a player to build on your parcels"
- )
- suspend fun cmdAllow(context: ExecutionContext, owner: OfflinePlayer, player: PlayerProfile): Any? =
- when (data[checkContext(context, owner)].allowBuild(toPrivilegeKey(player))) {
- FAIL_OWNER -> err("The target cannot be the owner themselves")
- FAIL -> err("${player.name} is already allowed globally")
- SUCCESS -> "${player.name} is now allowed to build globally"
- }
-
- @Cmd("disallow", aliases = ["remove", "forbid"])
- @Desc(
- "Globally disallows a player to build on",
- "the parcels that you own.",
- "If the player is allowed to build on specific",
- "parcels, they can still build there.",
- shortVersion = "globally disallows a player to build on your parcels"
- )
- suspend fun cmdDisallow(context: ExecutionContext, owner: OfflinePlayer, player: PlayerProfile): Any? =
- when (data[checkContext(context, owner)].disallowBuild(toPrivilegeKey(player))) {
- FAIL_OWNER -> err("The target cannot be the owner themselves")
- FAIL -> err("${player.name} is not currently allowed globally")
- SUCCESS -> "${player.name} is not allowed to build globally anymore"
- }
-
- @Cmd("ban", aliases = ["deny"])
- @Desc(
- "Globally bans a player from all the parcels",
- "that you own, making them unable to enter.",
- shortVersion = "globally bans a player from your parcels"
- )
- suspend fun cmdBan(context: ExecutionContext, owner: OfflinePlayer, player: PlayerProfile): Any? =
- when (data[checkContext(context, owner)].disallowEnter(toPrivilegeKey(player))) {
- FAIL_OWNER -> err("The target cannot be the owner themselves")
- FAIL -> err("${player.name} is already banned globally")
- SUCCESS -> "${player.name} is now banned globally"
- }
-
- @Cmd("unban", aliases = ["undeny"])
- @Desc(
- "Globally unbans a player from all the parcels",
- "that you own, they can enter again.",
- "If the player is banned from specific parcels,",
- "they will still be banned there.",
- shortVersion = "globally unbans a player from your parcels"
- )
- suspend fun cmdUnban(context: ExecutionContext, owner: OfflinePlayer, player: PlayerProfile): Any? =
- when (data[checkContext(context, owner)].allowEnter(toPrivilegeKey(player))) {
- FAIL_OWNER -> err("The target cannot be the owner themselves")
- FAIL -> err("${player.name} is not currently banned globally")
- SUCCESS -> "${player.name} is not banned globally anymore"
- }
-
+@file:Suppress("NON_EXHAUSTIVE_WHEN")
+
+package io.dico.parcels2.command
+
+import io.dico.dicore.command.ExecutionContext
+import io.dico.dicore.command.Validate
+import io.dico.dicore.command.annotation.Cmd
+import io.dico.dicore.command.annotation.Desc
+import io.dico.parcels2.*
+import io.dico.parcels2.Privilege.BANNED
+import io.dico.parcels2.Privilege.CAN_BUILD
+import io.dico.parcels2.PrivilegeChangeResult.*
+import io.dico.parcels2.defaultimpl.InfoBuilder
+import io.dico.parcels2.util.ext.PERM_ADMIN_MANAGE
+import org.bukkit.OfflinePlayer
+
+class CommandsAdminPrivilegesGlobal(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
+ private val data
+ inline get() = plugin.globalPrivileges
+
+ private fun checkContext(context: ExecutionContext, owner: OfflinePlayer, changing: Boolean = true): OfflinePlayer {
+ if (changing) {
+ checkConnected("have privileges changed")
+ }
+ val sender = context.sender
+ if (sender !== owner) {
+ Validate.isAuthorized(sender, PERM_ADMIN_MANAGE)
+ }
+ return owner
+ }
+
+ @Cmd("list", aliases = ["l"])
+ @Desc(
+ "List globally declared privileges, players you",
+ "allowed to build on or banned from all your parcels",
+ shortVersion = "lists globally declared privileges"
+ )
+ fun cmdList(context: ExecutionContext, owner: OfflinePlayer): Any? {
+ checkContext(context, owner, changing = false)
+ val map = plugin.globalPrivileges[owner]
+ Validate.isTrue(map.hasAnyDeclaredPrivileges(), "This user has not declared any global privileges")
+
+ return StringBuilder().apply {
+ with(InfoBuilder) {
+ appendProfilesWithPrivilege("Globally Allowed", map, null, CAN_BUILD)
+ appendProfilesWithPrivilege("Globally Banned", map, null, BANNED)
+ }
+ }.toString()
+ }
+
+ @Cmd("entrust")
+ @Desc(
+ "Allows a player to manage globally",
+ shortVersion = "allows a player to manage globally"
+ )
+ suspend fun cmdEntrust(context: ExecutionContext, owner: OfflinePlayer, player: PlayerProfile): Any? =
+ when (data[checkContext(context, owner)].allowManage(toPrivilegeKey(player))) {
+ FAIL_OWNER -> err("The target cannot be the owner themselves")
+ FAIL -> err("${player.name} is already allowed to manage globally")
+ SUCCESS -> "${player.name} is now allowed to manage globally"
+ }
+
+ @Cmd("distrust")
+ @Desc(
+ "Disallows a player to manage globally,",
+ "they will still be able to build",
+ shortVersion = "disallows a player to manage globally"
+ )
+ suspend fun cmdDistrust(context: ExecutionContext, owner: OfflinePlayer, player: PlayerProfile): Any? =
+ when (data[checkContext(context, owner)].disallowManage(toPrivilegeKey(player))) {
+ FAIL_OWNER -> err("The target cannot be the owner themselves")
+ FAIL -> err("${player.name} is not currently allowed to manage globally")
+ SUCCESS -> "${player.name} is not allowed to manage globally anymore"
+ }
+
+ @Cmd("allow", aliases = ["add", "permit"])
+ @Desc(
+ "Globally allows a player to build on all",
+ "the parcels that you own.",
+ shortVersion = "globally allows a player to build on your parcels"
+ )
+ suspend fun cmdAllow(context: ExecutionContext, owner: OfflinePlayer, player: PlayerProfile): Any? =
+ when (data[checkContext(context, owner)].allowBuild(toPrivilegeKey(player))) {
+ FAIL_OWNER -> err("The target cannot be the owner themselves")
+ FAIL -> err("${player.name} is already allowed globally")
+ SUCCESS -> "${player.name} is now allowed to build globally"
+ }
+
+ @Cmd("disallow", aliases = ["remove", "forbid"])
+ @Desc(
+ "Globally disallows a player to build on",
+ "the parcels that you own.",
+ "If the player is allowed to build on specific",
+ "parcels, they can still build there.",
+ shortVersion = "globally disallows a player to build on your parcels"
+ )
+ suspend fun cmdDisallow(context: ExecutionContext, owner: OfflinePlayer, player: PlayerProfile): Any? =
+ when (data[checkContext(context, owner)].disallowBuild(toPrivilegeKey(player))) {
+ FAIL_OWNER -> err("The target cannot be the owner themselves")
+ FAIL -> err("${player.name} is not currently allowed globally")
+ SUCCESS -> "${player.name} is not allowed to build globally anymore"
+ }
+
+ @Cmd("ban", aliases = ["deny"])
+ @Desc(
+ "Globally bans a player from all the parcels",
+ "that you own, making them unable to enter.",
+ shortVersion = "globally bans a player from your parcels"
+ )
+ suspend fun cmdBan(context: ExecutionContext, owner: OfflinePlayer, player: PlayerProfile): Any? =
+ when (data[checkContext(context, owner)].disallowEnter(toPrivilegeKey(player))) {
+ FAIL_OWNER -> err("The target cannot be the owner themselves")
+ FAIL -> err("${player.name} is already banned globally")
+ SUCCESS -> "${player.name} is now banned globally"
+ }
+
+ @Cmd("unban", aliases = ["undeny"])
+ @Desc(
+ "Globally unbans a player from all the parcels",
+ "that you own, they can enter again.",
+ "If the player is banned from specific parcels,",
+ "they will still be banned there.",
+ shortVersion = "globally unbans a player from your parcels"
+ )
+ suspend fun cmdUnban(context: ExecutionContext, owner: OfflinePlayer, player: PlayerProfile): Any? =
+ when (data[checkContext(context, owner)].allowEnter(toPrivilegeKey(player))) {
+ FAIL_OWNER -> err("The target cannot be the owner themselves")
+ FAIL -> err("${player.name} is not currently banned globally")
+ SUCCESS -> "${player.name} is not banned globally anymore"
+ }
+
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsDebug.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsDebug.kt
index e0bde7d..3f166ad 100644
--- a/src/main/kotlin/io/dico/parcels2/command/CommandsDebug.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/CommandsDebug.kt
@@ -1,162 +1,162 @@
-package io.dico.parcels2.command
-
-import io.dico.dicore.Formatting
-import io.dico.dicore.command.*
-import io.dico.dicore.command.IContextFilter.Priority.PERMISSION
-import io.dico.dicore.command.annotation.Cmd
-import io.dico.dicore.command.annotation.PreprocessArgs
-import io.dico.dicore.command.annotation.RequireParameters
-import io.dico.dicore.command.parameter.ArgumentBuffer
-import io.dico.parcels2.*
-import io.dico.parcels2.blockvisitor.RegionTraverser
-import io.dico.parcels2.util.ext.PERM_ADMIN_MANAGE
-import io.dico.parcels2.util.ext.PERM_BAN_BYPASS
-import io.dico.parcels2.util.ext.PERM_BUILD_ANYWHERE
-import kotlinx.coroutines.launch
-import org.bukkit.Bukkit
-import org.bukkit.Material
-import org.bukkit.block.BlockFace
-import org.bukkit.block.data.Directional
-import org.bukkit.command.CommandSender
-import org.bukkit.entity.Player
-import java.util.Random
-
-class CommandsDebug(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
-
- @Cmd("reloadoptions")
- fun reloadOptions() {
- plugin.loadOptions()
- }
-
- @Cmd("tpworld")
- fun tpWorld(sender: Player, worldName: String): String {
- if (worldName == "list") {
- return Bukkit.getWorlds().joinToString("\n- ", "- ", "")
- }
- val world = Bukkit.getWorld(worldName) ?: err("World $worldName is not loaded")
- sender.teleport(world.spawnLocation)
- return "Teleported you to $worldName spawn"
- }
-
- @Cmd("make_mess")
- @RequireParcelPrivilege(Privilege.OWNER)
- fun ParcelScope.cmdMakeMess(context: ExecutionContext) {
- Validate.isTrue(!parcel.hasBlockVisitors, "A process is already running in this parcel")
-
- val server = plugin.server
- val blockDatas = arrayOf(
- server.createBlockData(Material.BLUE_WOOL),
- server.createBlockData(Material.LIME_WOOL),
- server.createBlockData(Material.GLASS),
- server.createBlockData(Material.STONE_SLAB),
- server.createBlockData(Material.STONE),
- server.createBlockData(Material.QUARTZ_BLOCK),
- server.createBlockData(Material.BROWN_CONCRETE)
- )
- val random = Random()
-
- world.blockManager.tryDoBlockOperation(plugin.parcelProvider, parcel.id, traverser = RegionTraverser.upward) { block ->
- block.blockData = blockDatas[random.nextInt(7)]
- }?.onProgressUpdate(1000, 1000) { progress, elapsedTime ->
- context.sendMessage(
- EMessageType.INFORMATIVE, "Mess progress: %.02f%%, %.2fs elapsed"
- .format(progress * 100, elapsedTime / 1000.0)
- )
- }
- }
-
- @Cmd("directionality", aliases = ["dir"])
- fun cmdDirectionality(sender: Player, context: ExecutionContext, material: Material): Any? {
- val senderLoc = sender.location
- val block = senderLoc.add(senderLoc.direction.setY(0).normalize().multiply(2).toLocation(sender.world)).block
-
- val blockData = Bukkit.createBlockData(material)
- if (blockData is Directional) {
- blockData.facing = BlockFace.SOUTH
- }
-
- block.blockData = blockData
- return if (blockData is Directional) "The block is facing south" else "The block is not directional, however it implements " +
- blockData.javaClass.interfaces!!.contentToString()
- }
-
- @Cmd("jobs")
- fun cmdJobs(): Any? {
- val workers = plugin.jobDispatcher.jobs
- println(workers.map { it.coroutine }.joinToString(separator = "\n"))
- return "Task count: ${workers.size}"
- }
-
- @Cmd("complete_jobs")
- fun cmdCompleteJobs(): Any? = cmdJobs().also {
- plugin.launch { plugin.jobDispatcher.completeAllTasks() }
- }
-
- @Cmd("message")
- @PreprocessArgs
- fun cmdMessage(sender: CommandSender, message: String): Any? {
- // testing @PreprocessArgs which merges "hello there" into a single argument
- sender.sendMessage(Formatting.translate(message))
- return null
- }
-
- @Cmd("hasperm")
- fun cmdHasperm(target: Player, permission: String): Any? {
- return target.hasPermission(permission).toString()
- }
-
- @Cmd("permissions")
- fun cmdPermissions(context: ExecutionContext, of: Player, vararg address: String): Any? {
- val target = context.address.dispatcherForTree.getDeepChild(ArgumentBuffer(address))
- Validate.isTrue(target.depth == address.size && target.hasCommand(), "Not found: /${address.joinToString(separator = " ")}")
- return getPermissionsOf(target).joinToString(separator = "\n") { "$it: ${of.hasPermission(it)}" }
- }
-
- @Cmd("privilege")
- @RequireParameters(1)
- suspend fun ParcelScope.cmdPrivilege(target: PlayerProfile, adminPerm: String?): Any? {
- val key = toPrivilegeKey(target)
-
- val perm = when (adminPerm) {
- "none" -> null
- "build" -> PERM_BUILD_ANYWHERE
- "manage", null -> PERM_ADMIN_MANAGE
- "enter" -> PERM_BAN_BYPASS
- else -> err("adminPerm should be build, manager or enter")
- }
-
- val privilege = if (perm == null) {
- parcel.getStoredPrivilege(key)
- } else {
- if (key is PlayerProfile.Star) err("* can't have permissions")
- parcel.getEffectivePrivilege(key.player!!, perm)
- }
-
- return privilege.toString()
- }
-
- private fun getPermissionsOf(address: ICommandAddress) = getPermissionsOf(address, emptyArray(), mutableListOf())
-
- private fun getPermissionsOf(address: ICommandAddress, path: Array<String>, result: MutableList<String>): List<String> {
- val command = address.command ?: return result
-
- var inherited = false
- for (filter in command.contextFilters) {
- when (filter) {
- is PermissionContextFilter -> {
- if (path.isEmpty()) result.add(filter.permission)
- else if (filter.isInheritable) result.add(filter.getInheritedPermission(path))
- }
- is InheritingContextFilter -> {
- if (filter.priority == PERMISSION && address.hasParent() && !inherited) {
- inherited = true
- getPermissionsOf(address.parent, arrayOf(address.mainKey, *path), result)
- }
- }
- }
- }
-
- return result
- }
-
+package io.dico.parcels2.command
+
+import io.dico.dicore.Formatting
+import io.dico.dicore.command.*
+import io.dico.dicore.command.IContextFilter.Priority.PERMISSION
+import io.dico.dicore.command.annotation.Cmd
+import io.dico.dicore.command.annotation.PreprocessArgs
+import io.dico.dicore.command.annotation.RequireParameters
+import io.dico.dicore.command.parameter.ArgumentBuffer
+import io.dico.parcels2.*
+import io.dico.parcels2.blockvisitor.RegionTraverser
+import io.dico.parcels2.util.ext.PERM_ADMIN_MANAGE
+import io.dico.parcels2.util.ext.PERM_BAN_BYPASS
+import io.dico.parcels2.util.ext.PERM_BUILD_ANYWHERE
+import kotlinx.coroutines.launch
+import org.bukkit.Bukkit
+import org.bukkit.Material
+import org.bukkit.block.BlockFace
+import org.bukkit.block.data.Directional
+import org.bukkit.command.CommandSender
+import org.bukkit.entity.Player
+import java.util.Random
+
+class CommandsDebug(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
+
+ @Cmd("reloadoptions")
+ fun reloadOptions() {
+ plugin.loadOptions()
+ }
+
+ @Cmd("tpworld")
+ fun tpWorld(sender: Player, worldName: String): String {
+ if (worldName == "list") {
+ return Bukkit.getWorlds().joinToString("\n- ", "- ", "")
+ }
+ val world = Bukkit.getWorld(worldName) ?: err("World $worldName is not loaded")
+ sender.teleport(world.spawnLocation)
+ return "Teleported you to $worldName spawn"
+ }
+
+ @Cmd("make_mess")
+ @RequireParcelPrivilege(Privilege.OWNER)
+ fun ParcelScope.cmdMakeMess(context: ExecutionContext) {
+ Validate.isTrue(!parcel.hasBlockVisitors, "A process is already running in this parcel")
+
+ val server = plugin.server
+ val blockDatas = arrayOf(
+ server.createBlockData(Material.BLUE_WOOL),
+ server.createBlockData(Material.LIME_WOOL),
+ server.createBlockData(Material.GLASS),
+ server.createBlockData(Material.STONE_SLAB),
+ server.createBlockData(Material.STONE),
+ server.createBlockData(Material.QUARTZ_BLOCK),
+ server.createBlockData(Material.BROWN_CONCRETE)
+ )
+ val random = Random()
+
+ world.blockManager.tryDoBlockOperation(plugin.parcelProvider, parcel.id, traverser = RegionTraverser.upward) { block ->
+ block.blockData = blockDatas[random.nextInt(7)]
+ }?.onProgressUpdate(1000, 1000) { progress, elapsedTime ->
+ context.sendMessage(
+ EMessageType.INFORMATIVE, "Mess progress: %.02f%%, %.2fs elapsed"
+ .format(progress * 100, elapsedTime / 1000.0)
+ )
+ }
+ }
+
+ @Cmd("directionality", aliases = ["dir"])
+ fun cmdDirectionality(sender: Player, context: ExecutionContext, material: Material): Any? {
+ val senderLoc = sender.location
+ val block = senderLoc.add(senderLoc.direction.setY(0).normalize().multiply(2).toLocation(sender.world)).block
+
+ val blockData = Bukkit.createBlockData(material)
+ if (blockData is Directional) {
+ blockData.facing = BlockFace.SOUTH
+ }
+
+ block.blockData = blockData
+ return if (blockData is Directional) "The block is facing south" else "The block is not directional, however it implements " +
+ blockData.javaClass.interfaces!!.contentToString()
+ }
+
+ @Cmd("jobs")
+ fun cmdJobs(): Any? {
+ val workers = plugin.jobDispatcher.jobs
+ println(workers.map { it.coroutine }.joinToString(separator = "\n"))
+ return "Task count: ${workers.size}"
+ }
+
+ @Cmd("complete_jobs")
+ fun cmdCompleteJobs(): Any? = cmdJobs().also {
+ plugin.launch { plugin.jobDispatcher.completeAllTasks() }
+ }
+
+ @Cmd("message")
+ @PreprocessArgs
+ fun cmdMessage(sender: CommandSender, message: String): Any? {
+ // testing @PreprocessArgs which merges "hello there" into a single argument
+ sender.sendMessage(Formatting.translate(message))
+ return null
+ }
+
+ @Cmd("hasperm")
+ fun cmdHasperm(target: Player, permission: String): Any? {
+ return target.hasPermission(permission).toString()
+ }
+
+ @Cmd("permissions")
+ fun cmdPermissions(context: ExecutionContext, of: Player, vararg address: String): Any? {
+ val target = context.address.dispatcherForTree.getDeepChild(ArgumentBuffer(address))
+ Validate.isTrue(target.depth == address.size && target.hasCommand(), "Not found: /${address.joinToString(separator = " ")}")
+ return getPermissionsOf(target).joinToString(separator = "\n") { "$it: ${of.hasPermission(it)}" }
+ }
+
+ @Cmd("privilege")
+ @RequireParameters(1)
+ suspend fun ParcelScope.cmdPrivilege(target: PlayerProfile, adminPerm: String?): Any? {
+ val key = toPrivilegeKey(target)
+
+ val perm = when (adminPerm) {
+ "none" -> null
+ "build" -> PERM_BUILD_ANYWHERE
+ "manage", null -> PERM_ADMIN_MANAGE
+ "enter" -> PERM_BAN_BYPASS
+ else -> err("adminPerm should be build, manager or enter")
+ }
+
+ val privilege = if (perm == null) {
+ parcel.getStoredPrivilege(key)
+ } else {
+ if (key is PlayerProfile.Star) err("* can't have permissions")
+ parcel.getEffectivePrivilege(key.player!!, perm)
+ }
+
+ return privilege.toString()
+ }
+
+ private fun getPermissionsOf(address: ICommandAddress) = getPermissionsOf(address, emptyArray(), mutableListOf())
+
+ private fun getPermissionsOf(address: ICommandAddress, path: Array<String>, result: MutableList<String>): List<String> {
+ val command = address.command ?: return result
+
+ var inherited = false
+ for (filter in command.contextFilters) {
+ when (filter) {
+ is PermissionContextFilter -> {
+ if (path.isEmpty()) result.add(filter.permission)
+ else if (filter.isInheritable) result.add(filter.getInheritedPermission(path))
+ }
+ is InheritingContextFilter -> {
+ if (filter.priority == PERMISSION && address.hasParent() && !inherited) {
+ inherited = true
+ getPermissionsOf(address.parent, arrayOf(address.mainKey, *path), result)
+ }
+ }
+ }
+ }
+
+ return result
+ }
+
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt
index c918b80..61e8d9e 100644
--- a/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/CommandsGeneral.kt
@@ -1,142 +1,142 @@
-package io.dico.parcels2.command
-
-import io.dico.dicore.command.ExecutionContext
-import io.dico.dicore.command.Validate
-import io.dico.dicore.command.annotation.Cmd
-import io.dico.dicore.command.annotation.Desc
-import io.dico.dicore.command.annotation.Flag
-import io.dico.dicore.command.annotation.RequireParameters
-import io.dico.parcels2.ParcelsPlugin
-import io.dico.parcels2.PlayerProfile
-import io.dico.parcels2.Privilege
-import io.dico.parcels2.command.ParcelTarget.TargetKind
-import io.dico.parcels2.util.ext.hasParcelHomeOthers
-import io.dico.parcels2.util.ext.hasPermAdminManage
-import io.dico.parcels2.util.ext.uuid
-import org.bukkit.block.Biome
-import org.bukkit.entity.Player
-
-class CommandsGeneral(plugin: ParcelsPlugin, parent: SpecialCommandAddress) : AbstractParcelCommands(plugin) {
-
- @Cmd("auto")
- @Desc(
- "Finds the unclaimed parcel nearest to origin,",
- "and gives it to you",
- shortVersion = "sets you up with a fresh, unclaimed parcel"
- )
- suspend fun WorldScope.cmdAuto(player: Player): Any? {
- checkConnected("be claimed")
- checkParcelLimit(player, world)
-
- val parcel = world.nextEmptyParcel()
- ?: err("This world is full, please ask an admin to upsize it")
- parcel.owner = PlayerProfile(uuid = player.uuid)
- player.teleport(parcel.homeLocation)
- return "Enjoy your new parcel!"
- }
-
- @Cmd("info", aliases = ["i"])
- @Desc(
- "Displays general information",
- "about the parcel you're on",
- shortVersion = "displays information about this parcel"
- )
- fun ParcelScope.cmdInfo(player: Player) = parcel.infoString
-
- init {
- parent.addSpeciallyTreatedKeys("home", "h")
- }
-
- @Cmd("home", aliases = ["h"])
- @Desc(
- "Teleports you to your parcels,",
- "unless another player was specified.",
- "You can specify an index number if you have",
- "more than one parcel",
- shortVersion = "teleports you to parcels"
- )
- @RequireParameters(0)
- suspend fun cmdHome(
- player: Player,
- @TargetKind(TargetKind.OWNER_REAL) target: ParcelTarget
- ): Any? {
- return cmdGoto(player, target)
- }
-
- @Cmd("tp", aliases = ["teleport"])
- suspend fun cmdTp(
- player: Player,
- @TargetKind(TargetKind.ID) target: ParcelTarget
- ): Any? {
- return cmdGoto(player, target)
- }
-
- @Cmd("goto")
- suspend fun cmdGoto(
- player: Player,
- @TargetKind(TargetKind.ANY) target: ParcelTarget
- ): Any? {
- if (target is ParcelTarget.ByOwner) {
- target.resolveOwner(plugin.storage)
- if (!target.owner.matches(player) && !player.hasParcelHomeOthers) {
- err("You do not have permission to teleport to other people's parcels")
- }
- }
-
- val match = target.getParcelSuspend(plugin.storage)
- ?: err("The specified parcel could not be matched")
- player.teleport(match.homeLocation)
- return null
- }
-
- @Cmd("goto_fake")
- suspend fun cmdGotoFake(
- player: Player,
- @TargetKind(TargetKind.OWNER_FAKE) target: ParcelTarget
- ): Any? {
- return cmdGoto(player, target)
- }
-
- @Cmd("claim")
- @Desc(
- "If this parcel is unowned, makes you the owner",
- shortVersion = "claims this parcel"
- )
- suspend fun ParcelScope.cmdClaim(player: Player): Any? {
- checkConnected("be claimed")
- parcel.owner.takeIf { !player.hasPermAdminManage }?.let {
- err(if (it.matches(player)) "You already own this parcel" else "This parcel is not available")
- }
-
- checkParcelLimit(player, world)
- parcel.owner = PlayerProfile(player)
- return "Enjoy your new parcel!"
- }
-
- @Cmd("unclaim")
- @Desc("Unclaims this parcel")
- @RequireParcelPrivilege(Privilege.OWNER)
- fun ParcelScope.cmdUnclaim(player: Player): Any? {
- checkConnected("be unclaimed")
- parcel.dispose()
- return "Your parcel has been disposed"
- }
-
- @Cmd("clear")
- @RequireParcelPrivilege(Privilege.OWNER)
- fun ParcelScope.cmdClear(context: ExecutionContext, @Flag sure: Boolean): Any? {
- Validate.isTrue(!parcel.hasBlockVisitors, "A process is already running in this parcel")
- if (!sure) return areYouSureMessage(context)
- world.blockManager.clearParcel(parcel.id)?.reportProgressUpdates(context, "Clear")
- return null
- }
-
- @Cmd("setbiome")
- @RequireParcelPrivilege(Privilege.OWNER)
- fun ParcelScope.cmdSetbiome(context: ExecutionContext, biome: Biome): Any? {
- Validate.isTrue(!parcel.hasBlockVisitors, "A process is already running in this parcel")
- world.blockManager.setBiome(parcel.id, biome)?.reportProgressUpdates(context, "Biome change")
- return null
- }
-
+package io.dico.parcels2.command
+
+import io.dico.dicore.command.ExecutionContext
+import io.dico.dicore.command.Validate
+import io.dico.dicore.command.annotation.Cmd
+import io.dico.dicore.command.annotation.Desc
+import io.dico.dicore.command.annotation.Flag
+import io.dico.dicore.command.annotation.RequireParameters
+import io.dico.parcels2.ParcelsPlugin
+import io.dico.parcels2.PlayerProfile
+import io.dico.parcels2.Privilege
+import io.dico.parcels2.command.ParcelTarget.TargetKind
+import io.dico.parcels2.util.ext.hasParcelHomeOthers
+import io.dico.parcels2.util.ext.hasPermAdminManage
+import io.dico.parcels2.util.ext.uuid
+import org.bukkit.block.Biome
+import org.bukkit.entity.Player
+
+class CommandsGeneral(plugin: ParcelsPlugin, parent: SpecialCommandAddress) : AbstractParcelCommands(plugin) {
+
+ @Cmd("auto")
+ @Desc(
+ "Finds the unclaimed parcel nearest to origin,",
+ "and gives it to you",
+ shortVersion = "sets you up with a fresh, unclaimed parcel"
+ )
+ suspend fun WorldScope.cmdAuto(player: Player): Any? {
+ checkConnected("be claimed")
+ checkParcelLimit(player, world)
+
+ val parcel = world.nextEmptyParcel()
+ ?: err("This world is full, please ask an admin to upsize it")
+ parcel.owner = PlayerProfile(uuid = player.uuid)
+ player.teleport(parcel.homeLocation)
+ return "Enjoy your new parcel!"
+ }
+
+ @Cmd("info", aliases = ["i"])
+ @Desc(
+ "Displays general information",
+ "about the parcel you're on",
+ shortVersion = "displays information about this parcel"
+ )
+ fun ParcelScope.cmdInfo(player: Player) = parcel.infoString
+
+ init {
+ parent.addSpeciallyTreatedKeys("home", "h")
+ }
+
+ @Cmd("home", aliases = ["h"])
+ @Desc(
+ "Teleports you to your parcels,",
+ "unless another player was specified.",
+ "You can specify an index number if you have",
+ "more than one parcel",
+ shortVersion = "teleports you to parcels"
+ )
+ @RequireParameters(0)
+ suspend fun cmdHome(
+ player: Player,
+ @TargetKind(TargetKind.OWNER_REAL) target: ParcelTarget
+ ): Any? {
+ return cmdGoto(player, target)
+ }
+
+ @Cmd("tp", aliases = ["teleport"])
+ suspend fun cmdTp(
+ player: Player,
+ @TargetKind(TargetKind.ID) target: ParcelTarget
+ ): Any? {
+ return cmdGoto(player, target)
+ }
+
+ @Cmd("goto")
+ suspend fun cmdGoto(
+ player: Player,
+ @TargetKind(TargetKind.ANY) target: ParcelTarget
+ ): Any? {
+ if (target is ParcelTarget.ByOwner) {
+ target.resolveOwner(plugin.storage)
+ if (!target.owner.matches(player) && !player.hasParcelHomeOthers) {
+ err("You do not have permission to teleport to other people's parcels")
+ }
+ }
+
+ val match = target.getParcelSuspend(plugin.storage)
+ ?: err("The specified parcel could not be matched")
+ player.teleport(match.homeLocation)
+ return null
+ }
+
+ @Cmd("goto_fake")
+ suspend fun cmdGotoFake(
+ player: Player,
+ @TargetKind(TargetKind.OWNER_FAKE) target: ParcelTarget
+ ): Any? {
+ return cmdGoto(player, target)
+ }
+
+ @Cmd("claim")
+ @Desc(
+ "If this parcel is unowned, makes you the owner",
+ shortVersion = "claims this parcel"
+ )
+ suspend fun ParcelScope.cmdClaim(player: Player): Any? {
+ checkConnected("be claimed")
+ parcel.owner.takeIf { !player.hasPermAdminManage }?.let {
+ err(if (it.matches(player)) "You already own this parcel" else "This parcel is not available")
+ }
+
+ checkParcelLimit(player, world)
+ parcel.owner = PlayerProfile(player)
+ return "Enjoy your new parcel!"
+ }
+
+ @Cmd("unclaim")
+ @Desc("Unclaims this parcel")
+ @RequireParcelPrivilege(Privilege.OWNER)
+ fun ParcelScope.cmdUnclaim(player: Player): Any? {
+ checkConnected("be unclaimed")
+ parcel.dispose()
+ return "Your parcel has been disposed"
+ }
+
+ @Cmd("clear")
+ @RequireParcelPrivilege(Privilege.OWNER)
+ fun ParcelScope.cmdClear(context: ExecutionContext, @Flag sure: Boolean): Any? {
+ Validate.isTrue(!parcel.hasBlockVisitors, "A process is already running in this parcel")
+ if (!sure) return areYouSureMessage(context)
+ world.blockManager.clearParcel(parcel.id)?.reportProgressUpdates(context, "Clear")
+ return null
+ }
+
+ @Cmd("setbiome")
+ @RequireParcelPrivilege(Privilege.OWNER)
+ fun ParcelScope.cmdSetbiome(context: ExecutionContext, biome: Biome): Any? {
+ Validate.isTrue(!parcel.hasBlockVisitors, "A process is already running in this parcel")
+ world.blockManager.setBiome(parcel.id, biome)?.reportProgressUpdates(context, "Biome change")
+ return null
+ }
+
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsPrivilegesGlobal.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsPrivilegesGlobal.kt
index 3b4234c..33ffb8b 100644
--- a/src/main/kotlin/io/dico/parcels2/command/CommandsPrivilegesGlobal.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/CommandsPrivilegesGlobal.kt
@@ -1,77 +1,77 @@
-package io.dico.parcels2.command
-
-import io.dico.dicore.command.ExecutionContext
-import io.dico.dicore.command.annotation.Cmd
-import io.dico.dicore.command.annotation.Desc
-import io.dico.parcels2.ParcelsPlugin
-import io.dico.parcels2.PlayerProfile
-import org.bukkit.entity.Player
-
-class CommandsPrivilegesGlobal(plugin: ParcelsPlugin,
- val adminVersion: CommandsAdminPrivilegesGlobal) : AbstractParcelCommands(plugin) {
- @Cmd("list", aliases = ["l"])
- @Desc(
- "List globally declared privileges, players you",
- "allowed to build on or banned from all your parcels",
- shortVersion = "lists globally declared privileges"
- )
- fun cmdList(sender: Player, context: ExecutionContext) =
- adminVersion.cmdList(context, sender)
-
- @Cmd("entrust")
- @Desc(
- "Allows a player to manage globally",
- shortVersion = "allows a player to manage globally"
- )
- suspend fun cmdEntrust(sender: Player, context: ExecutionContext, player: PlayerProfile) =
- adminVersion.cmdEntrust(context, sender, player)
-
- @Cmd("distrust")
- @Desc(
- "Disallows a player to manage globally,",
- "they will still be able to build",
- shortVersion = "disallows a player to manage globally"
- )
- suspend fun cmdDistrust(sender: Player, context: ExecutionContext, player: PlayerProfile) =
- adminVersion.cmdDistrust(context, sender, player)
-
- @Cmd("allow", aliases = ["add", "permit"])
- @Desc(
- "Globally allows a player to build on all",
- "the parcels that you own.",
- shortVersion = "globally allows a player to build on your parcels"
- )
- suspend fun cmdAllow(sender: Player, context: ExecutionContext, player: PlayerProfile) =
- adminVersion.cmdAllow(context, sender, player)
-
- @Cmd("disallow", aliases = ["remove", "forbid"])
- @Desc(
- "Globally disallows a player to build on",
- "the parcels that you own.",
- "If the player is allowed to build on specific",
- "parcels, they can still build there.",
- shortVersion = "globally disallows a player to build on your parcels"
- )
- suspend fun cmdDisallow(sender: Player, context: ExecutionContext, player: PlayerProfile) =
- adminVersion.cmdDisallow(context, sender, player)
-
- @Cmd("ban", aliases = ["deny"])
- @Desc(
- "Globally bans a player from all the parcels",
- "that you own, making them unable to enter.",
- shortVersion = "globally bans a player from your parcels"
- )
- suspend fun cmdBan(sender: Player, context: ExecutionContext, player: PlayerProfile) =
- adminVersion.cmdBan(context, sender, player)
-
- @Cmd("unban", aliases = ["undeny"])
- @Desc(
- "Globally unbans a player from all the parcels",
- "that you own, they can enter again.",
- "If the player is banned from specific parcels,",
- "they will still be banned there.",
- shortVersion = "globally unbans a player from your parcels"
- )
- suspend fun cmdUnban(sender: Player, context: ExecutionContext, player: PlayerProfile) =
- adminVersion.cmdUnban(context, sender, player)
+package io.dico.parcels2.command
+
+import io.dico.dicore.command.ExecutionContext
+import io.dico.dicore.command.annotation.Cmd
+import io.dico.dicore.command.annotation.Desc
+import io.dico.parcels2.ParcelsPlugin
+import io.dico.parcels2.PlayerProfile
+import org.bukkit.entity.Player
+
+class CommandsPrivilegesGlobal(plugin: ParcelsPlugin,
+ val adminVersion: CommandsAdminPrivilegesGlobal) : AbstractParcelCommands(plugin) {
+ @Cmd("list", aliases = ["l"])
+ @Desc(
+ "List globally declared privileges, players you",
+ "allowed to build on or banned from all your parcels",
+ shortVersion = "lists globally declared privileges"
+ )
+ fun cmdList(sender: Player, context: ExecutionContext) =
+ adminVersion.cmdList(context, sender)
+
+ @Cmd("entrust")
+ @Desc(
+ "Allows a player to manage globally",
+ shortVersion = "allows a player to manage globally"
+ )
+ suspend fun cmdEntrust(sender: Player, context: ExecutionContext, player: PlayerProfile) =
+ adminVersion.cmdEntrust(context, sender, player)
+
+ @Cmd("distrust")
+ @Desc(
+ "Disallows a player to manage globally,",
+ "they will still be able to build",
+ shortVersion = "disallows a player to manage globally"
+ )
+ suspend fun cmdDistrust(sender: Player, context: ExecutionContext, player: PlayerProfile) =
+ adminVersion.cmdDistrust(context, sender, player)
+
+ @Cmd("allow", aliases = ["add", "permit"])
+ @Desc(
+ "Globally allows a player to build on all",
+ "the parcels that you own.",
+ shortVersion = "globally allows a player to build on your parcels"
+ )
+ suspend fun cmdAllow(sender: Player, context: ExecutionContext, player: PlayerProfile) =
+ adminVersion.cmdAllow(context, sender, player)
+
+ @Cmd("disallow", aliases = ["remove", "forbid"])
+ @Desc(
+ "Globally disallows a player to build on",
+ "the parcels that you own.",
+ "If the player is allowed to build on specific",
+ "parcels, they can still build there.",
+ shortVersion = "globally disallows a player to build on your parcels"
+ )
+ suspend fun cmdDisallow(sender: Player, context: ExecutionContext, player: PlayerProfile) =
+ adminVersion.cmdDisallow(context, sender, player)
+
+ @Cmd("ban", aliases = ["deny"])
+ @Desc(
+ "Globally bans a player from all the parcels",
+ "that you own, making them unable to enter.",
+ shortVersion = "globally bans a player from your parcels"
+ )
+ suspend fun cmdBan(sender: Player, context: ExecutionContext, player: PlayerProfile) =
+ adminVersion.cmdBan(context, sender, player)
+
+ @Cmd("unban", aliases = ["undeny"])
+ @Desc(
+ "Globally unbans a player from all the parcels",
+ "that you own, they can enter again.",
+ "If the player is banned from specific parcels,",
+ "they will still be banned there.",
+ shortVersion = "globally unbans a player from your parcels"
+ )
+ suspend fun cmdUnban(sender: Player, context: ExecutionContext, player: PlayerProfile) =
+ adminVersion.cmdUnban(context, sender, player)
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/command/CommandsPrivilegesLocal.kt b/src/main/kotlin/io/dico/parcels2/command/CommandsPrivilegesLocal.kt
index 21910b1..e3aba22 100644
--- a/src/main/kotlin/io/dico/parcels2/command/CommandsPrivilegesLocal.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/CommandsPrivilegesLocal.kt
@@ -1,144 +1,144 @@
-package io.dico.parcels2.command
-
-import io.dico.dicore.command.Validate
-import io.dico.dicore.command.annotation.Cmd
-import io.dico.dicore.command.annotation.Desc
-import io.dico.parcels2.*
-import io.dico.parcels2.PrivilegeChangeResult.*
-import io.dico.parcels2.util.ext.PERM_ADMIN_MANAGE
-import io.dico.parcels2.util.ext.hasPermAdminManage
-import org.bukkit.entity.Player
-
-class CommandsPrivilegesLocal(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
-
- private fun ParcelScope.checkPrivilege(sender: Player, key: PrivilegeKey) {
- val senderPrivilege = parcel.getEffectivePrivilege(sender, PERM_ADMIN_MANAGE)
- val targetPrivilege = parcel.getStoredPrivilege(key)
- Validate.isTrue(senderPrivilege > targetPrivilege, "You may not change the privilege of ${key.notNullName}")
- }
-
- private fun ParcelScope.checkOwned(sender: Player) {
- Validate.isTrue(parcel.owner != null || sender.hasPermAdminManage, "This parcel is unowned")
- }
-
- @Cmd("entrust")
- @Desc(
- "Allows a player to manage this parcel",
- shortVersion = "allows a player to manage this parcel"
- )
- @RequireParcelPrivilege(Privilege.OWNER)
- suspend fun ParcelScope.cmdEntrust(sender: Player, player: PlayerProfile): Any? {
- checkConnected("have privileges changed")
- checkOwned(sender)
-
- val key = toPrivilegeKey(player)
- return when (parcel.allowManage(key)) {
- FAIL_OWNER -> err("The target already owns the parcel")
- FAIL -> err("${player.name} is already allowed to manage this parcel")
- SUCCESS -> "${player.name} is now allowed to manage this parcel"
- }
- }
-
- @Cmd("distrust")
- @Desc(
- "Disallows a player to manage this parcel,",
- "they will still be able to build",
- shortVersion = "disallows a player to manage this parcel"
- )
- @RequireParcelPrivilege(Privilege.OWNER)
- suspend fun ParcelScope.cmdDistrust(sender: Player, player: PlayerProfile): Any? {
- checkConnected("have privileges changed")
- checkOwned(sender)
-
- val key = toPrivilegeKey(player)
- return when (parcel.disallowManage(key)) {
- FAIL_OWNER -> err("The target owns the parcel and can't be distrusted")
- FAIL -> err("${player.name} is not currently allowed to manage this parcel")
- SUCCESS -> "${player.name} is not allowed to manage this parcel anymore"
- }
- }
-
- @Cmd("allow", aliases = ["add", "permit"])
- @Desc(
- "Allows a player to build on this parcel",
- shortVersion = "allows a player to build on this parcel"
- )
- @RequireParcelPrivilege(Privilege.CAN_MANAGE)
- suspend fun ParcelScope.cmdAllow(sender: Player, player: PlayerProfile): Any? {
- checkConnected("have privileges changed")
- checkOwned(sender)
-
- val key = toPrivilegeKey(player)
- checkPrivilege(sender, key)
-
- return when (parcel.allowBuild(key)) {
- FAIL_OWNER -> err("The target already owns the parcel")
- FAIL -> err("${player.name} is already allowed to build on this parcel")
- SUCCESS -> "${player.name} is now allowed to build on this parcel"
- }
- }
-
- @Cmd("disallow", aliases = ["remove", "forbid"])
- @Desc(
- "Disallows a player to build on this parcel,",
- "they won't be allowed to anymore",
- shortVersion = "disallows a player to build on this parcel"
- )
- @RequireParcelPrivilege(Privilege.CAN_MANAGE)
- suspend fun ParcelScope.cmdDisallow(sender: Player, player: PlayerProfile): Any? {
- checkConnected("have privileges changed")
- checkOwned(sender)
-
- val key = toPrivilegeKey(player)
- checkPrivilege(sender, key)
-
- return when (parcel.disallowBuild(key)) {
- FAIL_OWNER -> err("The target owns the parcel")
- FAIL -> err("${player.name} is not currently allowed to build on this parcel")
- SUCCESS -> "${player.name} is not allowed to build on this parcel anymore"
- }
- }
-
- @Cmd("ban", aliases = ["deny"])
- @Desc(
- "Bans a player from this parcel,",
- "making them unable to enter",
- shortVersion = "bans a player from this parcel"
- )
- @RequireParcelPrivilege(Privilege.CAN_MANAGE)
- suspend fun ParcelScope.cmdBan(sender: Player, player: PlayerProfile): Any? {
- checkConnected("have privileges changed")
- checkOwned(sender)
-
- val key = toPrivilegeKey(player)
- checkPrivilege(sender, key)
-
- return when (parcel.disallowEnter(key)) {
- FAIL_OWNER -> err("The target owns the parcel")
- FAIL -> err("${player.name} is already banned from this parcel")
- SUCCESS -> "${player.name} is now banned from this parcel"
- }
- }
-
- @Cmd("unban", aliases = ["undeny"])
- @Desc(
- "Unbans a player from this parcel,",
- "they will be able to enter it again",
- shortVersion = "unbans a player from this parcel"
- )
- @RequireParcelPrivilege(Privilege.CAN_MANAGE)
- suspend fun ParcelScope.cmdUnban(sender: Player, player: PlayerProfile): Any? {
- checkConnected("have privileges changed")
- checkOwned(sender)
-
- val key = toPrivilegeKey(player)
- checkPrivilege(sender, key)
-
- return when (parcel.allowEnter(key)) {
- FAIL_OWNER -> err("The target owns the parcel")
- FAIL -> err("${player.name} is not currently banned from this parcel")
- SUCCESS -> "${player.name} is not banned from this parcel anymore"
- }
- }
-
+package io.dico.parcels2.command
+
+import io.dico.dicore.command.Validate
+import io.dico.dicore.command.annotation.Cmd
+import io.dico.dicore.command.annotation.Desc
+import io.dico.parcels2.*
+import io.dico.parcels2.PrivilegeChangeResult.*
+import io.dico.parcels2.util.ext.PERM_ADMIN_MANAGE
+import io.dico.parcels2.util.ext.hasPermAdminManage
+import org.bukkit.entity.Player
+
+class CommandsPrivilegesLocal(plugin: ParcelsPlugin) : AbstractParcelCommands(plugin) {
+
+ private fun ParcelScope.checkPrivilege(sender: Player, key: PrivilegeKey) {
+ val senderPrivilege = parcel.getEffectivePrivilege(sender, PERM_ADMIN_MANAGE)
+ val targetPrivilege = parcel.getStoredPrivilege(key)
+ Validate.isTrue(senderPrivilege > targetPrivilege, "You may not change the privilege of ${key.notNullName}")
+ }
+
+ private fun ParcelScope.checkOwned(sender: Player) {
+ Validate.isTrue(parcel.owner != null || sender.hasPermAdminManage, "This parcel is unowned")
+ }
+
+ @Cmd("entrust")
+ @Desc(
+ "Allows a player to manage this parcel",
+ shortVersion = "allows a player to manage this parcel"
+ )
+ @RequireParcelPrivilege(Privilege.OWNER)
+ suspend fun ParcelScope.cmdEntrust(sender: Player, player: PlayerProfile): Any? {
+ checkConnected("have privileges changed")
+ checkOwned(sender)
+
+ val key = toPrivilegeKey(player)
+ return when (parcel.allowManage(key)) {
+ FAIL_OWNER -> err("The target already owns the parcel")
+ FAIL -> err("${player.name} is already allowed to manage this parcel")
+ SUCCESS -> "${player.name} is now allowed to manage this parcel"
+ }
+ }
+
+ @Cmd("distrust")
+ @Desc(
+ "Disallows a player to manage this parcel,",
+ "they will still be able to build",
+ shortVersion = "disallows a player to manage this parcel"
+ )
+ @RequireParcelPrivilege(Privilege.OWNER)
+ suspend fun ParcelScope.cmdDistrust(sender: Player, player: PlayerProfile): Any? {
+ checkConnected("have privileges changed")
+ checkOwned(sender)
+
+ val key = toPrivilegeKey(player)
+ return when (parcel.disallowManage(key)) {
+ FAIL_OWNER -> err("The target owns the parcel and can't be distrusted")
+ FAIL -> err("${player.name} is not currently allowed to manage this parcel")
+ SUCCESS -> "${player.name} is not allowed to manage this parcel anymore"
+ }
+ }
+
+ @Cmd("allow", aliases = ["add", "permit"])
+ @Desc(
+ "Allows a player to build on this parcel",
+ shortVersion = "allows a player to build on this parcel"
+ )
+ @RequireParcelPrivilege(Privilege.CAN_MANAGE)
+ suspend fun ParcelScope.cmdAllow(sender: Player, player: PlayerProfile): Any? {
+ checkConnected("have privileges changed")
+ checkOwned(sender)
+
+ val key = toPrivilegeKey(player)
+ checkPrivilege(sender, key)
+
+ return when (parcel.allowBuild(key)) {
+ FAIL_OWNER -> err("The target already owns the parcel")
+ FAIL -> err("${player.name} is already allowed to build on this parcel")
+ SUCCESS -> "${player.name} is now allowed to build on this parcel"
+ }
+ }
+
+ @Cmd("disallow", aliases = ["remove", "forbid"])
+ @Desc(
+ "Disallows a player to build on this parcel,",
+ "they won't be allowed to anymore",
+ shortVersion = "disallows a player to build on this parcel"
+ )
+ @RequireParcelPrivilege(Privilege.CAN_MANAGE)
+ suspend fun ParcelScope.cmdDisallow(sender: Player, player: PlayerProfile): Any? {
+ checkConnected("have privileges changed")
+ checkOwned(sender)
+
+ val key = toPrivilegeKey(player)
+ checkPrivilege(sender, key)
+
+ return when (parcel.disallowBuild(key)) {
+ FAIL_OWNER -> err("The target owns the parcel")
+ FAIL -> err("${player.name} is not currently allowed to build on this parcel")
+ SUCCESS -> "${player.name} is not allowed to build on this parcel anymore"
+ }
+ }
+
+ @Cmd("ban", aliases = ["deny"])
+ @Desc(
+ "Bans a player from this parcel,",
+ "making them unable to enter",
+ shortVersion = "bans a player from this parcel"
+ )
+ @RequireParcelPrivilege(Privilege.CAN_MANAGE)
+ suspend fun ParcelScope.cmdBan(sender: Player, player: PlayerProfile): Any? {
+ checkConnected("have privileges changed")
+ checkOwned(sender)
+
+ val key = toPrivilegeKey(player)
+ checkPrivilege(sender, key)
+
+ return when (parcel.disallowEnter(key)) {
+ FAIL_OWNER -> err("The target owns the parcel")
+ FAIL -> err("${player.name} is already banned from this parcel")
+ SUCCESS -> "${player.name} is now banned from this parcel"
+ }
+ }
+
+ @Cmd("unban", aliases = ["undeny"])
+ @Desc(
+ "Unbans a player from this parcel,",
+ "they will be able to enter it again",
+ shortVersion = "unbans a player from this parcel"
+ )
+ @RequireParcelPrivilege(Privilege.CAN_MANAGE)
+ suspend fun ParcelScope.cmdUnban(sender: Player, player: PlayerProfile): Any? {
+ checkConnected("have privileges changed")
+ checkOwned(sender)
+
+ val key = toPrivilegeKey(player)
+ checkPrivilege(sender, key)
+
+ return when (parcel.allowEnter(key)) {
+ FAIL_OWNER -> err("The target owns the parcel")
+ FAIL -> err("${player.name} is not currently banned from this parcel")
+ SUCCESS -> "${player.name} is not banned from this parcel anymore"
+ }
+ }
+
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/command/ParcelCommandBuilder.kt b/src/main/kotlin/io/dico/parcels2/command/ParcelCommandBuilder.kt
index 721ce2d..155f4f1 100644
--- a/src/main/kotlin/io/dico/parcels2/command/ParcelCommandBuilder.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/ParcelCommandBuilder.kt
@@ -1,137 +1,137 @@
-package io.dico.parcels2.command
-
-import io.dico.dicore.command.*
-import io.dico.dicore.command.parameter.ArgumentBuffer
-import io.dico.dicore.command.predef.DefaultGroupCommand
-import io.dico.dicore.command.registration.reflect.ReflectiveRegistration
-import io.dico.parcels2.Interactables
-import io.dico.parcels2.ParcelsPlugin
-import io.dico.parcels2.logger
-import io.dico.parcels2.util.ext.hasPermAdminManage
-import org.bukkit.command.CommandSender
-import java.util.LinkedList
-import java.util.Queue
-
-@Suppress("UsePropertyAccessSyntax")
-fun getParcelCommands(plugin: ParcelsPlugin): ICommandDispatcher = CommandBuilder().apply {
- val parcelsAddress = SpecialCommandAddress()
-
- setChatHandler(ParcelsChatHandler())
- addParameterType(false, ParcelParameterType(plugin.parcelProvider))
- addParameterType(false, ProfileParameterType())
- addParameterType(true, ParcelTarget.PType(plugin.parcelProvider, parcelsAddress))
-
- group(parcelsAddress, "parcel", "plot", "plots", "p") {
- addContextFilter(IContextFilter.inheritablePermission("parcels.command"))
- registerCommands(CommandsGeneral(plugin, parcelsAddress))
- registerCommands(CommandsPrivilegesLocal(plugin))
-
- group("option", "opt", "o") {
- setGroupDescription(
- "changes interaction options for this parcel",
- "Sets whether players who are not allowed to",
- "build here can interact with certain things."
- )
-
- group("interact", "i") {
- val command = ParcelOptionsInteractCommand(plugin)
- Interactables.classesById.forEach {
- addSubCommand(it.name, command)
- }
- }
- }
-
- val adminPrivilegesGlobal = CommandsAdminPrivilegesGlobal(plugin)
-
- group("global", "g") {
- registerCommands(CommandsPrivilegesGlobal(plugin, adminVersion = adminPrivilegesGlobal))
- }
-
- group("admin", "a") {
- setCommand(AdminGroupCommand())
- registerCommands(CommandsAdmin(plugin))
-
- group("global", "g") {
- registerCommands(adminPrivilegesGlobal)
- }
- }
-
- if (!logger.isDebugEnabled) return@group
-
- group("debug", "d") {
- registerCommands(CommandsDebug(plugin))
- }
- }
-
- generateHelpAndSyntaxCommands(parcelsAddress)
-}.getDispatcher()
-
-private inline fun CommandBuilder.group(name: String, vararg aliases: String, config: CommandBuilder.() -> Unit) {
- group(name, *aliases)
- config()
- parent()
-}
-
-private inline fun CommandBuilder.group(address: ICommandAddress, name: String, vararg aliases: String, config: CommandBuilder.() -> Unit) {
- group(address, name, *aliases)
- config()
- parent()
-}
-
-private fun CommandBuilder.generateHelpAndSyntaxCommands(root: ICommandAddress): CommandBuilder {
- generateCommands(root, "help", "syntax")
- return this
-}
-
-private fun generateCommands(address: ICommandAddress, vararg names: String) {
- val addresses: Queue<ICommandAddress> = LinkedList()
- addresses.offer(address)
-
- while (addresses.isNotEmpty()) {
- val cur = addresses.poll()
- cur.childrenMainKeys.mapTo(addresses) { cur.getChild(it) }
- if (cur.hasCommand()) {
- ReflectiveRegistration.generateCommands(cur, names)
- }
- }
-}
-
-class SpecialCommandAddress : ChildCommandAddress() {
- private val speciallyTreatedKeys = mutableListOf<String>()
-
- // Used to allow /p h:1 syntax, which is the same as what PlotMe uses.
- var speciallyParsedIndex: Int? = null; private set
-
- fun addSpeciallyTreatedKeys(vararg keys: String) {
- for (key in keys) {
- speciallyTreatedKeys.add(key + ":")
- }
- }
-
- // h:1
- @Throws(CommandException::class)
- override fun getChild(context: ExecutionContext, buffer: ArgumentBuffer): ChildCommandAddress? {
- speciallyParsedIndex = null
-
- val key = buffer.next() ?: return null
- for (specialKey in speciallyTreatedKeys) {
- if (key.startsWith(specialKey)) {
- val result = getChild(specialKey.substring(0, specialKey.length - 1))
- ?: return null
-
- val text = key.substring(specialKey.length)
- val num = text.toIntOrNull() ?: throw CommandException("$text is not a number")
- speciallyParsedIndex = num
-
- return result
- }
- }
-
- return super.getChild(key)
- }
-
-}
-
-private class AdminGroupCommand : DefaultGroupCommand() {
- override fun isVisibleTo(sender: CommandSender) = sender.hasPermAdminManage
-}
+package io.dico.parcels2.command
+
+import io.dico.dicore.command.*
+import io.dico.dicore.command.parameter.ArgumentBuffer
+import io.dico.dicore.command.predef.DefaultGroupCommand
+import io.dico.dicore.command.registration.reflect.ReflectiveRegistration
+import io.dico.parcels2.Interactables
+import io.dico.parcels2.ParcelsPlugin
+import io.dico.parcels2.logger
+import io.dico.parcels2.util.ext.hasPermAdminManage
+import org.bukkit.command.CommandSender
+import java.util.LinkedList
+import java.util.Queue
+
+@Suppress("UsePropertyAccessSyntax")
+fun getParcelCommands(plugin: ParcelsPlugin): ICommandDispatcher = CommandBuilder().apply {
+ val parcelsAddress = SpecialCommandAddress()
+
+ setChatHandler(ParcelsChatHandler())
+ addParameterType(false, ParcelParameterType(plugin.parcelProvider))
+ addParameterType(false, ProfileParameterType())
+ addParameterType(true, ParcelTarget.PType(plugin.parcelProvider, parcelsAddress))
+
+ group(parcelsAddress, "parcel", "plot", "plots", "p") {
+ addContextFilter(IContextFilter.inheritablePermission("parcels.command"))
+ registerCommands(CommandsGeneral(plugin, parcelsAddress))
+ registerCommands(CommandsPrivilegesLocal(plugin))
+
+ group("option", "opt", "o") {
+ setGroupDescription(
+ "changes interaction options for this parcel",
+ "Sets whether players who are not allowed to",
+ "build here can interact with certain things."
+ )
+
+ group("interact", "i") {
+ val command = ParcelOptionsInteractCommand(plugin)
+ Interactables.classesById.forEach {
+ addSubCommand(it.name, command)
+ }
+ }
+ }
+
+ val adminPrivilegesGlobal = CommandsAdminPrivilegesGlobal(plugin)
+
+ group("global", "g") {
+ registerCommands(CommandsPrivilegesGlobal(plugin, adminVersion = adminPrivilegesGlobal))
+ }
+
+ group("admin", "a") {
+ setCommand(AdminGroupCommand())
+ registerCommands(CommandsAdmin(plugin))
+
+ group("global", "g") {
+ registerCommands(adminPrivilegesGlobal)
+ }
+ }
+
+ if (!logger.isDebugEnabled) return@group
+
+ group("debug", "d") {
+ registerCommands(CommandsDebug(plugin))
+ }
+ }
+
+ generateHelpAndSyntaxCommands(parcelsAddress)
+}.getDispatcher()
+
+private inline fun CommandBuilder.group(name: String, vararg aliases: String, config: CommandBuilder.() -> Unit) {
+ group(name, *aliases)
+ config()
+ parent()
+}
+
+private inline fun CommandBuilder.group(address: ICommandAddress, name: String, vararg aliases: String, config: CommandBuilder.() -> Unit) {
+ group(address, name, *aliases)
+ config()
+ parent()
+}
+
+private fun CommandBuilder.generateHelpAndSyntaxCommands(root: ICommandAddress): CommandBuilder {
+ generateCommands(root, "help", "syntax")
+ return this
+}
+
+private fun generateCommands(address: ICommandAddress, vararg names: String) {
+ val addresses: Queue<ICommandAddress> = LinkedList()
+ addresses.offer(address)
+
+ while (addresses.isNotEmpty()) {
+ val cur = addresses.poll()
+ cur.childrenMainKeys.mapTo(addresses) { cur.getChild(it) }
+ if (cur.hasCommand()) {
+ ReflectiveRegistration.generateCommands(cur, names)
+ }
+ }
+}
+
+class SpecialCommandAddress : ChildCommandAddress() {
+ private val speciallyTreatedKeys = mutableListOf<String>()
+
+ // Used to allow /p h:1 syntax, which is the same as what PlotMe uses.
+ var speciallyParsedIndex: Int? = null; private set
+
+ fun addSpeciallyTreatedKeys(vararg keys: String) {
+ for (key in keys) {
+ speciallyTreatedKeys.add(key + ":")
+ }
+ }
+
+ // h:1
+ @Throws(CommandException::class)
+ override fun getChild(context: ExecutionContext, buffer: ArgumentBuffer): ChildCommandAddress? {
+ speciallyParsedIndex = null
+
+ val key = buffer.next() ?: return null
+ for (specialKey in speciallyTreatedKeys) {
+ if (key.startsWith(specialKey)) {
+ val result = getChild(specialKey.substring(0, specialKey.length - 1))
+ ?: return null
+
+ val text = key.substring(specialKey.length)
+ val num = text.toIntOrNull() ?: throw CommandException("$text is not a number")
+ speciallyParsedIndex = num
+
+ return result
+ }
+ }
+
+ return super.getChild(key)
+ }
+
+}
+
+private class AdminGroupCommand : DefaultGroupCommand() {
+ override fun isVisibleTo(sender: CommandSender) = sender.hasPermAdminManage
+}
diff --git a/src/main/kotlin/io/dico/parcels2/command/ParcelCommandReceivers.kt b/src/main/kotlin/io/dico/parcels2/command/ParcelCommandReceivers.kt
index 15548b4..e41fd37 100644
--- a/src/main/kotlin/io/dico/parcels2/command/ParcelCommandReceivers.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/ParcelCommandReceivers.kt
@@ -1,68 +1,68 @@
-package io.dico.parcels2.command
-
-import io.dico.dicore.command.CommandException
-import io.dico.dicore.command.ExecutionContext
-import io.dico.dicore.command.registration.reflect.ICommandReceiver
-import io.dico.dicore.command.Validate
-import io.dico.parcels2.*
-import io.dico.parcels2.Privilege.*
-import io.dico.parcels2.util.ext.hasPermAdminManage
-import io.dico.parcels2.util.ext.uuid
-import org.bukkit.entity.Player
-import java.lang.reflect.Method
-import kotlin.reflect.full.extensionReceiverParameter
-import kotlin.reflect.full.findAnnotation
-import kotlin.reflect.jvm.jvmErasure
-import kotlin.reflect.jvm.kotlinFunction
-
-@Target(AnnotationTarget.FUNCTION)
-@Retention(AnnotationRetention.RUNTIME)
-annotation class RequireParcelPrivilege(val privilege: Privilege)
-
-/*
-@Target(AnnotationTarget.FUNCTION)
-@Retention(AnnotationRetention.RUNTIME)
-annotation class SuspensionTimeout(val millis: Int)
-*/
-
-open class WorldScope(val world: ParcelWorld) : ICommandReceiver
-open class ParcelScope(val parcel: Parcel) : WorldScope(parcel.world) {
- fun checkCanManage(player: Player, action: String) = Validate.isTrue(parcel.canManage(player), "You must own this parcel to $action")
-}
-
-fun getParcelCommandReceiver(parcelProvider: ParcelProvider, context: ExecutionContext, method: Method, cmdName: String): ICommandReceiver {
- val player = context.sender as Player
- val function = method.kotlinFunction!!
- val receiverType = function.extensionReceiverParameter!!.type
- val require = function.findAnnotation<RequireParcelPrivilege>()
-
- return when (receiverType.jvmErasure) {
- ParcelScope::class -> ParcelScope(parcelProvider.getParcelRequired(player, require?.privilege))
- WorldScope::class -> WorldScope(parcelProvider.getWorldRequired(player, require?.privilege == ADMIN))
- else -> throw InternalError("Invalid command receiver type")
- }
-}
-
-fun ParcelProvider.getWorldRequired(player: Player, admin: Boolean = false): ParcelWorld {
- if (admin) Validate.isTrue(player.hasPermAdminManage, "You must have admin rights to use that command")
- return getWorld(player.world)
- ?: throw CommandException("You must be in a parcel world to use that command")
-}
-
-fun ParcelProvider.getParcelRequired(player: Player, privilege: Privilege? = null): Parcel {
- val parcel = getWorldRequired(player, admin = privilege == ADMIN).getParcelAt(player)
- ?: throw CommandException("You must be in a parcel to use that command")
-
- if (!player.hasPermAdminManage) {
- @Suppress("NON_EXHAUSTIVE_WHEN")
- when (privilege) {
- OWNER ->
- Validate.isTrue(parcel.isOwner(player.uuid), "You must own this parcel to use that command")
- CAN_MANAGE ->
- Validate.isTrue(parcel.canManage(player), "You must have management privileges on this parcel to use that command")
- }
- }
-
- return parcel
-}
-
+package io.dico.parcels2.command
+
+import io.dico.dicore.command.CommandException
+import io.dico.dicore.command.ExecutionContext
+import io.dico.dicore.command.registration.reflect.ICommandReceiver
+import io.dico.dicore.command.Validate
+import io.dico.parcels2.*
+import io.dico.parcels2.Privilege.*
+import io.dico.parcels2.util.ext.hasPermAdminManage
+import io.dico.parcels2.util.ext.uuid
+import org.bukkit.entity.Player
+import java.lang.reflect.Method
+import kotlin.reflect.full.extensionReceiverParameter
+import kotlin.reflect.full.findAnnotation
+import kotlin.reflect.jvm.jvmErasure
+import kotlin.reflect.jvm.kotlinFunction
+
+@Target(AnnotationTarget.FUNCTION)
+@Retention(AnnotationRetention.RUNTIME)
+annotation class RequireParcelPrivilege(val privilege: Privilege)
+
+/*
+@Target(AnnotationTarget.FUNCTION)
+@Retention(AnnotationRetention.RUNTIME)
+annotation class SuspensionTimeout(val millis: Int)
+*/
+
+open class WorldScope(val world: ParcelWorld) : ICommandReceiver
+open class ParcelScope(val parcel: Parcel) : WorldScope(parcel.world) {
+ fun checkCanManage(player: Player, action: String) = Validate.isTrue(parcel.canManage(player), "You must own this parcel to $action")
+}
+
+fun getParcelCommandReceiver(parcelProvider: ParcelProvider, context: ExecutionContext, method: Method, cmdName: String): ICommandReceiver {
+ val player = context.sender as Player
+ val function = method.kotlinFunction!!
+ val receiverType = function.extensionReceiverParameter!!.type
+ val require = function.findAnnotation<RequireParcelPrivilege>()
+
+ return when (receiverType.jvmErasure) {
+ ParcelScope::class -> ParcelScope(parcelProvider.getParcelRequired(player, require?.privilege))
+ WorldScope::class -> WorldScope(parcelProvider.getWorldRequired(player, require?.privilege == ADMIN))
+ else -> throw InternalError("Invalid command receiver type")
+ }
+}
+
+fun ParcelProvider.getWorldRequired(player: Player, admin: Boolean = false): ParcelWorld {
+ if (admin) Validate.isTrue(player.hasPermAdminManage, "You must have admin rights to use that command")
+ return getWorld(player.world)
+ ?: throw CommandException("You must be in a parcel world to use that command")
+}
+
+fun ParcelProvider.getParcelRequired(player: Player, privilege: Privilege? = null): Parcel {
+ val parcel = getWorldRequired(player, admin = privilege == ADMIN).getParcelAt(player)
+ ?: throw CommandException("You must be in a parcel to use that command")
+
+ if (!player.hasPermAdminManage) {
+ @Suppress("NON_EXHAUSTIVE_WHEN")
+ when (privilege) {
+ OWNER ->
+ Validate.isTrue(parcel.isOwner(player.uuid), "You must own this parcel to use that command")
+ CAN_MANAGE ->
+ Validate.isTrue(parcel.canManage(player), "You must have management privileges on this parcel to use that command")
+ }
+ }
+
+ return parcel
+}
+
diff --git a/src/main/kotlin/io/dico/parcels2/command/ParcelOptionsInteractCommand.kt b/src/main/kotlin/io/dico/parcels2/command/ParcelOptionsInteractCommand.kt
index 23087dc..fbe4333 100644
--- a/src/main/kotlin/io/dico/parcels2/command/ParcelOptionsInteractCommand.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/ParcelOptionsInteractCommand.kt
@@ -1,56 +1,56 @@
-package io.dico.parcels2.command
-
-import io.dico.dicore.command.*
-import io.dico.dicore.command.parameter.type.ParameterTypes
-import io.dico.parcels2.Interactables
-import io.dico.parcels2.ParcelsPlugin
-import io.dico.parcels2.Privilege
-import org.bukkit.command.CommandSender
-import org.bukkit.entity.Player
-
-class ParcelOptionsInteractCommand(val plugin: ParcelsPlugin) : Command() {
-
- init {
- setShortDescription("View and/or change the setting")
- setDescription(shortDescription)
- addContextFilter(IContextFilter.PLAYER_ONLY)
- addContextFilter(IContextFilter.INHERIT_PERMISSIONS)
- addParameter("allowed", "new setting", ParameterTypes.BOOLEAN)
- requiredParameters(0)
- }
-
- override fun execute(sender: CommandSender, context: ExecutionContext): String? {
- if (!plugin.storage.isConnected) err("Parcels cannot have their options changed right now because of a database error")
-
- val interactableClass = Interactables[context.address.mainKey]
- val allowed: Boolean? = context.get("allowed")
-
- val parcel = plugin.parcelProvider.getParcelRequired(sender as Player,
- if (allowed == null) Privilege.DEFAULT else Privilege.CAN_MANAGE)
-
- if (allowed == null) {
- val setting = parcel.interactableConfig.isInteractable(interactableClass)
- val default = setting == interactableClass.interactableByDefault
-
- val canColor = context.address.chatHandler.getChatFormatForType(EMessageType.BAD_NEWS)
- val cannotColor = context.address.chatHandler.getChatFormatForType(EMessageType.GOOD_NEWS)
- val resetColor = context.address.chatHandler.getChatFormatForType(EMessageType.RESULT)
-
- val settingString = (if (setting) "${canColor}can" else "${cannotColor}cannot") + resetColor
- val defaultString = if (default) " (default)" else ""
-
- return "Players $settingString interact with ${interactableClass.name} on this parcel$defaultString"
- }
-
- val change = parcel.interactableConfig.setInteractable(interactableClass, allowed)
-
- val interactableClassName = interactableClass.name
- return when {
- allowed && change -> "Other players can now interact with $interactableClassName"
- allowed && !change -> err("Other players could already interact with $interactableClassName")
- change -> "Other players can not interact with $interactableClassName anymore"
- else -> err("Other players were not allowed to interact with $interactableClassName")
- }
- }
-
-}
+package io.dico.parcels2.command
+
+import io.dico.dicore.command.*
+import io.dico.dicore.command.parameter.type.ParameterTypes
+import io.dico.parcels2.Interactables
+import io.dico.parcels2.ParcelsPlugin
+import io.dico.parcels2.Privilege
+import org.bukkit.command.CommandSender
+import org.bukkit.entity.Player
+
+class ParcelOptionsInteractCommand(val plugin: ParcelsPlugin) : Command() {
+
+ init {
+ setShortDescription("View and/or change the setting")
+ setDescription(shortDescription)
+ addContextFilter(IContextFilter.PLAYER_ONLY)
+ addContextFilter(IContextFilter.INHERIT_PERMISSIONS)
+ addParameter("allowed", "new setting", ParameterTypes.BOOLEAN)
+ requiredParameters(0)
+ }
+
+ override fun execute(sender: CommandSender, context: ExecutionContext): String? {
+ if (!plugin.storage.isConnected) err("Parcels cannot have their options changed right now because of a database error")
+
+ val interactableClass = Interactables[context.address.mainKey]
+ val allowed: Boolean? = context.get("allowed")
+
+ val parcel = plugin.parcelProvider.getParcelRequired(sender as Player,
+ if (allowed == null) Privilege.DEFAULT else Privilege.CAN_MANAGE)
+
+ if (allowed == null) {
+ val setting = parcel.interactableConfig.isInteractable(interactableClass)
+ val default = setting == interactableClass.interactableByDefault
+
+ val canColor = context.address.chatHandler.getChatFormatForType(EMessageType.BAD_NEWS)
+ val cannotColor = context.address.chatHandler.getChatFormatForType(EMessageType.GOOD_NEWS)
+ val resetColor = context.address.chatHandler.getChatFormatForType(EMessageType.RESULT)
+
+ val settingString = (if (setting) "${canColor}can" else "${cannotColor}cannot") + resetColor
+ val defaultString = if (default) " (default)" else ""
+
+ return "Players $settingString interact with ${interactableClass.name} on this parcel$defaultString"
+ }
+
+ val change = parcel.interactableConfig.setInteractable(interactableClass, allowed)
+
+ val interactableClassName = interactableClass.name
+ return when {
+ allowed && change -> "Other players can now interact with $interactableClassName"
+ allowed && !change -> err("Other players could already interact with $interactableClassName")
+ change -> "Other players can not interact with $interactableClassName anymore"
+ else -> err("Other players were not allowed to interact with $interactableClassName")
+ }
+ }
+
+}
diff --git a/src/main/kotlin/io/dico/parcels2/command/ParcelParameterTypes.kt b/src/main/kotlin/io/dico/parcels2/command/ParcelParameterTypes.kt
index c7083a1..730625e 100644
--- a/src/main/kotlin/io/dico/parcels2/command/ParcelParameterTypes.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/ParcelParameterTypes.kt
@@ -1,83 +1,83 @@
-package io.dico.parcels2.command
-
-import io.dico.dicore.command.CommandException
-import io.dico.dicore.command.parameter.ArgumentBuffer
-import io.dico.dicore.command.parameter.Parameter
-import io.dico.dicore.command.parameter.type.ParameterConfig
-import io.dico.dicore.command.parameter.type.ParameterType
-import io.dico.parcels2.*
-import io.dico.parcels2.command.ProfileKind.Companion.ANY
-import io.dico.parcels2.command.ProfileKind.Companion.FAKE
-import io.dico.parcels2.command.ProfileKind.Companion.REAL
-import org.bukkit.Location
-import org.bukkit.command.CommandSender
-import org.bukkit.entity.Player
-
-fun invalidInput(parameter: Parameter<*, *>, message: String): Nothing {
- throw CommandException("invalid input for ${parameter.name}: $message")
-}
-
-fun ParcelProvider.getTargetWorld(input: String?, sender: CommandSender, parameter: Parameter<*, *>): ParcelWorld {
- val worldName = input
- ?.takeUnless { it.isEmpty() }
- ?: (sender as? Player)?.world?.name
- ?: invalidInput(parameter, "console cannot omit the world name")
-
- return getWorld(worldName)
- ?: invalidInput(parameter, "$worldName is not a parcel world")
-}
-
-class ParcelParameterType(val parcelProvider: ParcelProvider) : ParameterType<Parcel, Void>(Parcel::class.java) {
- val regex = Regex.fromLiteral("((.+)->)?([0-9]+):([0-9]+)")
-
- override fun parse(parameter: Parameter<Parcel, Void>, sender: CommandSender, buffer: ArgumentBuffer): Parcel {
- val matchResult = regex.matchEntire(buffer.next()!!)
- ?: invalidInput(parameter, "must match (w->)?a:b (/${regex.pattern}/)")
-
- val world = parcelProvider.getTargetWorld(matchResult.groupValues[2], sender, parameter)
-
- val x = matchResult.groupValues[3].toIntOrNull()
- ?: invalidInput(parameter, "couldn't parse int")
-
- val z = matchResult.groupValues[4].toIntOrNull()
- ?: invalidInput(parameter, "couldn't parse int")
-
- return world.getParcelById(x, z)
- ?: invalidInput(parameter, "parcel id is out of range")
- }
-
-}
-
-annotation class ProfileKind(val kind: Int) {
- companion object : ParameterConfig<ProfileKind, Int>(ProfileKind::class.java) {
- const val REAL = 1
- const val FAKE = 2
- const val ANY = REAL or FAKE
-
- override fun toParameterInfo(annotation: ProfileKind): Int {
- return annotation.kind
- }
- }
-}
-
-class ProfileParameterType : ParameterType<PlayerProfile, Int>(PlayerProfile::class.java, ProfileKind) {
-
- override fun parse(parameter: Parameter<PlayerProfile, Int>, sender: CommandSender, buffer: ArgumentBuffer): PlayerProfile {
- val info = parameter.paramInfo ?: REAL
- val allowReal = (info and REAL) != 0
- val allowFake = (info and FAKE) != 0
-
- val input = buffer.next()!!
- return PlayerProfile.byName(input, allowReal, allowFake)
- }
-
- override fun complete(
- parameter: Parameter<PlayerProfile, Int>,
- sender: CommandSender,
- location: Location?,
- buffer: ArgumentBuffer
- ): MutableList<String> {
- logger.info("Completing PlayerProfile: ${buffer.next()}")
- return super.complete(parameter, sender, location, buffer)
- }
-}
+package io.dico.parcels2.command
+
+import io.dico.dicore.command.CommandException
+import io.dico.dicore.command.parameter.ArgumentBuffer
+import io.dico.dicore.command.parameter.Parameter
+import io.dico.dicore.command.parameter.type.ParameterConfig
+import io.dico.dicore.command.parameter.type.ParameterType
+import io.dico.parcels2.*
+import io.dico.parcels2.command.ProfileKind.Companion.ANY
+import io.dico.parcels2.command.ProfileKind.Companion.FAKE
+import io.dico.parcels2.command.ProfileKind.Companion.REAL
+import org.bukkit.Location
+import org.bukkit.command.CommandSender
+import org.bukkit.entity.Player
+
+fun invalidInput(parameter: Parameter<*, *>, message: String): Nothing {
+ throw CommandException("invalid input for ${parameter.name}: $message")
+}
+
+fun ParcelProvider.getTargetWorld(input: String?, sender: CommandSender, parameter: Parameter<*, *>): ParcelWorld {
+ val worldName = input
+ ?.takeUnless { it.isEmpty() }
+ ?: (sender as? Player)?.world?.name
+ ?: invalidInput(parameter, "console cannot omit the world name")
+
+ return getWorld(worldName)
+ ?: invalidInput(parameter, "$worldName is not a parcel world")
+}
+
+class ParcelParameterType(val parcelProvider: ParcelProvider) : ParameterType<Parcel, Void>(Parcel::class.java) {
+ val regex = Regex.fromLiteral("((.+)->)?([0-9]+):([0-9]+)")
+
+ override fun parse(parameter: Parameter<Parcel, Void>, sender: CommandSender, buffer: ArgumentBuffer): Parcel {
+ val matchResult = regex.matchEntire(buffer.next()!!)
+ ?: invalidInput(parameter, "must match (w->)?a:b (/${regex.pattern}/)")
+
+ val world = parcelProvider.getTargetWorld(matchResult.groupValues[2], sender, parameter)
+
+ val x = matchResult.groupValues[3].toIntOrNull()
+ ?: invalidInput(parameter, "couldn't parse int")
+
+ val z = matchResult.groupValues[4].toIntOrNull()
+ ?: invalidInput(parameter, "couldn't parse int")
+
+ return world.getParcelById(x, z)
+ ?: invalidInput(parameter, "parcel id is out of range")
+ }
+
+}
+
+annotation class ProfileKind(val kind: Int) {
+ companion object : ParameterConfig<ProfileKind, Int>(ProfileKind::class.java) {
+ const val REAL = 1
+ const val FAKE = 2
+ const val ANY = REAL or FAKE
+
+ override fun toParameterInfo(annotation: ProfileKind): Int {
+ return annotation.kind
+ }
+ }
+}
+
+class ProfileParameterType : ParameterType<PlayerProfile, Int>(PlayerProfile::class.java, ProfileKind) {
+
+ override fun parse(parameter: Parameter<PlayerProfile, Int>, sender: CommandSender, buffer: ArgumentBuffer): PlayerProfile {
+ val info = parameter.paramInfo ?: REAL
+ val allowReal = (info and REAL) != 0
+ val allowFake = (info and FAKE) != 0
+
+ val input = buffer.next()!!
+ return PlayerProfile.byName(input, allowReal, allowFake)
+ }
+
+ override fun complete(
+ parameter: Parameter<PlayerProfile, Int>,
+ sender: CommandSender,
+ location: Location?,
+ buffer: ArgumentBuffer
+ ): MutableList<String> {
+ logger.info("Completing PlayerProfile: ${buffer.next()}")
+ return super.complete(parameter, sender, location, buffer)
+ }
+}
diff --git a/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt b/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt
index 8891912..c39c4b6 100644
--- a/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/ParcelTarget.kt
@@ -1,191 +1,191 @@
-package io.dico.parcels2.command
-
-import io.dico.dicore.command.parameter.ArgumentBuffer
-import io.dico.dicore.command.parameter.Parameter
-import io.dico.dicore.command.parameter.type.ParameterConfig
-import io.dico.dicore.command.parameter.type.ParameterType
-import io.dico.parcels2.Parcel
-import io.dico.parcels2.ParcelProvider
-import io.dico.parcels2.ParcelWorld
-import io.dico.parcels2.PlayerProfile
-import io.dico.parcels2.command.ParcelTarget.TargetKind.Companion.DEFAULT_KIND
-import io.dico.parcels2.command.ParcelTarget.TargetKind.Companion.ID
-import io.dico.parcels2.command.ParcelTarget.TargetKind.Companion.OWNER
-import io.dico.parcels2.command.ParcelTarget.TargetKind.Companion.OWNER_FAKE
-import io.dico.parcels2.command.ParcelTarget.TargetKind.Companion.OWNER_REAL
-import io.dico.parcels2.command.ParcelTarget.TargetKind.Companion.PREFER_OWNED_FOR_DEFAULT
-import io.dico.parcels2.command.ParcelTarget.TargetKind.Companion.REAL
-import io.dico.parcels2.storage.Storage
-import io.dico.parcels2.util.math.Vec2i
-import io.dico.parcels2.util.math.floor
-import org.bukkit.command.CommandSender
-import org.bukkit.entity.Player
-
-sealed class ParcelTarget(val world: ParcelWorld, val parsedKind: Int, val isDefault: Boolean) {
-
- abstract suspend fun getParcelSuspend(storage: Storage): Parcel?
-
- class ByID(world: ParcelWorld, val id: Vec2i?, parsedKind: Int, isDefault: Boolean) : ParcelTarget(world, parsedKind, isDefault) {
- override suspend fun getParcelSuspend(storage: Storage): Parcel? = getParcel()
- fun getParcel() = id?.let { world.getParcelById(it) }
- val isPath: Boolean get() = id == null
- }
-
- class ByOwner(
- world: ParcelWorld,
- owner: PlayerProfile,
- val index: Int,
- parsedKind: Int,
- isDefault: Boolean,
- val onResolveFailure: (() -> Unit)? = null
- ) : ParcelTarget(world, parsedKind, isDefault) {
- init {
- if (index < 0) throw IllegalArgumentException("Invalid parcel home index: $index")
- }
-
- var owner = owner; private set
-
- suspend fun resolveOwner(storage: Storage): Boolean {
- val owner = owner
- if (owner is PlayerProfile.Unresolved) {
- this.owner = owner.tryResolveSuspendedly(storage) ?: if (parsedKind and OWNER_FAKE != 0) PlayerProfile.Fake(owner.name)
- else run { onResolveFailure?.invoke(); return false }
- }
- return true
- }
-
- override suspend fun getParcelSuspend(storage: Storage): Parcel? {
- onResolveFailure?.let { resolveOwner(storage) }
-
- val ownedParcelsSerialized = storage.getOwnedParcels(owner).await()
- val ownedParcels = ownedParcelsSerialized
- .filter { it.worldId.equals(world.id) }
- .map { world.getParcelById(it.x, it.z) }
-
- return ownedParcels.getOrNull(index)
- }
- }
-
- annotation class TargetKind(val kind: Int) {
- companion object : ParameterConfig<TargetKind, Int>(TargetKind::class.java) {
- const val ID = 1 // ID
- const val OWNER_REAL = 2 // an owner backed by a UUID
- const val OWNER_FAKE = 4 // an owner not backed by a UUID
-
- const val OWNER = OWNER_REAL or OWNER_FAKE // any owner
- const val ANY = ID or OWNER_REAL or OWNER_FAKE // any
- const val REAL = ID or OWNER_REAL // no owner not backed by a UUID
-
- const val DEFAULT_KIND = REAL
-
- const val PREFER_OWNED_FOR_DEFAULT = 8 // if the kind can be ID and OWNER_REAL, prefer OWNER_REAL for default
- // instead of parcel that the player is in
-
- override fun toParameterInfo(annotation: TargetKind): Int {
- return annotation.kind
- }
- }
- }
-
- class PType(val parcelProvider: ParcelProvider, val parcelAddress: SpecialCommandAddress? = null) :
- ParameterType<ParcelTarget, Int>(ParcelTarget::class.java, TargetKind) {
-
- override fun parse(parameter: Parameter<ParcelTarget, Int>, sender: CommandSender, buffer: ArgumentBuffer): ParcelTarget {
- var input = buffer.next()!!
- val worldString = input.substringBefore("/", missingDelimiterValue = "")
- input = input.substringAfter("/")
-
- val world = if (worldString.isEmpty()) {
- val player = requirePlayer(sender, parameter, "the world")
- parcelProvider.getWorld(player.world)
- ?: invalidInput(parameter, "You cannot omit the world if you're not in a parcel world")
- } else {
- parcelProvider.getWorld(worldString) ?: invalidInput(parameter, "$worldString is not a parcel world")
- }
-
- val kind = parameter.paramInfo ?: DEFAULT_KIND
- if (input.contains(',')) {
- if (kind and ID == 0) invalidInput(parameter, "You must specify a parcel by OWNER, that is, an owner and index")
- return ByID(world, getId(parameter, input), kind, false)
- }
-
- if (kind and OWNER == 0) invalidInput(parameter, "You must specify a parcel by ID, that is, the x and z component separated by a comma")
- val (owner, index) = getHomeIndex(parameter, kind, sender, input)
- return ByOwner(world, owner, index, kind, false, onResolveFailure = { invalidInput(parameter, "The player $input does not exist") })
- }
-
- private fun getId(parameter: Parameter<*, *>, input: String): Vec2i {
- val x = input.substringBefore(',').run {
- toIntOrNull() ?: invalidInput(parameter, "ID(x) must be an integer, $this is not an integer")
- }
- val z = input.substringAfter(',').run {
- toIntOrNull() ?: invalidInput(parameter, "ID(z) must be an integer, $this is not an integer")
- }
- return Vec2i(x, z)
- }
-
- private fun getHomeIndex(parameter: Parameter<*, *>, kind: Int, sender: CommandSender, input: String): Pair<PlayerProfile, Int> {
- val splitIdx = input.indexOf(':')
- val ownerString: String
- val index: Int?
-
- val speciallyParsedIndex = parcelAddress?.speciallyParsedIndex
-
- if (splitIdx == -1) {
-
- if (speciallyParsedIndex == null) {
- // just the index.
- index = input.toIntOrNull()
- ownerString = if (index == null) input else ""
- } else {
- // just the owner.
- index = speciallyParsedIndex
- ownerString = input
- }
-
- } else {
- if (speciallyParsedIndex != null) {
- invalidInput(parameter, "Duplicate home index")
- }
-
- ownerString = input.substring(0, splitIdx)
-
- val indexString = input.substring(splitIdx + 1)
- index = indexString.toIntOrNull()
- ?: invalidInput(parameter, "The home index must be an integer, $indexString is not an integer")
- }
-
- val owner = if (ownerString.isEmpty())
- PlayerProfile(requirePlayer(sender, parameter, "the player"))
- else
- PlayerProfile.byName(ownerString, allowReal = kind and OWNER_REAL != 0, allowFake = kind and OWNER_FAKE != 0)
-
- return owner to (index ?: 0)
- }
-
- private fun requirePlayer(sender: CommandSender, parameter: Parameter<*, *>, objName: String): Player {
- if (sender !is Player) invalidInput(parameter, "console cannot omit the $objName")
- return sender
- }
-
- override fun getDefaultValue(parameter: Parameter<ParcelTarget, Int>, sender: CommandSender, buffer: ArgumentBuffer): ParcelTarget? {
- val kind = parameter.paramInfo ?: DEFAULT_KIND
- val useLocation = when {
- kind and REAL == REAL -> kind and PREFER_OWNED_FOR_DEFAULT == 0
- kind and ID != 0 -> true
- kind and OWNER_REAL != 0 -> false
- else -> return null
- }
-
- val player = requirePlayer(sender, parameter, "the parcel")
- val world = parcelProvider.getWorld(player.world) ?: invalidInput(parameter, "You must be in a parcel world to omit the parcel")
- if (useLocation) {
- val id = player.location.let { world.getParcelIdAt(it.x.floor(), it.z.floor())?.pos }
- return ByID(world, id, kind, true)
- }
-
- return ByOwner(world, PlayerProfile(player), parcelAddress?.speciallyParsedIndex ?: 0, kind, true)
- }
- }
-
-}
+package io.dico.parcels2.command
+
+import io.dico.dicore.command.parameter.ArgumentBuffer
+import io.dico.dicore.command.parameter.Parameter
+import io.dico.dicore.command.parameter.type.ParameterConfig
+import io.dico.dicore.command.parameter.type.ParameterType
+import io.dico.parcels2.Parcel
+import io.dico.parcels2.ParcelProvider
+import io.dico.parcels2.ParcelWorld
+import io.dico.parcels2.PlayerProfile
+import io.dico.parcels2.command.ParcelTarget.TargetKind.Companion.DEFAULT_KIND
+import io.dico.parcels2.command.ParcelTarget.TargetKind.Companion.ID
+import io.dico.parcels2.command.ParcelTarget.TargetKind.Companion.OWNER
+import io.dico.parcels2.command.ParcelTarget.TargetKind.Companion.OWNER_FAKE
+import io.dico.parcels2.command.ParcelTarget.TargetKind.Companion.OWNER_REAL
+import io.dico.parcels2.command.ParcelTarget.TargetKind.Companion.PREFER_OWNED_FOR_DEFAULT
+import io.dico.parcels2.command.ParcelTarget.TargetKind.Companion.REAL
+import io.dico.parcels2.storage.Storage
+import io.dico.parcels2.util.math.Vec2i
+import io.dico.parcels2.util.math.floor
+import org.bukkit.command.CommandSender
+import org.bukkit.entity.Player
+
+sealed class ParcelTarget(val world: ParcelWorld, val parsedKind: Int, val isDefault: Boolean) {
+
+ abstract suspend fun getParcelSuspend(storage: Storage): Parcel?
+
+ class ByID(world: ParcelWorld, val id: Vec2i?, parsedKind: Int, isDefault: Boolean) : ParcelTarget(world, parsedKind, isDefault) {
+ override suspend fun getParcelSuspend(storage: Storage): Parcel? = getParcel()
+ fun getParcel() = id?.let { world.getParcelById(it) }
+ val isPath: Boolean get() = id == null
+ }
+
+ class ByOwner(
+ world: ParcelWorld,
+ owner: PlayerProfile,
+ val index: Int,
+ parsedKind: Int,
+ isDefault: Boolean,
+ val onResolveFailure: (() -> Unit)? = null
+ ) : ParcelTarget(world, parsedKind, isDefault) {
+ init {
+ if (index < 0) throw IllegalArgumentException("Invalid parcel home index: $index")
+ }
+
+ var owner = owner; private set
+
+ suspend fun resolveOwner(storage: Storage): Boolean {
+ val owner = owner
+ if (owner is PlayerProfile.Unresolved) {
+ this.owner = owner.tryResolveSuspendedly(storage) ?: if (parsedKind and OWNER_FAKE != 0) PlayerProfile.Fake(owner.name)
+ else run { onResolveFailure?.invoke(); return false }
+ }
+ return true
+ }
+
+ override suspend fun getParcelSuspend(storage: Storage): Parcel? {
+ onResolveFailure?.let { resolveOwner(storage) }
+
+ val ownedParcelsSerialized = storage.getOwnedParcels(owner).await()
+ val ownedParcels = ownedParcelsSerialized
+ .filter { it.worldId.equals(world.id) }
+ .map { world.getParcelById(it.x, it.z) }
+
+ return ownedParcels.getOrNull(index)
+ }
+ }
+
+ annotation class TargetKind(val kind: Int) {
+ companion object : ParameterConfig<TargetKind, Int>(TargetKind::class.java) {
+ const val ID = 1 // ID
+ const val OWNER_REAL = 2 // an owner backed by a UUID
+ const val OWNER_FAKE = 4 // an owner not backed by a UUID
+
+ const val OWNER = OWNER_REAL or OWNER_FAKE // any owner
+ const val ANY = ID or OWNER_REAL or OWNER_FAKE // any
+ const val REAL = ID or OWNER_REAL // no owner not backed by a UUID
+
+ const val DEFAULT_KIND = REAL
+
+ const val PREFER_OWNED_FOR_DEFAULT = 8 // if the kind can be ID and OWNER_REAL, prefer OWNER_REAL for default
+ // instead of parcel that the player is in
+
+ override fun toParameterInfo(annotation: TargetKind): Int {
+ return annotation.kind
+ }
+ }
+ }
+
+ class PType(val parcelProvider: ParcelProvider, val parcelAddress: SpecialCommandAddress? = null) :
+ ParameterType<ParcelTarget, Int>(ParcelTarget::class.java, TargetKind) {
+
+ override fun parse(parameter: Parameter<ParcelTarget, Int>, sender: CommandSender, buffer: ArgumentBuffer): ParcelTarget {
+ var input = buffer.next()!!
+ val worldString = input.substringBefore("/", missingDelimiterValue = "")
+ input = input.substringAfter("/")
+
+ val world = if (worldString.isEmpty()) {
+ val player = requirePlayer(sender, parameter, "the world")
+ parcelProvider.getWorld(player.world)
+ ?: invalidInput(parameter, "You cannot omit the world if you're not in a parcel world")
+ } else {
+ parcelProvider.getWorld(worldString) ?: invalidInput(parameter, "$worldString is not a parcel world")
+ }
+
+ val kind = parameter.paramInfo ?: DEFAULT_KIND
+ if (input.contains(',')) {
+ if (kind and ID == 0) invalidInput(parameter, "You must specify a parcel by OWNER, that is, an owner and index")
+ return ByID(world, getId(parameter, input), kind, false)
+ }
+
+ if (kind and OWNER == 0) invalidInput(parameter, "You must specify a parcel by ID, that is, the x and z component separated by a comma")
+ val (owner, index) = getHomeIndex(parameter, kind, sender, input)
+ return ByOwner(world, owner, index, kind, false, onResolveFailure = { invalidInput(parameter, "The player $input does not exist") })
+ }
+
+ private fun getId(parameter: Parameter<*, *>, input: String): Vec2i {
+ val x = input.substringBefore(',').run {
+ toIntOrNull() ?: invalidInput(parameter, "ID(x) must be an integer, $this is not an integer")
+ }
+ val z = input.substringAfter(',').run {
+ toIntOrNull() ?: invalidInput(parameter, "ID(z) must be an integer, $this is not an integer")
+ }
+ return Vec2i(x, z)
+ }
+
+ private fun getHomeIndex(parameter: Parameter<*, *>, kind: Int, sender: CommandSender, input: String): Pair<PlayerProfile, Int> {
+ val splitIdx = input.indexOf(':')
+ val ownerString: String
+ val index: Int?
+
+ val speciallyParsedIndex = parcelAddress?.speciallyParsedIndex
+
+ if (splitIdx == -1) {
+
+ if (speciallyParsedIndex == null) {
+ // just the index.
+ index = input.toIntOrNull()
+ ownerString = if (index == null) input else ""
+ } else {
+ // just the owner.
+ index = speciallyParsedIndex
+ ownerString = input
+ }
+
+ } else {
+ if (speciallyParsedIndex != null) {
+ invalidInput(parameter, "Duplicate home index")
+ }
+
+ ownerString = input.substring(0, splitIdx)
+
+ val indexString = input.substring(splitIdx + 1)
+ index = indexString.toIntOrNull()
+ ?: invalidInput(parameter, "The home index must be an integer, $indexString is not an integer")
+ }
+
+ val owner = if (ownerString.isEmpty())
+ PlayerProfile(requirePlayer(sender, parameter, "the player"))
+ else
+ PlayerProfile.byName(ownerString, allowReal = kind and OWNER_REAL != 0, allowFake = kind and OWNER_FAKE != 0)
+
+ return owner to (index ?: 0)
+ }
+
+ private fun requirePlayer(sender: CommandSender, parameter: Parameter<*, *>, objName: String): Player {
+ if (sender !is Player) invalidInput(parameter, "console cannot omit the $objName")
+ return sender
+ }
+
+ override fun getDefaultValue(parameter: Parameter<ParcelTarget, Int>, sender: CommandSender, buffer: ArgumentBuffer): ParcelTarget? {
+ val kind = parameter.paramInfo ?: DEFAULT_KIND
+ val useLocation = when {
+ kind and REAL == REAL -> kind and PREFER_OWNED_FOR_DEFAULT == 0
+ kind and ID != 0 -> true
+ kind and OWNER_REAL != 0 -> false
+ else -> return null
+ }
+
+ val player = requirePlayer(sender, parameter, "the parcel")
+ val world = parcelProvider.getWorld(player.world) ?: invalidInput(parameter, "You must be in a parcel world to omit the parcel")
+ if (useLocation) {
+ val id = player.location.let { world.getParcelIdAt(it.x.floor(), it.z.floor())?.pos }
+ return ByID(world, id, kind, true)
+ }
+
+ return ByOwner(world, PlayerProfile(player), parcelAddress?.speciallyParsedIndex ?: 0, kind, true)
+ }
+ }
+
+}
diff --git a/src/main/kotlin/io/dico/parcels2/command/ParcelsChatHandler.kt b/src/main/kotlin/io/dico/parcels2/command/ParcelsChatHandler.kt
index b616f77..52f1104 100644
--- a/src/main/kotlin/io/dico/parcels2/command/ParcelsChatHandler.kt
+++ b/src/main/kotlin/io/dico/parcels2/command/ParcelsChatHandler.kt
@@ -1,24 +1,24 @@
-package io.dico.parcels2.command
-
-import io.dico.dicore.Formatting
-import io.dico.dicore.command.EMessageType
-import io.dico.dicore.command.ExecutionContext
-import io.dico.dicore.command.chat.AbstractChatHandler
-import io.dico.parcels2.util.ext.plus
-
-class ParcelsChatHandler : AbstractChatHandler() {
-
- override fun getMessagePrefixForType(type: EMessageType?): String {
- return Formatting.RED + "[Parcels] "
- }
-
- override fun createMessage(context: ExecutionContext, type: EMessageType, message: String?): String? {
- if (message.isNullOrEmpty()) return null
- var result = getChatFormatForType(type) + message
- if (context.address.mainKey != "info") {
- result = getMessagePrefixForType(type) + result
- }
- return result
- }
-
+package io.dico.parcels2.command
+
+import io.dico.dicore.Formatting
+import io.dico.dicore.command.EMessageType
+import io.dico.dicore.command.ExecutionContext
+import io.dico.dicore.command.chat.AbstractChatHandler
+import io.dico.parcels2.util.ext.plus
+
+class ParcelsChatHandler : AbstractChatHandler() {
+
+ override fun getMessagePrefixForType(type: EMessageType?): String {
+ return Formatting.RED + "[Parcels] "
+ }
+
+ override fun createMessage(context: ExecutionContext, type: EMessageType, message: String?): String? {
+ if (message.isNullOrEmpty()) return null
+ var result = getChatFormatForType(type) + message
+ if (context.address.mainKey != "info") {
+ result = getMessagePrefixForType(type) + result
+ }
+ return result
+ }
+
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelContainer.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelContainer.kt
index 1193af3..b49cad4 100644
--- a/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelContainer.kt
+++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelContainer.kt
@@ -1,73 +1,73 @@
-package io.dico.parcels2.defaultimpl
-
-import io.dico.parcels2.Parcel
-import io.dico.parcels2.ParcelContainer
-import io.dico.parcels2.ParcelId
-import io.dico.parcels2.ParcelWorld
-
-class DefaultParcelContainer(val world: ParcelWorld) : ParcelContainer {
- private var parcels: Array<Array<Parcel>>
-
- init {
- parcels = initArray(world.options.axisLimit, world)
- }
-
- fun resizeIfSizeChanged() {
- if (parcels.size != world.options.axisLimit * 2 + 1) {
- resize(world.options.axisLimit)
- }
- }
-
- fun resize(axisLimit: Int) {
- parcels = initArray(axisLimit, world, this)
- }
-
- fun initArray(axisLimit: Int, world: ParcelWorld, cur: DefaultParcelContainer? = null): Array<Array<Parcel>> {
- val arraySize = 2 * axisLimit + 1
- return Array(arraySize) {
- val x = it - axisLimit
- Array(arraySize) {
- val z = it - axisLimit
- cur?.getParcelById(x, z) ?: ParcelImpl(world, x, z)
- }
- }
- }
-
- override fun getParcelById(x: Int, z: Int): Parcel? {
- return parcels.getOrNull(x + world.options.axisLimit)?.getOrNull(z + world.options.axisLimit)
- }
-
- override fun getParcelById(id: ParcelId): Parcel? {
- if (!world.id.equals(id.worldId)) throw IllegalArgumentException()
- return when (id) {
- is Parcel -> id
- else -> getParcelById(id.x, id.z)
- }
- }
-
- override fun nextEmptyParcel(): Parcel? {
- return walkInCircle().find { it.owner == null }
- }
-
- private fun walkInCircle(): Iterable<Parcel> = Iterable {
- iterator {
- val center = world.options.axisLimit
- yield(parcels[center][center])
- for (radius in 0..center) {
- var x = center - radius;
- var z = center - radius
- repeat(radius * 2) { yield(parcels[x++][z]) }
- repeat(radius * 2) { yield(parcels[x][z++]) }
- repeat(radius * 2) { yield(parcels[x--][z]) }
- repeat(radius * 2) { yield(parcels[x][z--]) }
- }
- }
- }
-
- fun getAllParcels(): Iterator<Parcel> = iterator {
- for (array in parcels) {
- yieldAll(array.iterator())
- }
- }
-
+package io.dico.parcels2.defaultimpl
+
+import io.dico.parcels2.Parcel
+import io.dico.parcels2.ParcelContainer
+import io.dico.parcels2.ParcelId
+import io.dico.parcels2.ParcelWorld
+
+class DefaultParcelContainer(val world: ParcelWorld) : ParcelContainer {
+ private var parcels: Array<Array<Parcel>>
+
+ init {
+ parcels = initArray(world.options.axisLimit, world)
+ }
+
+ fun resizeIfSizeChanged() {
+ if (parcels.size != world.options.axisLimit * 2 + 1) {
+ resize(world.options.axisLimit)
+ }
+ }
+
+ fun resize(axisLimit: Int) {
+ parcels = initArray(axisLimit, world, this)
+ }
+
+ fun initArray(axisLimit: Int, world: ParcelWorld, cur: DefaultParcelContainer? = null): Array<Array<Parcel>> {
+ val arraySize = 2 * axisLimit + 1
+ return Array(arraySize) {
+ val x = it - axisLimit
+ Array(arraySize) {
+ val z = it - axisLimit
+ cur?.getParcelById(x, z) ?: ParcelImpl(world, x, z)
+ }
+ }
+ }
+
+ override fun getParcelById(x: Int, z: Int): Parcel? {
+ return parcels.getOrNull(x + world.options.axisLimit)?.getOrNull(z + world.options.axisLimit)
+ }
+
+ override fun getParcelById(id: ParcelId): Parcel? {
+ if (!world.id.equals(id.worldId)) throw IllegalArgumentException()
+ return when (id) {
+ is Parcel -> id
+ else -> getParcelById(id.x, id.z)
+ }
+ }
+
+ override fun nextEmptyParcel(): Parcel? {
+ return walkInCircle().find { it.owner == null }
+ }
+
+ private fun walkInCircle(): Iterable<Parcel> = Iterable {
+ iterator {
+ val center = world.options.axisLimit
+ yield(parcels[center][center])
+ for (radius in 0..center) {
+ var x = center - radius;
+ var z = center - radius
+ repeat(radius * 2) { yield(parcels[x++][z]) }
+ repeat(radius * 2) { yield(parcels[x][z++]) }
+ repeat(radius * 2) { yield(parcels[x--][z]) }
+ repeat(radius * 2) { yield(parcels[x][z--]) }
+ }
+ }
+ }
+
+ fun getAllParcels(): Iterator<Parcel> = iterator {
+ for (array in parcels) {
+ yieldAll(array.iterator())
+ }
+ }
+
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt
index 9e43c05..caa3f1f 100644
--- a/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt
+++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/DefaultParcelGenerator.kt
@@ -1,378 +1,378 @@
-package io.dico.parcels2.defaultimpl
-
-import io.dico.parcels2.*
-import io.dico.parcels2.blockvisitor.RegionTraverser
-import io.dico.parcels2.options.DefaultGeneratorOptions
-import io.dico.parcels2.util.math.*
-import kotlinx.coroutines.CoroutineScope
-import org.bukkit.*
-import org.bukkit.block.Biome
-import org.bukkit.block.BlockFace
-import org.bukkit.block.Skull
-import org.bukkit.block.data.type.Slab
-import org.bukkit.block.data.type.WallSign
-import java.util.Random
-
-private val airType = Bukkit.createBlockData(Material.AIR)
-
-private const val chunkSize = 16
-
-class DefaultParcelGenerator(
- override val worldName: String,
- private val o: DefaultGeneratorOptions
-) : ParcelGenerator() {
- private var _world: World? = null
- override val world: World
- get() {
- if (_world == null) {
- val world = Bukkit.getWorld(worldName)
- maxHeight = world.maxHeight
- _world = world
- return world
- }
- return _world!!
- }
-
- private var maxHeight = 0
- val sectionSize = o.parcelSize + o.pathSize
- val pathOffset = (if (o.pathSize % 2 == 0) o.pathSize + 2 else o.pathSize + 1) / 2
- val makePathMain = o.pathSize > 2
- val makePathAlt = o.pathSize > 4
-
- private inline fun <T> generate(
- chunkX: Int,
- chunkZ: Int,
- floor: T, wall:
- T, pathMain: T,
- pathAlt: T,
- fill: T,
- setter: (Int, Int, Int, T) -> Unit
- ) {
-
- val floorHeight = o.floorHeight
- val parcelSize = o.parcelSize
- val sectionSize = sectionSize
- val pathOffset = pathOffset
- val makePathMain = makePathMain
- val makePathAlt = makePathAlt
-
- // parcel bottom x and z
- // umod is unsigned %: the result is always >= 0
- val pbx = ((chunkX shl 4) - o.offsetX) umod sectionSize
- val pbz = ((chunkZ shl 4) - o.offsetZ) umod sectionSize
-
- var curHeight: Int
- var x: Int
- var z: Int
- for (cx in 0..15) {
- for (cz in 0..15) {
- x = (pbx + cx) % sectionSize - pathOffset
- z = (pbz + cz) % sectionSize - pathOffset
- curHeight = floorHeight
-
- val type = when {
- (x in 0 until parcelSize && z in 0 until parcelSize) -> floor
- (x in -1..parcelSize && z in -1..parcelSize) -> {
- curHeight++
- wall
- }
- (makePathAlt && x in -2 until parcelSize + 2 && z in -2 until parcelSize + 2) -> pathAlt
- (makePathMain) -> pathMain
- else -> {
- curHeight++
- wall
- }
- }
-
- for (y in 0 until curHeight) {
- setter(cx, y, cz, fill)
- }
- setter(cx, curHeight, cz, type)
- }
- }
- }
-
- override fun generateChunkData(world: World?, random: Random?, chunkX: Int, chunkZ: Int, biome: BiomeGrid?): ChunkData {
- val out = Bukkit.createChunkData(world)
- generate(chunkX, chunkZ, o.floorType, o.wallType, o.pathMainType, o.pathAltType, o.fillType) { x, y, z, type ->
- out.setBlock(x, y, z, type)
- }
- return out
- }
-
- override fun populate(world: World?, random: Random?, chunk: Chunk?) {
- // do nothing
- }
-
- override fun getFixedSpawnLocation(world: World?, random: Random?): Location {
- val fix = if (o.parcelSize.even) 0.5 else 0.0
- return Location(world, o.offsetX + fix, o.floorHeight + 1.0, o.offsetZ + fix)
- }
-
- override fun makeParcelLocatorAndBlockManager(
- parcelProvider: ParcelProvider,
- container: ParcelContainer,
- coroutineScope: CoroutineScope,
- jobDispatcher: JobDispatcher
- ): Pair<ParcelLocator, ParcelBlockManager> {
- val impl = ParcelLocatorAndBlockManagerImpl(parcelProvider, container, coroutineScope, jobDispatcher)
- return impl to impl
- }
-
- private inline fun <T> convertBlockLocationToId(x: Int, z: Int, mapper: (Int, Int) -> T): T? {
- val sectionSize = sectionSize
- val parcelSize = o.parcelSize
- val absX = x - o.offsetX - pathOffset
- val absZ = z - o.offsetZ - pathOffset
- val modX = absX umod sectionSize
- val modZ = absZ umod sectionSize
- if (modX in 0 until parcelSize && modZ in 0 until parcelSize) {
- return mapper((absX - modX) / sectionSize + 1, (absZ - modZ) / sectionSize + 1)
- }
- return null
- }
-
- @Suppress("DEPRECATION")
- private inner class ParcelLocatorAndBlockManagerImpl(
- val parcelProvider: ParcelProvider,
- val container: ParcelContainer,
- coroutineScope: CoroutineScope,
- override val jobDispatcher: JobDispatcher
- ) : ParcelBlockManagerBase(), ParcelLocator, CoroutineScope by coroutineScope {
-
- override val world: World get() = this@DefaultParcelGenerator.world
- val worldId = parcelProvider.getWorld(world)?.id ?: ParcelWorldId(world)
- override val parcelTraverser: RegionTraverser = RegionTraverser.convergingTo(o.floorHeight)
-
- private val cornerWallType = when {
- o.wallType is Slab -> (o.wallType.clone() as Slab).apply { type = Slab.Type.DOUBLE }
- o.wallType.material.name.endsWith("CARPET") -> {
- Bukkit.createBlockData(Material.getMaterial(o.wallType.material.name.substringBefore("CARPET") + "WOOL"))
- }
- else -> null
- }
-
- override fun getParcelAt(x: Int, z: Int): Parcel? {
- return convertBlockLocationToId(x, z, container::getParcelById)
- }
-
- override fun getParcelIdAt(x: Int, z: Int): ParcelId? {
- return convertBlockLocationToId(x, z) { idx, idz -> ParcelId(worldId, idx, idz) }
- }
-
-
- private fun checkParcelId(parcel: ParcelId): ParcelId {
- if (!parcel.worldId.equals(worldId)) {
- throw IllegalArgumentException()
- }
- return parcel
- }
-
- override fun getRegionOrigin(parcel: ParcelId): Vec2i {
- checkParcelId(parcel)
- return Vec2i(
- sectionSize * (parcel.x - 1) + pathOffset + o.offsetX,
- sectionSize * (parcel.z - 1) + pathOffset + o.offsetZ
- )
- }
-
- override fun getRegion(parcel: ParcelId): Region {
- val origin = getRegionOrigin(parcel)
- return Region(
- Vec3i(origin.x, 0, origin.z),
- Vec3i(o.parcelSize, maxHeight, o.parcelSize)
- )
- }
-
- override fun getHomeLocation(parcel: ParcelId): Location {
- val origin = getRegionOrigin(parcel)
- val x = origin.x + (o.parcelSize - 1) / 2.0
- val z = origin.z - 2
- return Location(world, x + 0.5, o.floorHeight + 1.0, z + 0.5, 0F, 0F)
- }
-
- override fun getParcelForInfoBlockInteraction(block: Vec3i, type: Material, face: BlockFace): Parcel? {
- if (block.y != o.floorHeight + 1) return null
-
- val expectedParcelOrigin = when (type) {
- Material.WALL_SIGN -> Vec2i(block.x + 1, block.z + 2)
- o.wallType.material, cornerWallType?.material -> {
- if (face != BlockFace.NORTH || world[block + Vec3i.convert(BlockFace.NORTH)].type == Material.WALL_SIGN) {
- return null
- }
-
- Vec2i(block.x + 1, block.z + 1)
- }
- else -> return null
- }
-
- return getParcelAt(expectedParcelOrigin.x, expectedParcelOrigin.z)
- ?.takeIf { expectedParcelOrigin == getRegionOrigin(it.id) }
- ?.also { parcel ->
- if (type != Material.WALL_SIGN && parcel.owner != null) {
- updateParcelInfo(parcel.id, parcel.owner)
- parcel.isOwnerSignOutdated = false
- }
- }
- }
-
- override fun isParcelInfoSectionLoaded(parcel: ParcelId): Boolean {
- val wallBlockChunk = getRegionOrigin(parcel).add(-1, -1).toChunk()
- return world.isChunkLoaded(wallBlockChunk.x, wallBlockChunk.z)
- }
-
- override fun updateParcelInfo(parcel: ParcelId, owner: PlayerProfile?) {
- val b = getRegionOrigin(parcel)
-
- val wallBlock = world.getBlockAt(b.x - 1, o.floorHeight + 1, b.z - 1)
- val signBlock = world.getBlockAt(b.x - 1, o.floorHeight + 1, b.z - 2)
- val skullBlock = world.getBlockAt(b.x - 1, o.floorHeight + 2, b.z - 1)
-
- if (owner == null) {
- wallBlock.blockData = o.wallType
- signBlock.type = Material.AIR
- skullBlock.type = Material.AIR
-
- } else {
- cornerWallType?.let { wallBlock.blockData = it }
- signBlock.blockData = (Bukkit.createBlockData(Material.WALL_SIGN) as WallSign).apply { facing = BlockFace.NORTH }
-
- val sign = signBlock.state as org.bukkit.block.Sign
- sign.setLine(0, "${parcel.x},${parcel.z}")
- sign.setLine(2, owner.name ?: "")
- sign.update()
-
- skullBlock.type = Material.AIR
- skullBlock.type = Material.PLAYER_HEAD
- val skull = skullBlock.state as Skull
- if (owner is PlayerProfile.Real) {
- skull.owningPlayer = Bukkit.getOfflinePlayer(owner.uuid)
-
- } else if (!skull.setOwner(owner.name)) {
- skullBlock.type = Material.AIR
- return
- }
-
- skull.rotation = BlockFace.SOUTH
- skull.update()
- }
- }
-
- private fun trySubmitBlockVisitor(vararg parcels: ParcelId, function: JobFunction): Job? {
- parcels.forEach { checkParcelId(it) }
- return parcelProvider.trySubmitBlockVisitor(Permit(), parcels, function)
- }
-
- override fun setBiome(parcel: ParcelId, biome: Biome) = trySubmitBlockVisitor(checkParcelId(parcel)) {
- val world = world
- val b = getRegionOrigin(parcel)
- val parcelSize = o.parcelSize
- for (x in b.x until b.x + parcelSize) {
- for (z in b.z until b.z + parcelSize) {
- markSuspensionPoint()
- world.setBiome(x, z, biome)
- }
- }
- }
-
- override fun clearParcel(parcel: ParcelId) = trySubmitBlockVisitor(checkParcelId(parcel)) {
- val region = getRegion(parcel)
- val blocks = parcelTraverser.traverseRegion(region)
- val blockCount = region.blockCount.toDouble()
- val world = world
- val floorHeight = o.floorHeight
- val airType = airType
- val floorType = o.floorType
- val fillType = o.fillType
-
- for ((index, vec) in blocks.withIndex()) {
- markSuspensionPoint()
- val y = vec.y
- val blockType = when {
- y > floorHeight -> airType
- y == floorHeight -> floorType
- else -> fillType
- }
- world[vec].blockData = blockType
- setProgress((index + 1) / blockCount)
- }
- }
-
- override fun getParcelsWithOwnerBlockIn(chunk: Chunk): Collection<Vec2i> {
- /*
- * Get the offsets for the world out of the way
- * to simplify the calculation that follows.
- */
-
- val x = chunk.x.shl(4) - (o.offsetX + pathOffset)
- val z = chunk.z.shl(4) - (o.offsetZ + pathOffset)
-
- /* Locations of wall corners (where owner blocks are placed) are defined as:
- *
- * x umod sectionSize == sectionSize-1
- *
- * This check needs to be made for all 16 slices of the chunk in 2 dimensions
- * How to optimize this?
- * Let's take the expression
- *
- * x umod sectionSize
- *
- * And call it modX
- * x can be shifted (chunkSize -1) times to attempt to get a modX of 0.
- * This means that if the modX is 1, and sectionSize == (chunkSize-1), there would be a match at the last shift.
- * To check that there are any matches, we can see if the following holds:
- *
- * modX >= ((sectionSize-1) - (chunkSize-1))
- *
- * Which can be simplified to:
- * modX >= sectionSize - chunkSize
- *
- * if sectionSize == chunkSize, this expression can be simplified to
- * modX >= 0
- * which is always true. This is expected.
- * To get the total number of matches on a dimension, we can evaluate the following:
- *
- * (modX - (sectionSize - chunkSize) + sectionSize) / sectionSize
- *
- * We add sectionSize to the lhs because, if the other part of the lhs is 0, we need at least 1.
- * This can be simplified to:
- *
- * (modX + chunkSize) / sectionSize
- */
-
- val sectionSize = sectionSize
-
- val modX = x umod sectionSize
- val matchesOnDimensionX = (modX + chunkSize) / sectionSize
- if (matchesOnDimensionX <= 0) return emptyList()
-
- val modZ = z umod sectionSize
- val matchesOnDimensionZ = (modZ + chunkSize) / sectionSize
- if (matchesOnDimensionZ <= 0) return emptyList()
-
- /*
- * Now we need to find the first id within the matches,
- * and then return the subsequent matches in a rectangle following it.
- *
- * On each dimension, get the distance to the first match, which is equal to (sectionSize-1 - modX)
- * and add it to the coordinate value
- */
- val firstX = x + (sectionSize - 1 - modX)
- val firstZ = z + (sectionSize - 1 - modZ)
-
- val firstIdX = (firstX + 1) / sectionSize + 1
- val firstIdZ = (firstZ + 1) / sectionSize + 1
-
- if (matchesOnDimensionX == 1 && matchesOnDimensionZ == 1) {
- // fast-path optimization
- return listOf(Vec2i(firstIdX, firstIdZ))
- }
-
- return (0 until matchesOnDimensionX).flatMap { idOffsetX ->
- (0 until matchesOnDimensionZ).map { idOffsetZ -> Vec2i(firstIdX + idOffsetX, firstIdZ + idOffsetZ) }
- }
- }
-
- }
-
+package io.dico.parcels2.defaultimpl
+
+import io.dico.parcels2.*
+import io.dico.parcels2.blockvisitor.RegionTraverser
+import io.dico.parcels2.options.DefaultGeneratorOptions
+import io.dico.parcels2.util.math.*
+import kotlinx.coroutines.CoroutineScope
+import org.bukkit.*
+import org.bukkit.block.Biome
+import org.bukkit.block.BlockFace
+import org.bukkit.block.Skull
+import org.bukkit.block.data.type.Slab
+import org.bukkit.block.data.type.WallSign
+import java.util.Random
+
+private val airType = Bukkit.createBlockData(Material.AIR)
+
+private const val chunkSize = 16
+
+class DefaultParcelGenerator(
+ override val worldName: String,
+ private val o: DefaultGeneratorOptions
+) : ParcelGenerator() {
+ private var _world: World? = null
+ override val world: World
+ get() {
+ if (_world == null) {
+ val world = Bukkit.getWorld(worldName)
+ maxHeight = world.maxHeight
+ _world = world
+ return world
+ }
+ return _world!!
+ }
+
+ private var maxHeight = 0
+ val sectionSize = o.parcelSize + o.pathSize
+ val pathOffset = (if (o.pathSize % 2 == 0) o.pathSize + 2 else o.pathSize + 1) / 2
+ val makePathMain = o.pathSize > 2
+ val makePathAlt = o.pathSize > 4
+
+ private inline fun <T> generate(
+ chunkX: Int,
+ chunkZ: Int,
+ floor: T, wall:
+ T, pathMain: T,
+ pathAlt: T,
+ fill: T,
+ setter: (Int, Int, Int, T) -> Unit
+ ) {
+
+ val floorHeight = o.floorHeight
+ val parcelSize = o.parcelSize
+ val sectionSize = sectionSize
+ val pathOffset = pathOffset
+ val makePathMain = makePathMain
+ val makePathAlt = makePathAlt
+
+ // parcel bottom x and z
+ // umod is unsigned %: the result is always >= 0
+ val pbx = ((chunkX shl 4) - o.offsetX) umod sectionSize
+ val pbz = ((chunkZ shl 4) - o.offsetZ) umod sectionSize
+
+ var curHeight: Int
+ var x: Int
+ var z: Int
+ for (cx in 0..15) {
+ for (cz in 0..15) {
+ x = (pbx + cx) % sectionSize - pathOffset
+ z = (pbz + cz) % sectionSize - pathOffset
+ curHeight = floorHeight
+
+ val type = when {
+ (x in 0 until parcelSize && z in 0 until parcelSize) -> floor
+ (x in -1..parcelSize && z in -1..parcelSize) -> {
+ curHeight++
+ wall
+ }
+ (makePathAlt && x in -2 until parcelSize + 2 && z in -2 until parcelSize + 2) -> pathAlt
+ (makePathMain) -> pathMain
+ else -> {
+ curHeight++
+ wall
+ }
+ }
+
+ for (y in 0 until curHeight) {
+ setter(cx, y, cz, fill)
+ }
+ setter(cx, curHeight, cz, type)
+ }
+ }
+ }
+
+ override fun generateChunkData(world: World?, random: Random?, chunkX: Int, chunkZ: Int, biome: BiomeGrid?): ChunkData {
+ val out = Bukkit.createChunkData(world)
+ generate(chunkX, chunkZ, o.floorType, o.wallType, o.pathMainType, o.pathAltType, o.fillType) { x, y, z, type ->
+ out.setBlock(x, y, z, type)
+ }
+ return out
+ }
+
+ override fun populate(world: World?, random: Random?, chunk: Chunk?) {
+ // do nothing
+ }
+
+ override fun getFixedSpawnLocation(world: World?, random: Random?): Location {
+ val fix = if (o.parcelSize.even) 0.5 else 0.0
+ return Location(world, o.offsetX + fix, o.floorHeight + 1.0, o.offsetZ + fix)
+ }
+
+ override fun makeParcelLocatorAndBlockManager(
+ parcelProvider: ParcelProvider,
+ container: ParcelContainer,
+ coroutineScope: CoroutineScope,
+ jobDispatcher: JobDispatcher
+ ): Pair<ParcelLocator, ParcelBlockManager> {
+ val impl = ParcelLocatorAndBlockManagerImpl(parcelProvider, container, coroutineScope, jobDispatcher)
+ return impl to impl
+ }
+
+ private inline fun <T> convertBlockLocationToId(x: Int, z: Int, mapper: (Int, Int) -> T): T? {
+ val sectionSize = sectionSize
+ val parcelSize = o.parcelSize
+ val absX = x - o.offsetX - pathOffset
+ val absZ = z - o.offsetZ - pathOffset
+ val modX = absX umod sectionSize
+ val modZ = absZ umod sectionSize
+ if (modX in 0 until parcelSize && modZ in 0 until parcelSize) {
+ return mapper((absX - modX) / sectionSize + 1, (absZ - modZ) / sectionSize + 1)
+ }
+ return null
+ }
+
+ @Suppress("DEPRECATION")
+ private inner class ParcelLocatorAndBlockManagerImpl(
+ val parcelProvider: ParcelProvider,
+ val container: ParcelContainer,
+ coroutineScope: CoroutineScope,
+ override val jobDispatcher: JobDispatcher
+ ) : ParcelBlockManagerBase(), ParcelLocator, CoroutineScope by coroutineScope {
+
+ override val world: World get() = this@DefaultParcelGenerator.world
+ val worldId = parcelProvider.getWorld(world)?.id ?: ParcelWorldId(world)
+ override val parcelTraverser: RegionTraverser = RegionTraverser.convergingTo(o.floorHeight)
+
+ private val cornerWallType = when {
+ o.wallType is Slab -> (o.wallType.clone() as Slab).apply { type = Slab.Type.DOUBLE }
+ o.wallType.material.name.endsWith("CARPET") -> {
+ Bukkit.createBlockData(Material.getMaterial(o.wallType.material.name.substringBefore("CARPET") + "WOOL"))
+ }
+ else -> null
+ }
+
+ override fun getParcelAt(x: Int, z: Int): Parcel? {
+ return convertBlockLocationToId(x, z, container::getParcelById)
+ }
+
+ override fun getParcelIdAt(x: Int, z: Int): ParcelId? {
+ return convertBlockLocationToId(x, z) { idx, idz -> ParcelId(worldId, idx, idz) }
+ }
+
+
+ private fun checkParcelId(parcel: ParcelId): ParcelId {
+ if (!parcel.worldId.equals(worldId)) {
+ throw IllegalArgumentException()
+ }
+ return parcel
+ }
+
+ override fun getRegionOrigin(parcel: ParcelId): Vec2i {
+ checkParcelId(parcel)
+ return Vec2i(
+ sectionSize * (parcel.x - 1) + pathOffset + o.offsetX,
+ sectionSize * (parcel.z - 1) + pathOffset + o.offsetZ
+ )
+ }
+
+ override fun getRegion(parcel: ParcelId): Region {
+ val origin = getRegionOrigin(parcel)
+ return Region(
+ Vec3i(origin.x, 0, origin.z),
+ Vec3i(o.parcelSize, maxHeight, o.parcelSize)
+ )
+ }
+
+ override fun getHomeLocation(parcel: ParcelId): Location {
+ val origin = getRegionOrigin(parcel)
+ val x = origin.x + (o.parcelSize - 1) / 2.0
+ val z = origin.z - 2
+ return Location(world, x + 0.5, o.floorHeight + 1.0, z + 0.5, 0F, 0F)
+ }
+
+ override fun getParcelForInfoBlockInteraction(block: Vec3i, type: Material, face: BlockFace): Parcel? {
+ if (block.y != o.floorHeight + 1) return null
+
+ val expectedParcelOrigin = when (type) {
+ Material.WALL_SIGN -> Vec2i(block.x + 1, block.z + 2)
+ o.wallType.material, cornerWallType?.material -> {
+ if (face != BlockFace.NORTH || world[block + Vec3i.convert(BlockFace.NORTH)].type == Material.WALL_SIGN) {
+ return null
+ }
+
+ Vec2i(block.x + 1, block.z + 1)
+ }
+ else -> return null
+ }
+
+ return getParcelAt(expectedParcelOrigin.x, expectedParcelOrigin.z)
+ ?.takeIf { expectedParcelOrigin == getRegionOrigin(it.id) }
+ ?.also { parcel ->
+ if (type != Material.WALL_SIGN && parcel.owner != null) {
+ updateParcelInfo(parcel.id, parcel.owner)
+ parcel.isOwnerSignOutdated = false
+ }
+ }
+ }
+
+ override fun isParcelInfoSectionLoaded(parcel: ParcelId): Boolean {
+ val wallBlockChunk = getRegionOrigin(parcel).add(-1, -1).toChunk()
+ return world.isChunkLoaded(wallBlockChunk.x, wallBlockChunk.z)
+ }
+
+ override fun updateParcelInfo(parcel: ParcelId, owner: PlayerProfile?) {
+ val b = getRegionOrigin(parcel)
+
+ val wallBlock = world.getBlockAt(b.x - 1, o.floorHeight + 1, b.z - 1)
+ val signBlock = world.getBlockAt(b.x - 1, o.floorHeight + 1, b.z - 2)
+ val skullBlock = world.getBlockAt(b.x - 1, o.floorHeight + 2, b.z - 1)
+
+ if (owner == null) {
+ wallBlock.blockData = o.wallType
+ signBlock.type = Material.AIR
+ skullBlock.type = Material.AIR
+
+ } else {
+ cornerWallType?.let { wallBlock.blockData = it }
+ signBlock.blockData = (Bukkit.createBlockData(Material.WALL_SIGN) as WallSign).apply { facing = BlockFace.NORTH }
+
+ val sign = signBlock.state as org.bukkit.block.Sign
+ sign.setLine(0, "${parcel.x},${parcel.z}")
+ sign.setLine(2, owner.name ?: "")
+ sign.update()
+
+ skullBlock.type = Material.AIR
+ skullBlock.type = Material.PLAYER_HEAD
+ val skull = skullBlock.state as Skull
+ if (owner is PlayerProfile.Real) {
+ skull.owningPlayer = Bukkit.getOfflinePlayer(owner.uuid)
+
+ } else if (!skull.setOwner(owner.name)) {
+ skullBlock.type = Material.AIR
+ return
+ }
+
+ skull.rotation = BlockFace.SOUTH
+ skull.update()
+ }
+ }
+
+ private fun trySubmitBlockVisitor(vararg parcels: ParcelId, function: JobFunction): Job? {
+ parcels.forEach { checkParcelId(it) }
+ return parcelProvider.trySubmitBlockVisitor(Permit(), parcels, function)
+ }
+
+ override fun setBiome(parcel: ParcelId, biome: Biome) = trySubmitBlockVisitor(checkParcelId(parcel)) {
+ val world = world
+ val b = getRegionOrigin(parcel)
+ val parcelSize = o.parcelSize
+ for (x in b.x until b.x + parcelSize) {
+ for (z in b.z until b.z + parcelSize) {
+ markSuspensionPoint()
+ world.setBiome(x, z, biome)
+ }
+ }
+ }
+
+ override fun clearParcel(parcel: ParcelId) = trySubmitBlockVisitor(checkParcelId(parcel)) {
+ val region = getRegion(parcel)
+ val blocks = parcelTraverser.traverseRegion(region)
+ val blockCount = region.blockCount.toDouble()
+ val world = world
+ val floorHeight = o.floorHeight
+ val airType = airType
+ val floorType = o.floorType
+ val fillType = o.fillType
+
+ for ((index, vec) in blocks.withIndex()) {
+ markSuspensionPoint()
+ val y = vec.y
+ val blockType = when {
+ y > floorHeight -> airType
+ y == floorHeight -> floorType
+ else -> fillType
+ }
+ world[vec].blockData = blockType
+ setProgress((index + 1) / blockCount)
+ }
+ }
+
+ override fun getParcelsWithOwnerBlockIn(chunk: Chunk): Collection<Vec2i> {
+ /*
+ * Get the offsets for the world out of the way
+ * to simplify the calculation that follows.
+ */
+
+ val x = chunk.x.shl(4) - (o.offsetX + pathOffset)
+ val z = chunk.z.shl(4) - (o.offsetZ + pathOffset)
+
+ /* Locations of wall corners (where owner blocks are placed) are defined as:
+ *
+ * x umod sectionSize == sectionSize-1
+ *
+ * This check needs to be made for all 16 slices of the chunk in 2 dimensions
+ * How to optimize this?
+ * Let's take the expression
+ *
+ * x umod sectionSize
+ *
+ * And call it modX
+ * x can be shifted (chunkSize -1) times to attempt to get a modX of 0.
+ * This means that if the modX is 1, and sectionSize == (chunkSize-1), there would be a match at the last shift.
+ * To check that there are any matches, we can see if the following holds:
+ *
+ * modX >= ((sectionSize-1) - (chunkSize-1))
+ *
+ * Which can be simplified to:
+ * modX >= sectionSize - chunkSize
+ *
+ * if sectionSize == chunkSize, this expression can be simplified to
+ * modX >= 0
+ * which is always true. This is expected.
+ * To get the total number of matches on a dimension, we can evaluate the following:
+ *
+ * (modX - (sectionSize - chunkSize) + sectionSize) / sectionSize
+ *
+ * We add sectionSize to the lhs because, if the other part of the lhs is 0, we need at least 1.
+ * This can be simplified to:
+ *
+ * (modX + chunkSize) / sectionSize
+ */
+
+ val sectionSize = sectionSize
+
+ val modX = x umod sectionSize
+ val matchesOnDimensionX = (modX + chunkSize) / sectionSize
+ if (matchesOnDimensionX <= 0) return emptyList()
+
+ val modZ = z umod sectionSize
+ val matchesOnDimensionZ = (modZ + chunkSize) / sectionSize
+ if (matchesOnDimensionZ <= 0) return emptyList()
+
+ /*
+ * Now we need to find the first id within the matches,
+ * and then return the subsequent matches in a rectangle following it.
+ *
+ * On each dimension, get the distance to the first match, which is equal to (sectionSize-1 - modX)
+ * and add it to the coordinate value
+ */
+ val firstX = x + (sectionSize - 1 - modX)
+ val firstZ = z + (sectionSize - 1 - modZ)
+
+ val firstIdX = (firstX + 1) / sectionSize + 1
+ val firstIdZ = (firstZ + 1) / sectionSize + 1
+
+ if (matchesOnDimensionX == 1 && matchesOnDimensionZ == 1) {
+ // fast-path optimization
+ return listOf(Vec2i(firstIdX, firstIdZ))
+ }
+
+ return (0 until matchesOnDimensionX).flatMap { idOffsetX ->
+ (0 until matchesOnDimensionZ).map { idOffsetZ -> Vec2i(firstIdX + idOffsetX, firstIdZ + idOffsetZ) }
+ }
+ }
+
+ }
+
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/GlobalPrivilegesManagerImpl.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/GlobalPrivilegesManagerImpl.kt
index 769cee6..670dd94 100644
--- a/src/main/kotlin/io/dico/parcels2/defaultimpl/GlobalPrivilegesManagerImpl.kt
+++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/GlobalPrivilegesManagerImpl.kt
@@ -1,28 +1,28 @@
-@file:Suppress("UNCHECKED_CAST")
-
-package io.dico.parcels2.defaultimpl
-
-import io.dico.parcels2.*
-import io.dico.parcels2.util.ext.alsoIfTrue
-import java.util.Collections
-
-class GlobalPrivilegesManagerImpl(val plugin: ParcelsPlugin) : GlobalPrivilegesManager {
- private val map = mutableMapOf<PlayerProfile, GlobalPrivileges>()
-
- override fun get(owner: PlayerProfile.Real): GlobalPrivileges {
- return map[owner] ?: GlobalPrivilegesImpl(owner).also { map[owner] = it }
- }
-
- private inner class GlobalPrivilegesImpl(override val keyOfOwner: PlayerProfile.Real) : PrivilegesHolder(), GlobalPrivileges {
- override var privilegeOfStar: Privilege
- get() = super<GlobalPrivileges>.privilegeOfStar
- set(value) = run { super<GlobalPrivileges>.privilegeOfStar = value }
-
- override fun setRawStoredPrivilege(key: PrivilegeKey, privilege: Privilege): Boolean {
- return super.setRawStoredPrivilege(key, privilege).alsoIfTrue {
- plugin.storage.setGlobalPrivilege(keyOfOwner, key, privilege)
- }
- }
- }
-
+@file:Suppress("UNCHECKED_CAST")
+
+package io.dico.parcels2.defaultimpl
+
+import io.dico.parcels2.*
+import io.dico.parcels2.util.ext.alsoIfTrue
+import java.util.Collections
+
+class GlobalPrivilegesManagerImpl(val plugin: ParcelsPlugin) : GlobalPrivilegesManager {
+ private val map = mutableMapOf<PlayerProfile, GlobalPrivileges>()
+
+ override fun get(owner: PlayerProfile.Real): GlobalPrivileges {
+ return map[owner] ?: GlobalPrivilegesImpl(owner).also { map[owner] = it }
+ }
+
+ private inner class GlobalPrivilegesImpl(override val keyOfOwner: PlayerProfile.Real) : PrivilegesHolder(), GlobalPrivileges {
+ override var privilegeOfStar: Privilege
+ get() = super<GlobalPrivileges>.privilegeOfStar
+ set(value) = run { super<GlobalPrivileges>.privilegeOfStar = value }
+
+ override fun setRawStoredPrivilege(key: PrivilegeKey, privilege: Privilege): Boolean {
+ return super.setRawStoredPrivilege(key, privilege).alsoIfTrue {
+ plugin.storage.setGlobalPrivilege(keyOfOwner, key, privilege)
+ }
+ }
+ }
+
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/InfoBuilder.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/InfoBuilder.kt
index a3704c7..99df82a 100644
--- a/src/main/kotlin/io/dico/parcels2/defaultimpl/InfoBuilder.kt
+++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/InfoBuilder.kt
@@ -1,88 +1,88 @@
-package io.dico.parcels2.defaultimpl
-
-import io.dico.dicore.Formatting
-import io.dico.parcels2.Privilege
-import io.dico.parcels2.PrivilegeKey
-import io.dico.parcels2.RawPrivileges
-import io.dico.parcels2.filterProfilesWithPrivilegeTo
-
-object InfoBuilder {
- val infoStringColor1 = Formatting.GREEN
- val infoStringColor2 = Formatting.AQUA
-
- inline fun StringBuilder.appendField(field: StringBuilder.() -> Unit, value: StringBuilder.() -> Unit) {
- append(infoStringColor1)
- field()
- append(": ")
- append(infoStringColor2)
- value()
- append(' ')
- }
-
- inline fun StringBuilder.appendField(name: String, value: StringBuilder.() -> Unit) {
- appendField({ append(name) }, value)
- }
-
- inline fun StringBuilder.appendFieldWithCount(name: String, count: Int, value: StringBuilder.() -> Unit) {
- appendField({
- append(name)
- append('(')
- append(infoStringColor2)
- append(count)
- append(infoStringColor1)
- append(')')
- }, value)
- }
-
- fun StringBuilder.appendProfilesWithPrivilege(fieldName: String, local: RawPrivileges, global: RawPrivileges?, privilege: Privilege) {
- val map = linkedMapOf<PrivilegeKey, Privilege>()
- local.filterProfilesWithPrivilegeTo(map, privilege)
- val localCount = map.size
- global?.filterProfilesWithPrivilegeTo(map, privilege)
- appendPrivilegeProfiles(fieldName, map, localCount)
- }
-
- fun StringBuilder.appendPrivilegeProfiles(fieldName: String, map: LinkedHashMap<PrivilegeKey, Privilege>, localCount: Int) {
- if (map.isEmpty()) return
-
- appendFieldWithCount(fieldName, map.size) {
- // first [localCount] entries are local
- val separator = "$infoStringColor1, $infoStringColor2"
- val iterator = map.iterator()
-
- if (localCount != 0) {
- appendPrivilegeEntry(false, iterator.next().toPair())
- repeat(localCount - 1) {
- append(separator)
- appendPrivilegeEntry(false, iterator.next().toPair())
- }
-
- } else if (iterator.hasNext()) {
- // ensure there is never a leading or trailing separator
- appendPrivilegeEntry(true, iterator.next().toPair())
- }
-
- iterator.forEach { next ->
- append(separator)
- appendPrivilegeEntry(true, next.toPair())
- }
- }
- }
-
- fun StringBuilder.appendPrivilegeEntry(global: Boolean, pair: Pair<PrivilegeKey, Privilege>) {
- val (key, priv) = pair
-
- append(key.notNullName)
-
- // suffix. Maybe T should be M for mod or something. T means they have CAN_MANAGE privilege.
- append(
- when {
- global && priv == Privilege.CAN_MANAGE -> " (G) (T)"
- global -> " (G)"
- priv == Privilege.CAN_MANAGE -> " (T)"
- else -> ""
- }
- )
- }
-
+package io.dico.parcels2.defaultimpl
+
+import io.dico.dicore.Formatting
+import io.dico.parcels2.Privilege
+import io.dico.parcels2.PrivilegeKey
+import io.dico.parcels2.RawPrivileges
+import io.dico.parcels2.filterProfilesWithPrivilegeTo
+
+object InfoBuilder {
+ val infoStringColor1 = Formatting.GREEN
+ val infoStringColor2 = Formatting.AQUA
+
+ inline fun StringBuilder.appendField(field: StringBuilder.() -> Unit, value: StringBuilder.() -> Unit) {
+ append(infoStringColor1)
+ field()
+ append(": ")
+ append(infoStringColor2)
+ value()
+ append(' ')
+ }
+
+ inline fun StringBuilder.appendField(name: String, value: StringBuilder.() -> Unit) {
+ appendField({ append(name) }, value)
+ }
+
+ inline fun StringBuilder.appendFieldWithCount(name: String, count: Int, value: StringBuilder.() -> Unit) {
+ appendField({
+ append(name)
+ append('(')
+ append(infoStringColor2)
+ append(count)
+ append(infoStringColor1)
+ append(')')
+ }, value)
+ }
+
+ fun StringBuilder.appendProfilesWithPrivilege(fieldName: String, local: RawPrivileges, global: RawPrivileges?, privilege: Privilege) {
+ val map = linkedMapOf<PrivilegeKey, Privilege>()
+ local.filterProfilesWithPrivilegeTo(map, privilege)
+ val localCount = map.size
+ global?.filterProfilesWithPrivilegeTo(map, privilege)
+ appendPrivilegeProfiles(fieldName, map, localCount)
+ }
+
+ fun StringBuilder.appendPrivilegeProfiles(fieldName: String, map: LinkedHashMap<PrivilegeKey, Privilege>, localCount: Int) {
+ if (map.isEmpty()) return
+
+ appendFieldWithCount(fieldName, map.size) {
+ // first [localCount] entries are local
+ val separator = "$infoStringColor1, $infoStringColor2"
+ val iterator = map.iterator()
+
+ if (localCount != 0) {
+ appendPrivilegeEntry(false, iterator.next().toPair())
+ repeat(localCount - 1) {
+ append(separator)
+ appendPrivilegeEntry(false, iterator.next().toPair())
+ }
+
+ } else if (iterator.hasNext()) {
+ // ensure there is never a leading or trailing separator
+ appendPrivilegeEntry(true, iterator.next().toPair())
+ }
+
+ iterator.forEach { next ->
+ append(separator)
+ appendPrivilegeEntry(true, next.toPair())
+ }
+ }
+ }
+
+ fun StringBuilder.appendPrivilegeEntry(global: Boolean, pair: Pair<PrivilegeKey, Privilege>) {
+ val (key, priv) = pair
+
+ append(key.notNullName)
+
+ // suffix. Maybe T should be M for mod or something. T means they have CAN_MANAGE privilege.
+ append(
+ when {
+ global && priv == Privilege.CAN_MANAGE -> " (G) (T)"
+ global -> " (G)"
+ priv == Privilege.CAN_MANAGE -> " (T)"
+ else -> ""
+ }
+ )
+ }
+
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt
index 48a7fee..da004d6 100644
--- a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt
+++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelProviderImpl.kt
@@ -1,223 +1,223 @@
-package io.dico.parcels2.defaultimpl
-
-import io.dico.parcels2.*
-import io.dico.parcels2.blockvisitor.Schematic
-import io.dico.parcels2.util.schedule
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.launch
-import org.bukkit.Bukkit
-import org.bukkit.WorldCreator
-import org.joda.time.DateTime
-
-class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider {
- inline val options get() = plugin.options
- override val worlds: Map<String, ParcelWorld> get() = _worlds
- private val _worlds: MutableMap<String, ParcelWorld> = hashMapOf()
- private val _generators: MutableMap<String, ParcelGenerator> = hashMapOf()
- private var _worldsLoaded = false
- private var _dataIsLoaded = false
-
- // disabled while !_dataIsLoaded. getParcelById() will work though for data loading.
- override fun getWorld(name: String): ParcelWorld? = _worlds[name]?.takeIf { _dataIsLoaded }
-
- override fun getWorldById(id: ParcelWorldId): ParcelWorld? {
- if (id is ParcelWorld) return id
- return _worlds[id.name] ?: id.bukkitWorld?.let { getWorld(it) }
- }
-
- override fun getParcelById(id: ParcelId): Parcel? {
- if (id is Parcel) return id
- return getWorldById(id.worldId)?.container?.getParcelById(id.x, id.z)
- }
-
- override fun getWorldGenerator(worldName: String): ParcelGenerator? {
- return _worlds[worldName]?.generator
- ?: _generators[worldName]
- ?: options.worlds[worldName]?.generator?.newInstance(worldName)?.also { _generators[worldName] = it }
- }
-
- override fun loadWorlds() {
- if (_worldsLoaded) throw IllegalStateException()
- _worldsLoaded = true
- loadWorlds0()
- }
-
- private fun loadWorlds0() {
- if (Bukkit.getWorlds().isEmpty()) {
- plugin.schedule(::loadWorlds0)
- plugin.logger.warning("Scheduling to load worlds in the next tick because no bukkit worlds are loaded yet")
- return
- }
-
- val newlyCreatedWorlds = mutableListOf<ParcelWorld>()
- for ((worldName, worldOptions) in options.worlds.entries) {
- var parcelWorld = _worlds[worldName]
- if (parcelWorld != null) continue
-
- val generator: ParcelGenerator = getWorldGenerator(worldName)!!
- val worldExists = Bukkit.getWorld(worldName) != null
- val bukkitWorld =
- if (worldExists) Bukkit.getWorld(worldName)!!
- else {
- logger.info("Creating world $worldName")
- WorldCreator(worldName).generator(generator).createWorld()
- }
-
- parcelWorld = ParcelWorldImpl(plugin, bukkitWorld, generator, worldOptions.runtime,::DefaultParcelContainer)
-
- if (!worldExists) {
- val time = DateTime.now()
- plugin.storage.setWorldCreationTime(parcelWorld.id, time)
- parcelWorld.creationTime = time
- newlyCreatedWorlds.add(parcelWorld)
- } else {
- GlobalScope.launch(context = Dispatchers.Unconfined) {
- parcelWorld.creationTime = plugin.storage.getWorldCreationTime(parcelWorld.id).await() ?: DateTime.now()
- }
- }
-
- _worlds[worldName] = parcelWorld
- }
-
- loadStoredData(newlyCreatedWorlds.toSet())
- }
-
- private fun loadStoredData(newlyCreatedWorlds: Collection<ParcelWorld> = emptyList()) {
- plugin.launch(Dispatchers.Default) {
- val migration = plugin.options.migration
- if (migration.enabled) {
- migration.instance?.newInstance()?.apply {
- logger.warn("Migrating database now...")
- migrateTo(plugin.storage).join()
- logger.warn("Migration completed")
-
- if (migration.disableWhenComplete) {
- migration.enabled = false
- plugin.saveOptions()
- }
- }
- }
-
- logger.info("Loading all parcel data...")
-
- val job1 = launch {
- val channel = plugin.storage.transmitAllParcelData()
- while (true) {
- val (id, data) = channel.receiveOrNull() ?: break
- val parcel = getParcelById(id) ?: continue
- data?.let { parcel.copyData(it, callerIsDatabase = true) }
- }
- }
-
- val channel2 = plugin.storage.transmitAllGlobalPrivileges()
- while (true) {
- val (profile, data) = channel2.receiveOrNull() ?: break
- if (profile !is PrivilegeKey) {
- logger.error("Received profile that is not a privilege key: ${profile.javaClass}, $profile")
- continue
- }
- (plugin.globalPrivileges[profile] as PrivilegesHolder).copyPrivilegesFrom(data)
- }
-
- job1.join()
-
- logger.info("Loading data completed")
- _dataIsLoaded = true
- }
- }
-
- override fun acquireBlockVisitorPermit(parcelId: ParcelId, with: Permit): Boolean {
- val parcel = getParcelById(parcelId) as? ParcelImpl ?: return true
- return parcel.acquireBlockVisitorPermit(with)
- }
-
- override fun releaseBlockVisitorPermit(parcelId: ParcelId, with: Permit) {
- val parcel = getParcelById(parcelId) as? ParcelImpl ?: return
- parcel.releaseBlockVisitorPermit(with)
- }
-
- override fun trySubmitBlockVisitor(permit: Permit, vararg parcelIds: ParcelId, function: JobFunction): Job? {
- val withPermit = parcelIds.filter { acquireBlockVisitorPermit(it, permit) }
- if (withPermit.size != parcelIds.size) {
- withPermit.forEach { releaseBlockVisitorPermit(it, permit) }
- return null
- }
-
- val job = plugin.jobDispatcher.dispatch(function)
-
- plugin.launch {
- job.awaitCompletion()
- withPermit.forEach { releaseBlockVisitorPermit(it, permit) }
- }
-
- return job
- }
-
- override fun swapParcels(parcelId1: ParcelId, parcelId2: ParcelId): Job? {
- val blockManager1 = getWorldById(parcelId1.worldId)?.blockManager ?: return null
- val blockManager2 = getWorldById(parcelId2.worldId)?.blockManager ?: return null
-
- return trySubmitBlockVisitor(Permit(), parcelId1, parcelId2) {
- var region1 = blockManager1.getRegion(parcelId1)
- var region2 = blockManager2.getRegion(parcelId2)
-
- val size = region1.size.clampMax(region2.size)
- if (size != region1.size) {
- region1 = region1.withSize(size)
- region2 = region2.withSize(size)
- }
-
- val schematicOf1 = delegateWork(0.25) { Schematic().apply { load(blockManager1.world, region1) } }
- val schematicOf2 = delegateWork(0.25) { Schematic().apply { load(blockManager2.world, region2) } }
- delegateWork(0.25) { with(schematicOf1) { paste(blockManager2.world, region2.origin) } }
- delegateWork(0.25) { with(schematicOf2) { paste(blockManager1.world, region1.origin) } }
- }
- }
-
- /*
- fun loadWorlds(options: Options) {
- for ((worldName, worldOptions) in options.worlds.entries) {
- val world: ParcelWorld
- try {
-
- world = ParcelWorldImpl(
- worldName,
- worldOptions,
- worldOptions.generator.newGenerator(this, worldName),
- plugin.storage,
- plugin.globalPrivileges,
- ::DefaultParcelContainer)
-
- } catch (ex: Exception) {
- ex.printStackTrace()
- continue
- }
-
- _worlds[worldName] = world
- }
-
- plugin.functionHelper.schedule(10) {
- println("Parcels generating parcelProvider now")
- for ((name, world) in _worlds) {
- if (Bukkit.getWorld(name) == null) {
- val bworld = WorldCreator(name).generator(world.generator).createWorld()
- val spawn = world.generator.getFixedSpawnLocation(bworld, null)
- bworld.setSpawnLocation(spawn.x.floor(), spawn.y.floor(), spawn.z.floor())
- }
- }
-
- val channel = plugin.storage.transmitAllParcelData()
- val job = plugin.functionHelper.launchLazilyOnMainThread {
- do {
- val pair = channel.receiveOrNull() ?: break
- val parcel = getParcelById(pair.first) ?: continue
- pair.second?.let { parcel.copyDataIgnoringDatabase(it) }
- } while (true)
- }
- job.start()
- }
-
- }
- */
+package io.dico.parcels2.defaultimpl
+
+import io.dico.parcels2.*
+import io.dico.parcels2.blockvisitor.Schematic
+import io.dico.parcels2.util.schedule
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.launch
+import org.bukkit.Bukkit
+import org.bukkit.WorldCreator
+import org.joda.time.DateTime
+
+class ParcelProviderImpl(val plugin: ParcelsPlugin) : ParcelProvider {
+ inline val options get() = plugin.options
+ override val worlds: Map<String, ParcelWorld> get() = _worlds
+ private val _worlds: MutableMap<String, ParcelWorld> = hashMapOf()
+ private val _generators: MutableMap<String, ParcelGenerator> = hashMapOf()
+ private var _worldsLoaded = false
+ private var _dataIsLoaded = false
+
+ // disabled while !_dataIsLoaded. getParcelById() will work though for data loading.
+ override fun getWorld(name: String): ParcelWorld? = _worlds[name]?.takeIf { _dataIsLoaded }
+
+ override fun getWorldById(id: ParcelWorldId): ParcelWorld? {
+ if (id is ParcelWorld) return id
+ return _worlds[id.name] ?: id.bukkitWorld?.let { getWorld(it) }
+ }
+
+ override fun getParcelById(id: ParcelId): Parcel? {
+ if (id is Parcel) return id
+ return getWorldById(id.worldId)?.container?.getParcelById(id.x, id.z)
+ }
+
+ override fun getWorldGenerator(worldName: String): ParcelGenerator? {
+ return _worlds[worldName]?.generator
+ ?: _generators[worldName]
+ ?: options.worlds[worldName]?.generator?.newInstance(worldName)?.also { _generators[worldName] = it }
+ }
+
+ override fun loadWorlds() {
+ if (_worldsLoaded) throw IllegalStateException()
+ _worldsLoaded = true
+ loadWorlds0()
+ }
+
+ private fun loadWorlds0() {
+ if (Bukkit.getWorlds().isEmpty()) {
+ plugin.schedule(::loadWorlds0)
+ plugin.logger.warning("Scheduling to load worlds in the next tick because no bukkit worlds are loaded yet")
+ return
+ }
+
+ val newlyCreatedWorlds = mutableListOf<ParcelWorld>()
+ for ((worldName, worldOptions) in options.worlds.entries) {
+ var parcelWorld = _worlds[worldName]
+ if (parcelWorld != null) continue
+
+ val generator: ParcelGenerator = getWorldGenerator(worldName)!!
+ val worldExists = Bukkit.getWorld(worldName) != null
+ val bukkitWorld =
+ if (worldExists) Bukkit.getWorld(worldName)!!
+ else {
+ logger.info("Creating world $worldName")
+ WorldCreator(worldName).generator(generator).createWorld()
+ }
+
+ parcelWorld = ParcelWorldImpl(plugin, bukkitWorld, generator, worldOptions.runtime,::DefaultParcelContainer)
+
+ if (!worldExists) {
+ val time = DateTime.now()
+ plugin.storage.setWorldCreationTime(parcelWorld.id, time)
+ parcelWorld.creationTime = time
+ newlyCreatedWorlds.add(parcelWorld)
+ } else {
+ GlobalScope.launch(context = Dispatchers.Unconfined) {
+ parcelWorld.creationTime = plugin.storage.getWorldCreationTime(parcelWorld.id).await() ?: DateTime.now()
+ }
+ }
+
+ _worlds[worldName] = parcelWorld
+ }
+
+ loadStoredData(newlyCreatedWorlds.toSet())
+ }
+
+ private fun loadStoredData(newlyCreatedWorlds: Collection<ParcelWorld> = emptyList()) {
+ plugin.launch(Dispatchers.Default) {
+ val migration = plugin.options.migration
+ if (migration.enabled) {
+ migration.instance?.newInstance()?.apply {
+ logger.warn("Migrating database now...")
+ migrateTo(plugin.storage).join()
+ logger.warn("Migration completed")
+
+ if (migration.disableWhenComplete) {
+ migration.enabled = false
+ plugin.saveOptions()
+ }
+ }
+ }
+
+ logger.info("Loading all parcel data...")
+
+ val job1 = launch {
+ val channel = plugin.storage.transmitAllParcelData()
+ while (true) {
+ val (id, data) = channel.receiveOrNull() ?: break
+ val parcel = getParcelById(id) ?: continue
+ data?.let { parcel.copyData(it, callerIsDatabase = true) }
+ }
+ }
+
+ val channel2 = plugin.storage.transmitAllGlobalPrivileges()
+ while (true) {
+ val (profile, data) = channel2.receiveOrNull() ?: break
+ if (profile !is PrivilegeKey) {
+ logger.error("Received profile that is not a privilege key: ${profile.javaClass}, $profile")
+ continue
+ }
+ (plugin.globalPrivileges[profile] as PrivilegesHolder).copyPrivilegesFrom(data)
+ }
+
+ job1.join()
+
+ logger.info("Loading data completed")
+ _dataIsLoaded = true
+ }
+ }
+
+ override fun acquireBlockVisitorPermit(parcelId: ParcelId, with: Permit): Boolean {
+ val parcel = getParcelById(parcelId) as? ParcelImpl ?: return true
+ return parcel.acquireBlockVisitorPermit(with)
+ }
+
+ override fun releaseBlockVisitorPermit(parcelId: ParcelId, with: Permit) {
+ val parcel = getParcelById(parcelId) as? ParcelImpl ?: return
+ parcel.releaseBlockVisitorPermit(with)
+ }
+
+ override fun trySubmitBlockVisitor(permit: Permit, vararg parcelIds: ParcelId, function: JobFunction): Job? {
+ val withPermit = parcelIds.filter { acquireBlockVisitorPermit(it, permit) }
+ if (withPermit.size != parcelIds.size) {
+ withPermit.forEach { releaseBlockVisitorPermit(it, permit) }
+ return null
+ }
+
+ val job = plugin.jobDispatcher.dispatch(function)
+
+ plugin.launch {
+ job.awaitCompletion()
+ withPermit.forEach { releaseBlockVisitorPermit(it, permit) }
+ }
+
+ return job
+ }
+
+ override fun swapParcels(parcelId1: ParcelId, parcelId2: ParcelId): Job? {
+ val blockManager1 = getWorldById(parcelId1.worldId)?.blockManager ?: return null
+ val blockManager2 = getWorldById(parcelId2.worldId)?.blockManager ?: return null
+
+ return trySubmitBlockVisitor(Permit(), parcelId1, parcelId2) {
+ var region1 = blockManager1.getRegion(parcelId1)
+ var region2 = blockManager2.getRegion(parcelId2)
+
+ val size = region1.size.clampMax(region2.size)
+ if (size != region1.size) {
+ region1 = region1.withSize(size)
+ region2 = region2.withSize(size)
+ }
+
+ val schematicOf1 = delegateWork(0.25) { Schematic().apply { load(blockManager1.world, region1) } }
+ val schematicOf2 = delegateWork(0.25) { Schematic().apply { load(blockManager2.world, region2) } }
+ delegateWork(0.25) { with(schematicOf1) { paste(blockManager2.world, region2.origin) } }
+ delegateWork(0.25) { with(schematicOf2) { paste(blockManager1.world, region1.origin) } }
+ }
+ }
+
+ /*
+ fun loadWorlds(options: Options) {
+ for ((worldName, worldOptions) in options.worlds.entries) {
+ val world: ParcelWorld
+ try {
+
+ world = ParcelWorldImpl(
+ worldName,
+ worldOptions,
+ worldOptions.generator.newGenerator(this, worldName),
+ plugin.storage,
+ plugin.globalPrivileges,
+ ::DefaultParcelContainer)
+
+ } catch (ex: Exception) {
+ ex.printStackTrace()
+ continue
+ }
+
+ _worlds[worldName] = world
+ }
+
+ plugin.functionHelper.schedule(10) {
+ println("Parcels generating parcelProvider now")
+ for ((name, world) in _worlds) {
+ if (Bukkit.getWorld(name) == null) {
+ val bworld = WorldCreator(name).generator(world.generator).createWorld()
+ val spawn = world.generator.getFixedSpawnLocation(bworld, null)
+ bworld.setSpawnLocation(spawn.x.floor(), spawn.y.floor(), spawn.z.floor())
+ }
+ }
+
+ val channel = plugin.storage.transmitAllParcelData()
+ val job = plugin.functionHelper.launchLazilyOnMainThread {
+ do {
+ val pair = channel.receiveOrNull() ?: break
+ val parcel = getParcelById(pair.first) ?: continue
+ pair.second?.let { parcel.copyDataIgnoringDatabase(it) }
+ } while (true)
+ }
+ job.start()
+ }
+
+ }
+ */
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt
index 531a25f..6ce4f26 100644
--- a/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt
+++ b/src/main/kotlin/io/dico/parcels2/defaultimpl/ParcelWorldImpl.kt
@@ -1,75 +1,75 @@
-@file:Suppress("CanBePrimaryConstructorProperty", "UsePropertyAccessSyntax")
-
-package io.dico.parcels2.defaultimpl
-
-import io.dico.parcels2.*
-import io.dico.parcels2.options.RuntimeWorldOptions
-import io.dico.parcels2.storage.Storage
-import kotlinx.coroutines.CoroutineScope
-import org.bukkit.GameRule
-import org.bukkit.World
-import org.joda.time.DateTime
-import java.util.UUID
-
-class ParcelWorldImpl(
- val plugin: ParcelsPlugin,
- override val world: World,
- override val generator: ParcelGenerator,
- override var options: RuntimeWorldOptions,
- containerFactory: ParcelContainerFactory
-) : ParcelWorld, ParcelWorldId, ParcelContainer, ParcelLocator {
- override val id: ParcelWorldId get() = this
- override val uid: UUID? get() = world.uid
-
- override val storage get() = plugin.storage
- override val globalPrivileges get() = plugin.globalPrivileges
-
- init {
- if (generator.world != world) {
- throw IllegalArgumentException()
- }
- }
-
- override val name: String = world.name!!
- override val container: ParcelContainer = containerFactory(this)
- override val locator: ParcelLocator
- override val blockManager: ParcelBlockManager
-
- init {
- val (locator, blockManager) = generator.makeParcelLocatorAndBlockManager(plugin.parcelProvider, container, plugin, plugin.jobDispatcher)
- this.locator = locator
- this.blockManager = blockManager
- enforceOptions()
- }
-
- fun enforceOptions() {
- if (options.dayTime) {
- world.setGameRule(GameRule.DO_DAYLIGHT_CYCLE, false)
- world.setTime(6000)
- }
-
- if (options.noWeather) {
- world.setStorm(false)
- world.setThundering(false)
- world.weatherDuration = Int.MAX_VALUE
- }
-
- world.setGameRule(GameRule.DO_TILE_DROPS, options.doTileDrops)
- }
-
- // Accessed by ParcelProviderImpl
- override var creationTime: DateTime? = null
-
-
- override fun getParcelAt(x: Int, z: Int): Parcel? = locator.getParcelAt(x, z)
-
- override fun getParcelIdAt(x: Int, z: Int): ParcelId? = locator.getParcelIdAt(x, z)
-
- override fun getParcelById(x: Int, z: Int): Parcel? = container.getParcelById(x, z)
-
- override fun getParcelById(id: ParcelId): Parcel? = container.getParcelById(id)
-
- override fun nextEmptyParcel(): Parcel? = container.nextEmptyParcel()
-
- override fun toString() = parcelWorldIdToString()
-}
+@file:Suppress("CanBePrimaryConstructorProperty", "UsePropertyAccessSyntax")
+
+package io.dico.parcels2.defaultimpl
+
+import io.dico.parcels2.*
+import io.dico.parcels2.options.RuntimeWorldOptions
+import io.dico.parcels2.storage.Storage
+import kotlinx.coroutines.CoroutineScope
+import org.bukkit.GameRule
+import org.bukkit.World
+import org.joda.time.DateTime
+import java.util.UUID
+
+class ParcelWorldImpl(
+ val plugin: ParcelsPlugin,
+ override val world: World,
+ override val generator: ParcelGenerator,
+ override var options: RuntimeWorldOptions,
+ containerFactory: ParcelContainerFactory
+) : ParcelWorld, ParcelWorldId, ParcelContainer, ParcelLocator {
+ override val id: ParcelWorldId get() = this
+ override val uid: UUID? get() = world.uid
+
+ override val storage get() = plugin.storage
+ override val globalPrivileges get() = plugin.globalPrivileges
+
+ init {
+ if (generator.world != world) {
+ throw IllegalArgumentException()
+ }
+ }
+
+ override val name: String = world.name!!
+ override val container: ParcelContainer = containerFactory(this)
+ override val locator: ParcelLocator
+ override val blockManager: ParcelBlockManager
+
+ init {
+ val (locator, blockManager) = generator.makeParcelLocatorAndBlockManager(plugin.parcelProvider, container, plugin, plugin.jobDispatcher)
+ this.locator = locator
+ this.blockManager = blockManager
+ enforceOptions()
+ }
+
+ fun enforceOptions() {
+ if (options.dayTime) {
+ world.setGameRule(GameRule.DO_DAYLIGHT_CYCLE, false)
+ world.setTime(6000)
+ }
+
+ if (options.noWeather) {
+ world.setStorm(false)
+ world.setThundering(false)
+ world.weatherDuration = Int.MAX_VALUE
+ }
+
+ world.setGameRule(GameRule.DO_TILE_DROPS, options.doTileDrops)
+ }
+
+ // Accessed by ParcelProviderImpl
+ override var creationTime: DateTime? = null
+
+
+ override fun getParcelAt(x: Int, z: Int): Parcel? = locator.getParcelAt(x, z)
+
+ override fun getParcelIdAt(x: Int, z: Int): ParcelId? = locator.getParcelIdAt(x, z)
+
+ override fun getParcelById(x: Int, z: Int): Parcel? = container.getParcelById(x, z)
+
+ override fun getParcelById(id: ParcelId): Parcel? = container.getParcelById(id)
+
+ override fun nextEmptyParcel(): Parcel? = container.nextEmptyParcel()
+
+ override fun toString() = parcelWorldIdToString()
+}
diff --git a/src/main/kotlin/io/dico/parcels2/listener/ParcelEntityTracker.kt b/src/main/kotlin/io/dico/parcels2/listener/ParcelEntityTracker.kt
index 198e0e7..78b8b03 100644
--- a/src/main/kotlin/io/dico/parcels2/listener/ParcelEntityTracker.kt
+++ b/src/main/kotlin/io/dico/parcels2/listener/ParcelEntityTracker.kt
@@ -1,61 +1,61 @@
-package io.dico.parcels2.listener
-
-import io.dico.parcels2.Parcel
-import io.dico.parcels2.ParcelProvider
-import io.dico.parcels2.util.ext.editLoop
-import org.bukkit.entity.Entity
-
-class ParcelEntityTracker(val parcelProvider: ParcelProvider) {
- val map = mutableMapOf<Entity, Parcel?>()
-
- fun untrack(entity: Entity) {
- map.remove(entity)
- }
-
- fun track(entity: Entity, parcel: Parcel?) {
- map[entity] = parcel
- }
-
- /*
- * Tracks entities. If the entity is dead, they are removed from the collection.
- * If the entity is found to have left the parcel it was created in, it will be removed from the world and from the list.
- * If it is still in the parcel it was created in, and it is on the ground, it is removed from the list.
- *
- * Start after 5 seconds, run every 0.25 seconds
- */
- fun tick() {
- map.editLoop { entity, parcel ->
- if (entity.isDead) {
- remove(); return@editLoop
- }
-
- if (parcel != null && parcel.hasBlockVisitors) {
- remove()
-
- val newParcel = parcelProvider.getParcelAt(entity.location)
- if (newParcel !== parcel && (newParcel == null || !newParcel.hasBlockVisitors)) {
- entity.remove()
- }
-
- return@editLoop
- }
-
- val newParcel = parcelProvider.getParcelAt(entity.location)
- if (newParcel !== parcel && (newParcel == null || !newParcel.hasBlockVisitors)) {
- remove()
- entity.remove()
- }
- }
- }
-
- fun swapParcels(parcel1: Parcel, parcel2: Parcel) {
- map.editLoop { ->
- if (value === parcel1) {
- value = parcel2
- } else if (value === parcel2) {
- value = parcel1
- }
- }
- }
-
+package io.dico.parcels2.listener
+
+import io.dico.parcels2.Parcel
+import io.dico.parcels2.ParcelProvider
+import io.dico.parcels2.util.ext.editLoop
+import org.bukkit.entity.Entity
+
+class ParcelEntityTracker(val parcelProvider: ParcelProvider) {
+ val map = mutableMapOf<Entity, Parcel?>()
+
+ fun untrack(entity: Entity) {
+ map.remove(entity)
+ }
+
+ fun track(entity: Entity, parcel: Parcel?) {
+ map[entity] = parcel
+ }
+
+ /*
+ * Tracks entities. If the entity is dead, they are removed from the collection.
+ * If the entity is found to have left the parcel it was created in, it will be removed from the world and from the list.
+ * If it is still in the parcel it was created in, and it is on the ground, it is removed from the list.
+ *
+ * Start after 5 seconds, run every 0.25 seconds
+ */
+ fun tick() {
+ map.editLoop { entity, parcel ->
+ if (entity.isDead) {
+ remove(); return@editLoop
+ }
+
+ if (parcel != null && parcel.hasBlockVisitors) {
+ remove()
+
+ val newParcel = parcelProvider.getParcelAt(entity.location)
+ if (newParcel !== parcel && (newParcel == null || !newParcel.hasBlockVisitors)) {
+ entity.remove()
+ }
+
+ return@editLoop
+ }
+
+ val newParcel = parcelProvider.getParcelAt(entity.location)
+ if (newParcel !== parcel && (newParcel == null || !newParcel.hasBlockVisitors)) {
+ remove()
+ entity.remove()
+ }
+ }
+ }
+
+ fun swapParcels(parcel1: Parcel, parcel2: Parcel) {
+ map.editLoop { ->
+ if (value === parcel1) {
+ value = parcel2
+ } else if (value === parcel2) {
+ value = parcel1
+ }
+ }
+ }
+
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt b/src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt
index 9c9bdc2..8bef7d1 100644
--- a/src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt
+++ b/src/main/kotlin/io/dico/parcels2/listener/ParcelListeners.kt
@@ -1,661 +1,661 @@
-package io.dico.parcels2.listener
-
-import gnu.trove.TLongCollection
-import gnu.trove.set.hash.TLongHashSet
-import io.dico.dicore.Formatting
-import io.dico.dicore.ListenerMarker
-import io.dico.dicore.RegistratorListener
-import io.dico.parcels2.*
-import io.dico.parcels2.storage.Storage
-import io.dico.parcels2.util.ext.*
-import io.dico.parcels2.util.math.*
-import org.bukkit.Location
-import org.bukkit.Material.*
-import org.bukkit.World
-import org.bukkit.block.Biome
-import org.bukkit.block.Block
-import org.bukkit.block.data.Directional
-import org.bukkit.block.data.type.Bed
-import org.bukkit.entity.*
-import org.bukkit.entity.minecart.ExplosiveMinecart
-import org.bukkit.event.EventPriority
-import org.bukkit.event.EventPriority.NORMAL
-import org.bukkit.event.block.*
-import org.bukkit.event.entity.*
-import org.bukkit.event.hanging.HangingBreakByEntityEvent
-import org.bukkit.event.hanging.HangingBreakEvent
-import org.bukkit.event.hanging.HangingPlaceEvent
-import org.bukkit.event.inventory.InventoryInteractEvent
-import org.bukkit.event.player.*
-import org.bukkit.event.vehicle.VehicleMoveEvent
-import org.bukkit.event.weather.WeatherChangeEvent
-import org.bukkit.event.world.ChunkLoadEvent
-import org.bukkit.event.world.StructureGrowEvent
-import org.bukkit.inventory.InventoryHolder
-import java.util.EnumSet
-
-class ParcelListeners(
- val parcelProvider: ParcelProvider,
- val entityTracker: ParcelEntityTracker,
- val storage: Storage
-) {
- private fun canBuildOnArea(user: Player, area: Parcel?) =
- if (area == null) user.hasPermBuildAnywhere else area.canBuild(user)
-
- private fun canInteract(user: Player, area: Parcel?, interactClass: String) =
- canBuildOnArea(user, area) || (area != null && area.interactableConfig(interactClass))
-
- /**
- * Get the world and parcel that the block resides in
- * the parcel is nullable, and often named area because that means path.
- * returns null if not in a registered parcel world - should always return in that case to not affect other worlds.
- */
- private fun getWorldAndArea(block: Block): Pair<ParcelWorld, Parcel?>? {
- val world = parcelProvider.getWorld(block.world) ?: return null
- return world to world.getParcelAt(block)
- }
-
-
- /*
- * Prevents players from entering plots they are banned from
- */
- @field:ListenerMarker(priority = NORMAL)
- val onPlayerMoveEvent = RegistratorListener<PlayerMoveEvent> l@{ event ->
- val user = event.player
- if (user.hasPermBanBypass) return@l
- val toLoc = event.to
- val parcel = parcelProvider.getParcelAt(toLoc) ?: return@l
-
- if (!parcel.canEnterFast(user)) {
- val region = parcel.world.blockManager.getRegion(parcel.id)
- val dimension = region.getFirstUncontainedDimensionOf(Vec3i(event.from))
-
- if (dimension == null) {
- user.teleport(parcel.homeLocation)
- user.sendParcelMessage(nopermit = true, message = "You are banned from this parcel")
-
- } else {
- val speed = getPlayerSpeed(user)
- val from = Vec3d(event.from)
- val to = Vec3d(toLoc).with(dimension, from[dimension])
-
- var newTo = to
- dimension.otherDimensions.forEach {
- val delta = to[it] - from[it]
- newTo = newTo.add(it, delta * 100 * if (it == Dimension.Y) 0.5 else speed)
- }
-
- event.to = Location(
- toLoc.world,
- newTo.x, newTo.y.clampMin(0.0).clampMax(255.0), newTo.z,
- toLoc.yaw, toLoc.pitch
- )
- }
- }
- }
-
- /*
- * Prevents players from breaking blocks outside of their parcels
- * Prevents containers from dropping their contents when broken, if configured
- */
- @field:ListenerMarker(priority = NORMAL)
- val onBlockBreakEvent = RegistratorListener<BlockBreakEvent> l@{ event ->
- val (world, area) = getWorldAndArea(event.block) ?: return@l
- if (!canBuildOnArea(event.player, area)) {
- event.isCancelled = true; return@l
- }
-
- if (!world.options.dropEntityItems) {
- val state = event.block.state
- if (state is InventoryHolder) {
- state.inventory.clear()
- state.update()
- }
- }
- }
-
- /*
- * Prevents players from placing blocks outside of their parcels
- */
- @field:ListenerMarker(priority = NORMAL)
- val onBlockPlaceEvent = RegistratorListener<BlockPlaceEvent> l@{ event ->
- val (_, area) = getWorldAndArea(event.block) ?: return@l
- if (!canBuildOnArea(event.player, area)) {
- event.isCancelled = true
- }
-
- area?.updateOwnerSign()
- }
-
- /*
- * Control pistons
- */
- @field:ListenerMarker(priority = NORMAL)
- val onBlockPistonExtendEvent = RegistratorListener<BlockPistonExtendEvent> l@{ event ->
- checkPistonMovement(event, event.blocks)
- }
-
- @field:ListenerMarker(priority = NORMAL)
- val onBlockPistonRetractEvent = RegistratorListener<BlockPistonRetractEvent> l@{ event ->
- checkPistonMovement(event, event.blocks)
- }
-
- // Doing some unnecessary optimizations here..
- //@formatter:off
- private inline fun Column(x: Int, z: Int): Long = x.toLong() or (z.toLong().shl(32))
-
- private inline val Long.columnX get() = and(0xFFFF_FFFFL).toInt()
- private inline val Long.columnZ get() = ushr(32).and(0xFFFF_FFFFL).toInt()
- private inline fun TLongCollection.troveForEach(block: (Long) -> Unit) = iterator().let { while (it.hasNext()) block(it.next()) }
- //@formatter:on
- private fun checkPistonMovement(event: BlockPistonEvent, blocks: List<Block>) {
- val world = parcelProvider.getWorld(event.block.world) ?: return
- val direction = event.direction
- val columns = TLongHashSet(blocks.size * 2)
-
- blocks.forEach {
- columns.add(Column(it.x, it.z))
- it.getRelative(direction).let { columns.add(Column(it.x, it.z)) }
- }
-
- columns.troveForEach {
- val area = world.getParcelAt(it.columnX, it.columnZ)
- if (area == null || area.hasBlockVisitors) {
- event.isCancelled = true
- return
- }
- }
- }
-
- /*
- * Prevents explosions if enabled by the configs for that world
- */
- @field:ListenerMarker(priority = NORMAL)
- val onExplosionPrimeEvent = RegistratorListener<ExplosionPrimeEvent> l@{ event ->
- val (world, area) = getWorldAndArea(event.entity.location.block) ?: return@l
- if (area != null && area.hasBlockVisitors) {
- event.radius = 0F; event.isCancelled = true
- } else if (world.options.disableExplosions) {
- event.radius = 0F
- }
- }
-
- /*
- * Prevents creepers and tnt minecarts from exploding if explosions are disabled
- */
- @field:ListenerMarker(priority = NORMAL)
- val onEntityExplodeEvent = RegistratorListener<EntityExplodeEvent> l@{ event ->
- entityTracker.untrack(event.entity)
- val world = parcelProvider.getWorld(event.entity.world) ?: return@l
- if (world.options.disableExplosions || world.getParcelAt(event.entity).let { it != null && it.hasBlockVisitors }) {
- event.isCancelled = true
- }
- }
-
- /*
- * Prevents liquids from flowing out of plots
- */
- @field:ListenerMarker(priority = NORMAL)
- val onBlockFromToEvent = RegistratorListener<BlockFromToEvent> l@{ event ->
- val (_, area) = getWorldAndArea(event.toBlock) ?: return@l
- if (area == null || area.hasBlockVisitors) event.isCancelled = true
- }
-
- private val bedTypes = EnumSet.copyOf(getMaterialsWithWoolColorPrefix("BED").toList())
- /*
- * Prevents players from placing liquids, using flint and steel, changing redstone components,
- * using inputs (unless allowed by the plot),
- * and using items disabled in the configuration for that world.
- * Prevents player from using beds in HELL or SKY biomes if explosions are disabled.
- */
- @Suppress("NON_EXHAUSTIVE_WHEN")
- @field:ListenerMarker(priority = NORMAL)
- val onPlayerInteractEvent = RegistratorListener<PlayerInteractEvent> l@{ event ->
- val user = event.player
- val world = parcelProvider.getWorld(user.world) ?: return@l
- val clickedBlock = event.clickedBlock
- val parcel = clickedBlock?.let { world.getParcelAt(it) }
-
- if (!user.hasPermBuildAnywhere && parcel != null && !parcel.canEnter(user)) {
- user.sendParcelMessage(nopermit = true, message = "You cannot interact with parcels you're banned from")
- event.isCancelled = true; return@l
- }
-
- when (event.action) {
- Action.RIGHT_CLICK_BLOCK -> run {
- val type = clickedBlock.type
-
- val interactableClass = Interactables[type]
- if (interactableClass != null && !parcel.effectiveInteractableConfig.isInteractable(type) && (parcel == null || !parcel.canBuild(user))) {
- user.sendParcelMessage(nopermit = true, message = "You cannot interact with ${interactableClass.name} here")
- event.isCancelled = true
- return@l
- }
-
- if (bedTypes.contains(type)) {
- val bed = clickedBlock.blockData as Bed
- val head = if (bed.part == Bed.Part.FOOT) clickedBlock.getRelative(bed.facing) else clickedBlock
- when (head.biome) {
- Biome.NETHER, Biome.THE_END -> {
- if (world.options.disableExplosions) {
- user.sendParcelMessage(nopermit = true, message = "You cannot use this bed because it would explode")
- event.isCancelled = true; return@l
- }
- }
- }
-
- if (!canBuildOnArea(user, parcel)) {
- user.sendParcelMessage(nopermit = true, message = "You may not sleep here")
- event.isCancelled = true; return@l
- }
- }
-
- onPlayerRightClick(event, world, parcel)
-
- if (!event.isCancelled && parcel == null) {
- world.blockManager.getParcelForInfoBlockInteraction(Vec3i(clickedBlock), type, event.blockFace)
- ?.apply { user.sendMessage(Formatting.GREEN + infoString) }
- }
- }
-
- Action.RIGHT_CLICK_AIR -> onPlayerRightClick(event, world, parcel)
- Action.PHYSICAL -> if (!canBuildOnArea(user, parcel) && !(parcel != null && parcel.interactableConfig("pressure_plates"))) {
- user.sendParcelMessage(nopermit = true, message = "You cannot use inputs in this parcel")
- event.isCancelled = true; return@l
- }
- }
- }
-
- // private val blockPlaceInteractItems = EnumSet.of(LAVA_BUCKET, WATER_BUCKET, BUCKET, FLINT_AND_STEEL)
-
- @Suppress("NON_EXHAUSTIVE_WHEN")
- private fun onPlayerRightClick(event: PlayerInteractEvent, world: ParcelWorld, parcel: Parcel?) {
- if (event.hasItem()) {
- val item = event.item.type
- if (world.options.blockedItems.contains(item)) {
- event.player.sendParcelMessage(nopermit = true, message = "You cannot use this item because it is disabled in this world")
- event.isCancelled = true; return
- }
-
- when (item) {
- LAVA_BUCKET, WATER_BUCKET, BUCKET, FLINT_AND_STEEL -> {
- val block = event.clickedBlock.getRelative(event.blockFace)
- val otherParcel = world.getParcelAt(block)
- if (!canBuildOnArea(event.player, otherParcel)) {
- event.isCancelled = true
- }
- }
- }
- }
- }
-
- /*
- * Prevents players from breeding mobs, entering or opening boats/minecarts,
- * rotating item frames, doing stuff with leashes, and putting stuff on armor stands.
- */
- @Suppress("NON_EXHAUSTIVE_WHEN")
- @field:ListenerMarker(priority = NORMAL)
- val onPlayerInteractEntityEvent = RegistratorListener<PlayerInteractEntityEvent> l@{ event ->
- val (_, area) = getWorldAndArea(event.rightClicked.location.block) ?: return@l
- if (canBuildOnArea(event.player, area)) return@l
- when (event.rightClicked.type) {
- EntityType.BOAT,
- EntityType.MINECART,
- EntityType.MINECART_CHEST,
- EntityType.MINECART_COMMAND,
- EntityType.MINECART_FURNACE,
- EntityType.MINECART_HOPPER,
- EntityType.MINECART_MOB_SPAWNER,
- EntityType.MINECART_TNT,
-
- EntityType.ARMOR_STAND,
- EntityType.PAINTING,
- EntityType.ITEM_FRAME,
- EntityType.LEASH_HITCH,
-
- EntityType.CHICKEN,
- EntityType.COW,
- EntityType.HORSE,
- EntityType.SHEEP,
- EntityType.VILLAGER,
- EntityType.WOLF -> event.isCancelled = true
- }
- }
-
- /*
- * Prevents endermen from griefing.
- * Prevents sand blocks from exiting the parcel in which they became an entity.
- */
- @field:ListenerMarker(priority = NORMAL)
- val onEntityChangeBlockEvent = RegistratorListener<EntityChangeBlockEvent> l@{ event ->
- val (_, area) = getWorldAndArea(event.block) ?: return@l
- if (event.entity.type == EntityType.ENDERMAN || area == null || area.hasBlockVisitors) {
- event.isCancelled = true; return@l
- }
-
- if (event.entity.type == EntityType.FALLING_BLOCK) {
- // a sand block started falling. Track it and delete it if it gets out of this parcel.
- entityTracker.track(event.entity, area)
- }
- }
-
- /*
- * Prevents portals from being created if set so in the configs for that world
- */
- @field:ListenerMarker(priority = NORMAL)
- val onEntityCreatePortalEvent = RegistratorListener<EntityCreatePortalEvent> l@{ event ->
- val world = parcelProvider.getWorld(event.entity.world) ?: return@l
- if (world.options.blockPortalCreation) event.isCancelled = true
- }
-
- /*
- * Prevents players from dropping items
- */
- @field:ListenerMarker(priority = NORMAL)
- val onPlayerDropItemEvent = RegistratorListener<PlayerDropItemEvent> l@{ event ->
- val (_, area) = getWorldAndArea(event.itemDrop.location.block) ?: return@l
- if (!canInteract(event.player, area, "containers")) event.isCancelled = true
- }
-
- /*
- * Prevents players from picking up items
- */
- @field:ListenerMarker(priority = NORMAL)
- val onEntityPickupItemEvent = RegistratorListener<EntityPickupItemEvent> l@{ event ->
- val user = event.entity as? Player ?: return@l
- val (_, area) = getWorldAndArea(event.item.location.block) ?: return@l
- if (!canInteract(user, area, "containers")) event.isCancelled = true
- }
-
- /*
- * Prevents players from editing inventories
- */
- @field:ListenerMarker(priority = NORMAL, events = ["inventory.InventoryClickEvent", "inventory.InventoryDragEvent"])
- val onInventoryClickEvent = RegistratorListener<InventoryInteractEvent> l@{ event ->
- val user = event.whoClicked as? Player ?: return@l
- if ((event.inventory ?: return@l).holder === user) return@l // inventory null: hotbar
- val (_, area) = getWorldAndArea(event.inventory.location.block) ?: return@l
- if (!canInteract(user, area, "containers")) {
- event.isCancelled = true
- }
- }
-
- /*
- * Cancels weather changes and sets the weather to sunny if requested by the config for that world.
- */
- @field:ListenerMarker(priority = NORMAL)
- val onWeatherChangeEvent = RegistratorListener<WeatherChangeEvent> l@{ event ->
- val world = parcelProvider.getWorld(event.world) ?: return@l
- if (world.options.noWeather && event.toWeatherState()) {
- event.isCancelled = true
- }
- }
-
- private fun resetWeather(world: World) {
- world.setStorm(false)
- world.isThundering = false
- world.weatherDuration = Int.MAX_VALUE
- }
-
-// TODO: BlockFormEvent, BlockSpreadEvent, BlockFadeEvent, Fireworks
-
- /*
- * Prevents natural blocks forming
- */
- @ListenerMarker(priority = NORMAL)
- val onBlockFormEvent = RegistratorListener<BlockFormEvent> l@{ event ->
- val block = event.block
- val (world, area) = getWorldAndArea(block) ?: return@l
-
- // prevent any generation whatsoever on paths
- if (area == null) {
- event.isCancelled = true; return@l
- }
-
- val hasEntity = event is EntityBlockFormEvent
- val player = (event as? EntityBlockFormEvent)?.entity as? Player
-
- val cancel: Boolean = when (event.newState.type) {
-
- // prevent ice generation from Frost Walkers enchantment
- FROSTED_ICE -> player != null && !area.canBuild(player)
-
- // prevent snow generation from weather
- SNOW -> !hasEntity && world.options.preventWeatherBlockChanges
-
- else -> false
- }
-
- if (cancel) {
- event.isCancelled = true
- }
- }
-
- /*
- * Prevents mobs (living entities) from spawning if that is disabled for that world in the config.
- */
- @field:ListenerMarker(priority = NORMAL)
- val onEntitySpawnEvent = RegistratorListener<EntitySpawnEvent> l@{ event ->
- val world = parcelProvider.getWorld(event.entity.world) ?: return@l
- if (event.entity is Creature && world.options.blockMobSpawning) {
- event.isCancelled = true
- } else if (world.getParcelAt(event.entity).let { it != null && it.hasBlockVisitors }) {
- event.isCancelled = true
- }
- }
-
- /*
- * Prevents minecarts/boats from moving outside a plot
- */
- @field:ListenerMarker(priority = NORMAL)
- val onVehicleMoveEvent = RegistratorListener<VehicleMoveEvent> l@{ event ->
- val (_, area) = getWorldAndArea(event.to.block) ?: return@l
- if (area == null) {
- event.vehicle.passengers.forEach {
- if (it.type == EntityType.PLAYER) {
- (it as Player).sendParcelMessage(except = true, message = "Your ride ends here")
- } else it.remove()
- }
- event.vehicle.eject()
- event.vehicle.remove()
- } else if (area.hasBlockVisitors) {
- event.to.subtract(event.to).add(event.from)
- }
- }
-
- /*
- * Prevents players from removing items from item frames
- * Prevents TNT Minecarts and creepers from destroying entities (This event is called BEFORE EntityExplodeEvent GG)
- * Actually doesn't prevent this because the entities are destroyed anyway, even though the code works?
- */
- @field:ListenerMarker(priority = NORMAL)
- val onEntityDamageByEntityEvent = RegistratorListener<EntityDamageByEntityEvent> l@{ event ->
- val world = parcelProvider.getWorld(event.entity.world) ?: return@l
- if (world.options.disableExplosions && event.damager is ExplosiveMinecart || event.damager is Creeper) {
- event.isCancelled = true; return@l
- }
-
- val user = event.damager as? Player
- ?: (event.damager as? Projectile)?.let { it.shooter as? Player }
- ?: return@l
-
- if (!canBuildOnArea(user, world.getParcelAt(event.entity))) {
- event.isCancelled = true
- }
- }
-
- @field:ListenerMarker(priority = NORMAL)
- val onHangingBreakEvent = RegistratorListener<HangingBreakEvent> l@{ event ->
- val world = parcelProvider.getWorld(event.entity.world) ?: return@l
- if (event.cause == HangingBreakEvent.RemoveCause.EXPLOSION && world.options.disableExplosions) {
- event.isCancelled = true; return@l
- }
-
- if (world.getParcelAt(event.entity).let { it != null && it.hasBlockVisitors }) {
- event.isCancelled = true
- }
- }
-
- /*
- * Prevents players from deleting paintings and item frames
- * This appears to take care of shooting with a bow, throwing snowballs or throwing ender pearls.
- */
- @field:ListenerMarker(priority = NORMAL)
- val onHangingBreakByEntityEvent = RegistratorListener<HangingBreakByEntityEvent> l@{ event ->
- val world = parcelProvider.getWorld(event.entity.world) ?: return@l
- val user = event.remover as? Player ?: return@l
- if (!canBuildOnArea(user, world.getParcelAt(event.entity))) {
- event.isCancelled = true
- }
- }
-
- /*
- * Prevents players from placing paintings and item frames
- */
- @field:ListenerMarker(priority = NORMAL)
- val onHangingPlaceEvent = RegistratorListener<HangingPlaceEvent> l@{ event ->
- val world = parcelProvider.getWorld(event.entity.world) ?: return@l
- val block = event.block.getRelative(event.blockFace)
- if (!canBuildOnArea(event.player, world.getParcelAt(block))) {
- event.isCancelled = true
- }
- }
-
- /*
- * Prevents stuff from growing outside of plots
- */
- @field:ListenerMarker(priority = NORMAL)
- val onStructureGrowEvent = RegistratorListener<StructureGrowEvent> l@{ event ->
- val (world, area) = getWorldAndArea(event.location.block) ?: return@l
- if (area == null) {
- event.isCancelled = true; return@l
- }
-
- if (!event.player.hasPermBuildAnywhere && !area.canBuild(event.player)) {
- event.isCancelled = true; return@l
- }
-
- event.blocks.removeIf { world.getParcelAt(it.block) !== area }
- }
-
- /*
- * Prevents dispensers/droppers from dispensing out of parcels
- */
- @field:ListenerMarker(priority = NORMAL)
- val onBlockDispenseEvent = RegistratorListener<BlockDispenseEvent> l@{ event ->
- val block = event.block
- if (!block.type.let { it == DISPENSER || it == DROPPER }) return@l
- val world = parcelProvider.getWorld(block.world) ?: return@l
- val data = block.blockData as Directional
- val targetBlock = block.getRelative(data.facing)
- if (world.getParcelAt(targetBlock) == null) {
- event.isCancelled = true
- }
- }
-
- /*
- * Track spawned items, making sure they don't leave the parcel.
- */
- @field:ListenerMarker(priority = NORMAL)
- val onItemSpawnEvent = RegistratorListener<ItemSpawnEvent> l@{ event ->
- val (_, area) = getWorldAndArea(event.location.block) ?: return@l
- if (area == null) event.isCancelled = true
- else entityTracker.track(event.entity, area)
- }
-
- /*
- * Prevents endermen and endermite from teleporting outside their parcel
- */
- @field:ListenerMarker(priority = NORMAL)
- val onEntityTeleportEvent = RegistratorListener<EntityTeleportEvent> l@{ event ->
- val (world, area) = getWorldAndArea(event.from.block) ?: return@l
- if (area !== world.getParcelAt(event.to)) {
- event.isCancelled = true
- }
- }
-
- /*
- * Prevents projectiles from flying out of parcels
- * Prevents players from firing projectiles if they cannot build
- */
- @field:ListenerMarker(priority = NORMAL)
- val onProjectileLaunchEvent = RegistratorListener<ProjectileLaunchEvent> l@{ event ->
- val (_, area) = getWorldAndArea(event.entity.location.block) ?: return@l
- if (area == null || (event.entity.shooter as? Player)?.let { !canBuildOnArea(it, area) } == true) {
- event.isCancelled = true
- } else {
- entityTracker.track(event.entity, area)
- }
- }
-
- /*
- * Prevents entities from dropping items upon death, if configured that way
- */
- @field:ListenerMarker(priority = NORMAL)
- val onEntityDeathEvent = RegistratorListener<EntityDeathEvent> l@{ event ->
- entityTracker.untrack(event.entity)
- val world = parcelProvider.getWorld(event.entity.world) ?: return@l
- if (!world.options.dropEntityItems) {
- event.drops.clear()
- event.droppedExp = 0
- }
- }
-
- /*
- * Assigns players their default game mode upon entering the world
- */
- @field:ListenerMarker(priority = NORMAL)
- val onPlayerChangedWorldEvent = RegistratorListener<PlayerChangedWorldEvent> l@{ event ->
- val world = parcelProvider.getWorld(event.player.world) ?: return@l
- if (world.options.gameMode != null && !event.player.hasPermGamemodeBypass) {
- event.player.gameMode = world.options.gameMode
- }
- }
-
- /**
- * Updates owner signs of parcels that get loaded if it is marked outdated
- */
- @ListenerMarker(priority = EventPriority.NORMAL)
- val onChunkLoadEvent = RegistratorListener<ChunkLoadEvent> l@{ event ->
- val world = parcelProvider.getWorld(event.chunk.world) ?: return@l
- val parcels = world.blockManager.getParcelsWithOwnerBlockIn(event.chunk)
- if (parcels.isEmpty()) return@l
-
- parcels.forEach { id ->
- val parcel = world.getParcelById(id)?.takeIf { it.isOwnerSignOutdated } ?: return@forEach
- world.blockManager.updateParcelInfo(parcel.id, parcel.owner)
- parcel.isOwnerSignOutdated = false
- }
-
- }
-
- @ListenerMarker
- val onPlayerJoinEvent = RegistratorListener<PlayerJoinEvent> l@{ event ->
- storage.updatePlayerName(event.player.uuid, event.player.name)
- }
-
- /**
- * Attempts to prevent redstone contraptions from breaking while they are being swapped
- * Might remove if it causes lag
- */
- @ListenerMarker
- val onBlockRedstoneEvent = RegistratorListener<BlockRedstoneEvent> l@{ event ->
- val (_, area) = getWorldAndArea(event.block) ?: return@l
- if (area == null || area.hasBlockVisitors) {
- event.newCurrent = event.oldCurrent
- }
- }
-
-
- private fun getPlayerSpeed(player: Player): Double =
- if (player.isFlying) {
- player.flySpeed * if (player.isSprinting) 21.6 else 10.92
- } else {
- player.walkSpeed * when {
- player.isSprinting -> 5.612
- player.isSneaking -> 1.31
- else -> 4.317
- } / 1.5 //?
- } / 20.0
-
+package io.dico.parcels2.listener
+
+import gnu.trove.TLongCollection
+import gnu.trove.set.hash.TLongHashSet
+import io.dico.dicore.Formatting
+import io.dico.dicore.ListenerMarker
+import io.dico.dicore.RegistratorListener
+import io.dico.parcels2.*
+import io.dico.parcels2.storage.Storage
+import io.dico.parcels2.util.ext.*
+import io.dico.parcels2.util.math.*
+import org.bukkit.Location
+import org.bukkit.Material.*
+import org.bukkit.World
+import org.bukkit.block.Biome
+import org.bukkit.block.Block
+import org.bukkit.block.data.Directional
+import org.bukkit.block.data.type.Bed
+import org.bukkit.entity.*
+import org.bukkit.entity.minecart.ExplosiveMinecart
+import org.bukkit.event.EventPriority
+import org.bukkit.event.EventPriority.NORMAL
+import org.bukkit.event.block.*
+import org.bukkit.event.entity.*
+import org.bukkit.event.hanging.HangingBreakByEntityEvent
+import org.bukkit.event.hanging.HangingBreakEvent
+import org.bukkit.event.hanging.HangingPlaceEvent
+import org.bukkit.event.inventory.InventoryInteractEvent
+import org.bukkit.event.player.*
+import org.bukkit.event.vehicle.VehicleMoveEvent
+import org.bukkit.event.weather.WeatherChangeEvent
+import org.bukkit.event.world.ChunkLoadEvent
+import org.bukkit.event.world.StructureGrowEvent
+import org.bukkit.inventory.InventoryHolder
+import java.util.EnumSet
+
+class ParcelListeners(
+ val parcelProvider: ParcelProvider,
+ val entityTracker: ParcelEntityTracker,
+ val storage: Storage
+) {
+ private fun canBuildOnArea(user: Player, area: Parcel?) =
+ if (area == null) user.hasPermBuildAnywhere else area.canBuild(user)
+
+ private fun canInteract(user: Player, area: Parcel?, interactClass: String) =
+ canBuildOnArea(user, area) || (area != null && area.interactableConfig(interactClass))
+
+ /**
+ * Get the world and parcel that the block resides in
+ * the parcel is nullable, and often named area because that means path.
+ * returns null if not in a registered parcel world - should always return in that case to not affect other worlds.
+ */
+ private fun getWorldAndArea(block: Block): Pair<ParcelWorld, Parcel?>? {
+ val world = parcelProvider.getWorld(block.world) ?: return null
+ return world to world.getParcelAt(block)
+ }
+
+
+ /*
+ * Prevents players from entering plots they are banned from
+ */
+ @field:ListenerMarker(priority = NORMAL)
+ val onPlayerMoveEvent = RegistratorListener<PlayerMoveEvent> l@{ event ->
+ val user = event.player
+ if (user.hasPermBanBypass) return@l
+ val toLoc = event.to
+ val parcel = parcelProvider.getParcelAt(toLoc) ?: return@l
+
+ if (!parcel.canEnterFast(user)) {
+ val region = parcel.world.blockManager.getRegion(parcel.id)
+ val dimension = region.getFirstUncontainedDimensionOf(Vec3i(event.from))
+
+ if (dimension == null) {
+ user.teleport(parcel.homeLocation)
+ user.sendParcelMessage(nopermit = true, message = "You are banned from this parcel")
+
+ } else {
+ val speed = getPlayerSpeed(user)
+ val from = Vec3d(event.from)
+ val to = Vec3d(toLoc).with(dimension, from[dimension])
+
+ var newTo = to
+ dimension.otherDimensions.forEach {
+ val delta = to[it] - from[it]
+ newTo = newTo.add(it, delta * 100 * if (it == Dimension.Y) 0.5 else speed)
+ }
+
+ event.to = Location(
+ toLoc.world,
+ newTo.x, newTo.y.clampMin(0.0).clampMax(255.0), newTo.z,
+ toLoc.yaw, toLoc.pitch
+ )
+ }
+ }
+ }
+
+ /*
+ * Prevents players from breaking blocks outside of their parcels
+ * Prevents containers from dropping their contents when broken, if configured
+ */
+ @field:ListenerMarker(priority = NORMAL)
+ val onBlockBreakEvent = RegistratorListener<BlockBreakEvent> l@{ event ->
+ val (world, area) = getWorldAndArea(event.block) ?: return@l
+ if (!canBuildOnArea(event.player, area)) {
+ event.isCancelled = true; return@l
+ }
+
+ if (!world.options.dropEntityItems) {
+ val state = event.block.state
+ if (state is InventoryHolder) {
+ state.inventory.clear()
+ state.update()
+ }
+ }
+ }
+
+ /*
+ * Prevents players from placing blocks outside of their parcels
+ */
+ @field:ListenerMarker(priority = NORMAL)
+ val onBlockPlaceEvent = RegistratorListener<BlockPlaceEvent> l@{ event ->
+ val (_, area) = getWorldAndArea(event.block) ?: return@l
+ if (!canBuildOnArea(event.player, area)) {
+ event.isCancelled = true
+ }
+
+ area?.updateOwnerSign()
+ }
+
+ /*
+ * Control pistons
+ */
+ @field:ListenerMarker(priority = NORMAL)
+ val onBlockPistonExtendEvent = RegistratorListener<BlockPistonExtendEvent> l@{ event ->
+ checkPistonMovement(event, event.blocks)
+ }
+
+ @field:ListenerMarker(priority = NORMAL)
+ val onBlockPistonRetractEvent = RegistratorListener<BlockPistonRetractEvent> l@{ event ->
+ checkPistonMovement(event, event.blocks)
+ }
+
+ // Doing some unnecessary optimizations here..
+ //@formatter:off
+ private inline fun Column(x: Int, z: Int): Long = x.toLong() or (z.toLong().shl(32))
+
+ private inline val Long.columnX get() = and(0xFFFF_FFFFL).toInt()
+ private inline val Long.columnZ get() = ushr(32).and(0xFFFF_FFFFL).toInt()
+ private inline fun TLongCollection.troveForEach(block: (Long) -> Unit) = iterator().let { while (it.hasNext()) block(it.next()) }
+ //@formatter:on
+ private fun checkPistonMovement(event: BlockPistonEvent, blocks: List<Block>) {
+ val world = parcelProvider.getWorld(event.block.world) ?: return
+ val direction = event.direction
+ val columns = TLongHashSet(blocks.size * 2)
+
+ blocks.forEach {
+ columns.add(Column(it.x, it.z))
+ it.getRelative(direction).let { columns.add(Column(it.x, it.z)) }
+ }
+
+ columns.troveForEach {
+ val area = world.getParcelAt(it.columnX, it.columnZ)
+ if (area == null || area.hasBlockVisitors) {
+ event.isCancelled = true
+ return
+ }
+ }
+ }
+
+ /*
+ * Prevents explosions if enabled by the configs for that world
+ */
+ @field:ListenerMarker(priority = NORMAL)
+ val onExplosionPrimeEvent = RegistratorListener<ExplosionPrimeEvent> l@{ event ->
+ val (world, area) = getWorldAndArea(event.entity.location.block) ?: return@l
+ if (area != null && area.hasBlockVisitors) {
+ event.radius = 0F; event.isCancelled = true
+ } else if (world.options.disableExplosions) {
+ event.radius = 0F
+ }
+ }
+
+ /*
+ * Prevents creepers and tnt minecarts from exploding if explosions are disabled
+ */
+ @field:ListenerMarker(priority = NORMAL)
+ val onEntityExplodeEvent = RegistratorListener<EntityExplodeEvent> l@{ event ->
+ entityTracker.untrack(event.entity)
+ val world = parcelProvider.getWorld(event.entity.world) ?: return@l
+ if (world.options.disableExplosions || world.getParcelAt(event.entity).let { it != null && it.hasBlockVisitors }) {
+ event.isCancelled = true
+ }
+ }
+
+ /*
+ * Prevents liquids from flowing out of plots
+ */
+ @field:ListenerMarker(priority = NORMAL)
+ val onBlockFromToEvent = RegistratorListener<BlockFromToEvent> l@{ event ->
+ val (_, area) = getWorldAndArea(event.toBlock) ?: return@l
+ if (area == null || area.hasBlockVisitors) event.isCancelled = true
+ }
+
+ private val bedTypes = EnumSet.copyOf(getMaterialsWithWoolColorPrefix("BED").toList())
+ /*
+ * Prevents players from placing liquids, using flint and steel, changing redstone components,
+ * using inputs (unless allowed by the plot),
+ * and using items disabled in the configuration for that world.
+ * Prevents player from using beds in HELL or SKY biomes if explosions are disabled.
+ */
+ @Suppress("NON_EXHAUSTIVE_WHEN")
+ @field:ListenerMarker(priority = NORMAL)
+ val onPlayerInteractEvent = RegistratorListener<PlayerInteractEvent> l@{ event ->
+ val user = event.player
+ val world = parcelProvider.getWorld(user.world) ?: return@l
+ val clickedBlock = event.clickedBlock
+ val parcel = clickedBlock?.let { world.getParcelAt(it) }
+
+ if (!user.hasPermBuildAnywhere && parcel != null && !parcel.canEnter(user)) {
+ user.sendParcelMessage(nopermit = true, message = "You cannot interact with parcels you're banned from")
+ event.isCancelled = true; return@l
+ }
+
+ when (event.action) {
+ Action.RIGHT_CLICK_BLOCK -> run {
+ val type = clickedBlock.type
+
+ val interactableClass = Interactables[type]
+ if (interactableClass != null && !parcel.effectiveInteractableConfig.isInteractable(type) && (parcel == null || !parcel.canBuild(user))) {
+ user.sendParcelMessage(nopermit = true, message = "You cannot interact with ${interactableClass.name} here")
+ event.isCancelled = true
+ return@l
+ }
+
+ if (bedTypes.contains(type)) {
+ val bed = clickedBlock.blockData as Bed
+ val head = if (bed.part == Bed.Part.FOOT) clickedBlock.getRelative(bed.facing) else clickedBlock
+ when (head.biome) {
+ Biome.NETHER, Biome.THE_END -> {
+ if (world.options.disableExplosions) {
+ user.sendParcelMessage(nopermit = true, message = "You cannot use this bed because it would explode")
+ event.isCancelled = true; return@l
+ }
+ }
+ }
+
+ if (!canBuildOnArea(user, parcel)) {
+ user.sendParcelMessage(nopermit = true, message = "You may not sleep here")
+ event.isCancelled = true; return@l
+ }
+ }
+
+ onPlayerRightClick(event, world, parcel)
+
+ if (!event.isCancelled && parcel == null) {
+ world.blockManager.getParcelForInfoBlockInteraction(Vec3i(clickedBlock), type, event.blockFace)
+ ?.apply { user.sendMessage(Formatting.GREEN + infoString) }
+ }
+ }
+
+ Action.RIGHT_CLICK_AIR -> onPlayerRightClick(event, world, parcel)
+ Action.PHYSICAL -> if (!canBuildOnArea(user, parcel) && !(parcel != null && parcel.interactableConfig("pressure_plates"))) {
+ user.sendParcelMessage(nopermit = true, message = "You cannot use inputs in this parcel")
+ event.isCancelled = true; return@l
+ }
+ }
+ }
+
+ // private val blockPlaceInteractItems = EnumSet.of(LAVA_BUCKET, WATER_BUCKET, BUCKET, FLINT_AND_STEEL)
+
+ @Suppress("NON_EXHAUSTIVE_WHEN")
+ private fun onPlayerRightClick(event: PlayerInteractEvent, world: ParcelWorld, parcel: Parcel?) {
+ if (event.hasItem()) {
+ val item = event.item.type
+ if (world.options.blockedItems.contains(item)) {
+ event.player.sendParcelMessage(nopermit = true, message = "You cannot use this item because it is disabled in this world")
+ event.isCancelled = true; return
+ }
+
+ when (item) {
+ LAVA_BUCKET, WATER_BUCKET, BUCKET, FLINT_AND_STEEL -> {
+ val block = event.clickedBlock.getRelative(event.blockFace)
+ val otherParcel = world.getParcelAt(block)
+ if (!canBuildOnArea(event.player, otherParcel)) {
+ event.isCancelled = true
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * Prevents players from breeding mobs, entering or opening boats/minecarts,
+ * rotating item frames, doing stuff with leashes, and putting stuff on armor stands.
+ */
+ @Suppress("NON_EXHAUSTIVE_WHEN")
+ @field:ListenerMarker(priority = NORMAL)
+ val onPlayerInteractEntityEvent = RegistratorListener<PlayerInteractEntityEvent> l@{ event ->
+ val (_, area) = getWorldAndArea(event.rightClicked.location.block) ?: return@l
+ if (canBuildOnArea(event.player, area)) return@l
+ when (event.rightClicked.type) {
+ EntityType.BOAT,
+ EntityType.MINECART,
+ EntityType.MINECART_CHEST,
+ EntityType.MINECART_COMMAND,
+ EntityType.MINECART_FURNACE,
+ EntityType.MINECART_HOPPER,
+ EntityType.MINECART_MOB_SPAWNER,
+ EntityType.MINECART_TNT,
+
+ EntityType.ARMOR_STAND,
+ EntityType.PAINTING,
+ EntityType.ITEM_FRAME,
+ EntityType.LEASH_HITCH,
+
+ EntityType.CHICKEN,
+ EntityType.COW,
+ EntityType.HORSE,
+ EntityType.SHEEP,
+ EntityType.VILLAGER,
+ EntityType.WOLF -> event.isCancelled = true
+ }
+ }
+
+ /*
+ * Prevents endermen from griefing.
+ * Prevents sand blocks from exiting the parcel in which they became an entity.
+ */
+ @field:ListenerMarker(priority = NORMAL)
+ val onEntityChangeBlockEvent = RegistratorListener<EntityChangeBlockEvent> l@{ event ->
+ val (_, area) = getWorldAndArea(event.block) ?: return@l
+ if (event.entity.type == EntityType.ENDERMAN || area == null || area.hasBlockVisitors) {
+ event.isCancelled = true; return@l
+ }
+
+ if (event.entity.type == EntityType.FALLING_BLOCK) {
+ // a sand block started falling. Track it and delete it if it gets out of this parcel.
+ entityTracker.track(event.entity, area)
+ }
+ }
+
+ /*
+ * Prevents portals from being created if set so in the configs for that world
+ */
+ @field:ListenerMarker(priority = NORMAL)
+ val onEntityCreatePortalEvent = RegistratorListener<EntityCreatePortalEvent> l@{ event ->
+ val world = parcelProvider.getWorld(event.entity.world) ?: return@l
+ if (world.options.blockPortalCreation) event.isCancelled = true
+ }
+
+ /*
+ * Prevents players from dropping items
+ */
+ @field:ListenerMarker(priority = NORMAL)
+ val onPlayerDropItemEvent = RegistratorListener<PlayerDropItemEvent> l@{ event ->
+ val (_, area) = getWorldAndArea(event.itemDrop.location.block) ?: return@l
+ if (!canInteract(event.player, area, "containers")) event.isCancelled = true
+ }
+
+ /*
+ * Prevents players from picking up items
+ */
+ @field:ListenerMarker(priority = NORMAL)
+ val onEntityPickupItemEvent = RegistratorListener<EntityPickupItemEvent> l@{ event ->
+ val user = event.entity as? Player ?: return@l
+ val (_, area) = getWorldAndArea(event.item.location.block) ?: return@l
+ if (!canInteract(user, area, "containers")) event.isCancelled = true
+ }
+
+ /*
+ * Prevents players from editing inventories
+ */
+ @field:ListenerMarker(priority = NORMAL, events = ["inventory.InventoryClickEvent", "inventory.InventoryDragEvent"])
+ val onInventoryClickEvent = RegistratorListener<InventoryInteractEvent> l@{ event ->
+ val user = event.whoClicked as? Player ?: return@l
+ if ((event.inventory ?: return@l).holder === user) return@l // inventory null: hotbar
+ val (_, area) = getWorldAndArea(event.inventory.location.block) ?: return@l
+ if (!canInteract(user, area, "containers")) {
+ event.isCancelled = true
+ }
+ }
+
+ /*
+ * Cancels weather changes and sets the weather to sunny if requested by the config for that world.
+ */
+ @field:ListenerMarker(priority = NORMAL)
+ val onWeatherChangeEvent = RegistratorListener<WeatherChangeEvent> l@{ event ->
+ val world = parcelProvider.getWorld(event.world) ?: return@l
+ if (world.options.noWeather && event.toWeatherState()) {
+ event.isCancelled = true
+ }
+ }
+
+ private fun resetWeather(world: World) {
+ world.setStorm(false)
+ world.isThundering = false
+ world.weatherDuration = Int.MAX_VALUE
+ }
+
+// TODO: BlockFormEvent, BlockSpreadEvent, BlockFadeEvent, Fireworks
+
+ /*
+ * Prevents natural blocks forming
+ */
+ @ListenerMarker(priority = NORMAL)
+ val onBlockFormEvent = RegistratorListener<BlockFormEvent> l@{ event ->
+ val block = event.block
+ val (world, area) = getWorldAndArea(block) ?: return@l
+
+ // prevent any generation whatsoever on paths
+ if (area == null) {
+ event.isCancelled = true; return@l
+ }
+
+ val hasEntity = event is EntityBlockFormEvent
+ val player = (event as? EntityBlockFormEvent)?.entity as? Player
+
+ val cancel: Boolean = when (event.newState.type) {
+
+ // prevent ice generation from Frost Walkers enchantment
+ FROSTED_ICE -> player != null && !area.canBuild(player)
+
+ // prevent snow generation from weather
+ SNOW -> !hasEntity && world.options.preventWeatherBlockChanges
+
+ else -> false
+ }
+
+ if (cancel) {
+ event.isCancelled = true
+ }
+ }
+
+ /*
+ * Prevents mobs (living entities) from spawning if that is disabled for that world in the config.
+ */
+ @field:ListenerMarker(priority = NORMAL)
+ val onEntitySpawnEvent = RegistratorListener<EntitySpawnEvent> l@{ event ->
+ val world = parcelProvider.getWorld(event.entity.world) ?: return@l
+ if (event.entity is Creature && world.options.blockMobSpawning) {
+ event.isCancelled = true
+ } else if (world.getParcelAt(event.entity).let { it != null && it.hasBlockVisitors }) {
+ event.isCancelled = true
+ }
+ }
+
+ /*
+ * Prevents minecarts/boats from moving outside a plot
+ */
+ @field:ListenerMarker(priority = NORMAL)
+ val onVehicleMoveEvent = RegistratorListener<VehicleMoveEvent> l@{ event ->
+ val (_, area) = getWorldAndArea(event.to.block) ?: return@l
+ if (area == null) {
+ event.vehicle.passengers.forEach {
+ if (it.type == EntityType.PLAYER) {
+ (it as Player).sendParcelMessage(except = true, message = "Your ride ends here")
+ } else it.remove()
+ }
+ event.vehicle.eject()
+ event.vehicle.remove()
+ } else if (area.hasBlockVisitors) {
+ event.to.subtract(event.to).add(event.from)
+ }
+ }
+
+ /*
+ * Prevents players from removing items from item frames
+ * Prevents TNT Minecarts and creepers from destroying entities (This event is called BEFORE EntityExplodeEvent GG)
+ * Actually doesn't prevent this because the entities are destroyed anyway, even though the code works?
+ */
+ @field:ListenerMarker(priority = NORMAL)
+ val onEntityDamageByEntityEvent = RegistratorListener<EntityDamageByEntityEvent> l@{ event ->
+ val world = parcelProvider.getWorld(event.entity.world) ?: return@l
+ if (world.options.disableExplosions && event.damager is ExplosiveMinecart || event.damager is Creeper) {
+ event.isCancelled = true; return@l
+ }
+
+ val user = event.damager as? Player
+ ?: (event.damager as? Projectile)?.let { it.shooter as? Player }
+ ?: return@l
+
+ if (!canBuildOnArea(user, world.getParcelAt(event.entity))) {
+ event.isCancelled = true
+ }
+ }
+
+ @field:ListenerMarker(priority = NORMAL)
+ val onHangingBreakEvent = RegistratorListener<HangingBreakEvent> l@{ event ->
+ val world = parcelProvider.getWorld(event.entity.world) ?: return@l
+ if (event.cause == HangingBreakEvent.RemoveCause.EXPLOSION && world.options.disableExplosions) {
+ event.isCancelled = true; return@l
+ }
+
+ if (world.getParcelAt(event.entity).let { it != null && it.hasBlockVisitors }) {
+ event.isCancelled = true
+ }
+ }
+
+ /*
+ * Prevents players from deleting paintings and item frames
+ * This appears to take care of shooting with a bow, throwing snowballs or throwing ender pearls.
+ */
+ @field:ListenerMarker(priority = NORMAL)
+ val onHangingBreakByEntityEvent = RegistratorListener<HangingBreakByEntityEvent> l@{ event ->
+ val world = parcelProvider.getWorld(event.entity.world) ?: return@l
+ val user = event.remover as? Player ?: return@l
+ if (!canBuildOnArea(user, world.getParcelAt(event.entity))) {
+ event.isCancelled = true
+ }
+ }
+
+ /*
+ * Prevents players from placing paintings and item frames
+ */
+ @field:ListenerMarker(priority = NORMAL)
+ val onHangingPlaceEvent = RegistratorListener<HangingPlaceEvent> l@{ event ->
+ val world = parcelProvider.getWorld(event.entity.world) ?: return@l
+ val block = event.block.getRelative(event.blockFace)
+ if (!canBuildOnArea(event.player, world.getParcelAt(block))) {
+ event.isCancelled = true
+ }
+ }
+
+ /*
+ * Prevents stuff from growing outside of plots
+ */
+ @field:ListenerMarker(priority = NORMAL)
+ val onStructureGrowEvent = RegistratorListener<StructureGrowEvent> l@{ event ->
+ val (world, area) = getWorldAndArea(event.location.block) ?: return@l
+ if (area == null) {
+ event.isCancelled = true; return@l
+ }
+
+ if (!event.player.hasPermBuildAnywhere && !area.canBuild(event.player)) {
+ event.isCancelled = true; return@l
+ }
+
+ event.blocks.removeIf { world.getParcelAt(it.block) !== area }
+ }
+
+ /*
+ * Prevents dispensers/droppers from dispensing out of parcels
+ */
+ @field:ListenerMarker(priority = NORMAL)
+ val onBlockDispenseEvent = RegistratorListener<BlockDispenseEvent> l@{ event ->
+ val block = event.block
+ if (!block.type.let { it == DISPENSER || it == DROPPER }) return@l
+ val world = parcelProvider.getWorld(block.world) ?: return@l
+ val data = block.blockData as Directional
+ val targetBlock = block.getRelative(data.facing)
+ if (world.getParcelAt(targetBlock) == null) {
+ event.isCancelled = true
+ }
+ }
+
+ /*
+ * Track spawned items, making sure they don't leave the parcel.
+ */
+ @field:ListenerMarker(priority = NORMAL)
+ val onItemSpawnEvent = RegistratorListener<ItemSpawnEvent> l@{ event ->
+ val (_, area) = getWorldAndArea(event.location.block) ?: return@l
+ if (area == null) event.isCancelled = true
+ else entityTracker.track(event.entity, area)
+ }
+
+ /*
+ * Prevents endermen and endermite from teleporting outside their parcel
+ */
+ @field:ListenerMarker(priority = NORMAL)
+ val onEntityTeleportEvent = RegistratorListener<EntityTeleportEvent> l@{ event ->
+ val (world, area) = getWorldAndArea(event.from.block) ?: return@l
+ if (area !== world.getParcelAt(event.to)) {
+ event.isCancelled = true
+ }
+ }
+
+ /*
+ * Prevents projectiles from flying out of parcels
+ * Prevents players from firing projectiles if they cannot build
+ */
+ @field:ListenerMarker(priority = NORMAL)
+ val onProjectileLaunchEvent = RegistratorListener<ProjectileLaunchEvent> l@{ event ->
+ val (_, area) = getWorldAndArea(event.entity.location.block) ?: return@l
+ if (area == null || (event.entity.shooter as? Player)?.let { !canBuildOnArea(it, area) } == true) {
+ event.isCancelled = true
+ } else {
+ entityTracker.track(event.entity, area)
+ }
+ }
+
+ /*
+ * Prevents entities from dropping items upon death, if configured that way
+ */
+ @field:ListenerMarker(priority = NORMAL)
+ val onEntityDeathEvent = RegistratorListener<EntityDeathEvent> l@{ event ->
+ entityTracker.untrack(event.entity)
+ val world = parcelProvider.getWorld(event.entity.world) ?: return@l
+ if (!world.options.dropEntityItems) {
+ event.drops.clear()
+ event.droppedExp = 0
+ }
+ }
+
+ /*
+ * Assigns players their default game mode upon entering the world
+ */
+ @field:ListenerMarker(priority = NORMAL)
+ val onPlayerChangedWorldEvent = RegistratorListener<PlayerChangedWorldEvent> l@{ event ->
+ val world = parcelProvider.getWorld(event.player.world) ?: return@l
+ if (world.options.gameMode != null && !event.player.hasPermGamemodeBypass) {
+ event.player.gameMode = world.options.gameMode
+ }
+ }
+
+ /**
+ * Updates owner signs of parcels that get loaded if it is marked outdated
+ */
+ @ListenerMarker(priority = EventPriority.NORMAL)
+ val onChunkLoadEvent = RegistratorListener<ChunkLoadEvent> l@{ event ->
+ val world = parcelProvider.getWorld(event.chunk.world) ?: return@l
+ val parcels = world.blockManager.getParcelsWithOwnerBlockIn(event.chunk)
+ if (parcels.isEmpty()) return@l
+
+ parcels.forEach { id ->
+ val parcel = world.getParcelById(id)?.takeIf { it.isOwnerSignOutdated } ?: return@forEach
+ world.blockManager.updateParcelInfo(parcel.id, parcel.owner)
+ parcel.isOwnerSignOutdated = false
+ }
+
+ }
+
+ @ListenerMarker
+ val onPlayerJoinEvent = RegistratorListener<PlayerJoinEvent> l@{ event ->
+ storage.updatePlayerName(event.player.uuid, event.player.name)
+ }
+
+ /**
+ * Attempts to prevent redstone contraptions from breaking while they are being swapped
+ * Might remove if it causes lag
+ */
+ @ListenerMarker
+ val onBlockRedstoneEvent = RegistratorListener<BlockRedstoneEvent> l@{ event ->
+ val (_, area) = getWorldAndArea(event.block) ?: return@l
+ if (area == null || area.hasBlockVisitors) {
+ event.newCurrent = event.oldCurrent
+ }
+ }
+
+
+ private fun getPlayerSpeed(player: Player): Double =
+ if (player.isFlying) {
+ player.flySpeed * if (player.isSprinting) 21.6 else 10.92
+ } else {
+ player.walkSpeed * when {
+ player.isSprinting -> 5.612
+ player.isSneaking -> 1.31
+ else -> 4.317
+ } / 1.5 //?
+ } / 20.0
+
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/listener/WorldEditListener.kt b/src/main/kotlin/io/dico/parcels2/listener/WorldEditListener.kt
index 4d35a53..fc31305 100644
--- a/src/main/kotlin/io/dico/parcels2/listener/WorldEditListener.kt
+++ b/src/main/kotlin/io/dico/parcels2/listener/WorldEditListener.kt
@@ -1,79 +1,79 @@
-package io.dico.parcels2.listener
-
-import com.sk89q.worldedit.EditSession.Stage.BEFORE_REORDER
-import com.sk89q.worldedit.Vector
-import com.sk89q.worldedit.Vector2D
-import com.sk89q.worldedit.WorldEdit
-import com.sk89q.worldedit.bukkit.WorldEditPlugin
-import com.sk89q.worldedit.event.extent.EditSessionEvent
-import com.sk89q.worldedit.extent.AbstractDelegateExtent
-import com.sk89q.worldedit.extent.Extent
-import com.sk89q.worldedit.util.eventbus.EventHandler.Priority.VERY_EARLY
-import com.sk89q.worldedit.util.eventbus.Subscribe
-import com.sk89q.worldedit.world.biome.BaseBiome
-import com.sk89q.worldedit.world.block.BlockStateHolder
-import io.dico.parcels2.ParcelWorld
-import io.dico.parcels2.ParcelsPlugin
-import io.dico.parcels2.canBuildFast
-import io.dico.parcels2.util.ext.hasPermBuildAnywhere
-import io.dico.parcels2.util.ext.sendParcelMessage
-import org.bukkit.entity.Player
-import org.bukkit.plugin.Plugin
-
-class WorldEditListener(val parcels: ParcelsPlugin, val worldEdit: WorldEdit) {
-
- @Subscribe(priority = VERY_EARLY)
- fun onEditSession(event: EditSessionEvent) {
- val worldName = event.world?.name ?: return
- val world = parcels.parcelProvider.getWorld(worldName) ?: return
- if (event.stage == BEFORE_REORDER) return
-
- val actor = event.actor
- if (actor == null || !actor.isPlayer) return
-
- val player = parcels.server.getPlayer(actor.uniqueId)
- if (player.hasPermBuildAnywhere) return
-
- event.extent = ParcelsExtent(event.extent, world, player)
- }
-
- private class ParcelsExtent(extent: Extent,
- val world: ParcelWorld,
- val player: Player) : AbstractDelegateExtent(extent) {
- private var messageSent = false
-
- private fun canBuild(x: Int, z: Int): Boolean {
- world.getParcelAt(x, z)?.let { parcel ->
- if (parcel.canBuildFast(player)) {
- return true
- }
- }
-
- if (!messageSent) {
- messageSent = true
- player.sendParcelMessage(except = true, message = "You can't use WorldEdit there")
- }
-
- return false
- }
-
- override fun setBlock(location: Vector, block: BlockStateHolder<*>): Boolean {
- return canBuild(location.blockX, location.blockZ) && super.setBlock(location, block)
- }
-
- override fun setBiome(coord: Vector2D, biome: BaseBiome): Boolean {
- return canBuild(coord.blockX, coord.blockZ) && super.setBiome(coord, biome)
- }
-
- }
-
- companion object {
- fun register(parcels: ParcelsPlugin, worldEditPlugin: Plugin) {
- if (worldEditPlugin !is WorldEditPlugin) return
- val worldEdit = worldEditPlugin.worldEdit
- val listener = WorldEditListener(parcels, worldEdit)
- worldEdit.eventBus.register(listener)
- }
- }
-
+package io.dico.parcels2.listener
+
+import com.sk89q.worldedit.EditSession.Stage.BEFORE_REORDER
+import com.sk89q.worldedit.Vector
+import com.sk89q.worldedit.Vector2D
+import com.sk89q.worldedit.WorldEdit
+import com.sk89q.worldedit.bukkit.WorldEditPlugin
+import com.sk89q.worldedit.event.extent.EditSessionEvent
+import com.sk89q.worldedit.extent.AbstractDelegateExtent
+import com.sk89q.worldedit.extent.Extent
+import com.sk89q.worldedit.util.eventbus.EventHandler.Priority.VERY_EARLY
+import com.sk89q.worldedit.util.eventbus.Subscribe
+import com.sk89q.worldedit.world.biome.BaseBiome
+import com.sk89q.worldedit.world.block.BlockStateHolder
+import io.dico.parcels2.ParcelWorld
+import io.dico.parcels2.ParcelsPlugin
+import io.dico.parcels2.canBuildFast
+import io.dico.parcels2.util.ext.hasPermBuildAnywhere
+import io.dico.parcels2.util.ext.sendParcelMessage
+import org.bukkit.entity.Player
+import org.bukkit.plugin.Plugin
+
+class WorldEditListener(val parcels: ParcelsPlugin, val worldEdit: WorldEdit) {
+
+ @Subscribe(priority = VERY_EARLY)
+ fun onEditSession(event: EditSessionEvent) {
+ val worldName = event.world?.name ?: return
+ val world = parcels.parcelProvider.getWorld(worldName) ?: return
+ if (event.stage == BEFORE_REORDER) return
+
+ val actor = event.actor
+ if (actor == null || !actor.isPlayer) return
+
+ val player = parcels.server.getPlayer(actor.uniqueId)
+ if (player.hasPermBuildAnywhere) return
+
+ event.extent = ParcelsExtent(event.extent, world, player)
+ }
+
+ private class ParcelsExtent(extent: Extent,
+ val world: ParcelWorld,
+ val player: Player) : AbstractDelegateExtent(extent) {
+ private var messageSent = false
+
+ private fun canBuild(x: Int, z: Int): Boolean {
+ world.getParcelAt(x, z)?.let { parcel ->
+ if (parcel.canBuildFast(player)) {
+ return true
+ }
+ }
+
+ if (!messageSent) {
+ messageSent = true
+ player.sendParcelMessage(except = true, message = "You can't use WorldEdit there")
+ }
+
+ return false
+ }
+
+ override fun setBlock(location: Vector, block: BlockStateHolder<*>): Boolean {
+ return canBuild(location.blockX, location.blockZ) && super.setBlock(location, block)
+ }
+
+ override fun setBiome(coord: Vector2D, biome: BaseBiome): Boolean {
+ return canBuild(coord.blockX, coord.blockZ) && super.setBiome(coord, biome)
+ }
+
+ }
+
+ companion object {
+ fun register(parcels: ParcelsPlugin, worldEditPlugin: Plugin) {
+ if (worldEditPlugin !is WorldEditPlugin) return
+ val worldEdit = worldEditPlugin.worldEdit
+ val listener = WorldEditListener(parcels, worldEdit)
+ worldEdit.eventBus.register(listener)
+ }
+ }
+
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/options/GeneratorOptions.kt b/src/main/kotlin/io/dico/parcels2/options/GeneratorOptions.kt
index d0626dc..a6a57e5 100644
--- a/src/main/kotlin/io/dico/parcels2/options/GeneratorOptions.kt
+++ b/src/main/kotlin/io/dico/parcels2/options/GeneratorOptions.kt
@@ -1,36 +1,36 @@
-package io.dico.parcels2.options
-
-import io.dico.parcels2.ParcelGenerator
-import io.dico.parcels2.defaultimpl.DefaultParcelGenerator
-import org.bukkit.Bukkit
-import org.bukkit.Material
-import org.bukkit.block.Biome
-import org.bukkit.block.data.BlockData
-import kotlin.reflect.KClass
-
-object GeneratorOptionsFactories : PolymorphicOptionsFactories<ParcelGenerator>("name", GeneratorOptions::class, DefaultGeneratorOptionsFactory())
-
-class GeneratorOptions (name: String = "default", options: Any = DefaultGeneratorOptions()) : PolymorphicOptions<ParcelGenerator>(name, options, GeneratorOptionsFactories) {
- fun newInstance(worldName: String) = factory.newInstance(key, options, worldName)
-}
-
-private class DefaultGeneratorOptionsFactory : PolymorphicOptionsFactory<ParcelGenerator> {
- override val supportedKeys: List<String> = listOf("default")
- override val optionsClass: KClass<out Any> get() = DefaultGeneratorOptions::class
-
- override fun newInstance(key: String, options: Any, vararg extra: Any?): ParcelGenerator {
- return DefaultParcelGenerator(extra.first() as String, options as DefaultGeneratorOptions)
- }
-}
-
-class DefaultGeneratorOptions(val defaultBiome: Biome = Biome.JUNGLE,
- val wallType: BlockData = Bukkit.createBlockData(Material.STONE_SLAB),
- val floorType: BlockData = Bukkit.createBlockData(Material.QUARTZ_BLOCK),
- val fillType: BlockData = Bukkit.createBlockData(Material.QUARTZ_BLOCK),
- val pathMainType: BlockData = Bukkit.createBlockData(Material.SANDSTONE),
- val pathAltType: BlockData = Bukkit.createBlockData(Material.REDSTONE_BLOCK),
- val parcelSize: Int = 101,
- val pathSize: Int = 9,
- val floorHeight: Int = 64,
- val offsetX: Int = 0,
+package io.dico.parcels2.options
+
+import io.dico.parcels2.ParcelGenerator
+import io.dico.parcels2.defaultimpl.DefaultParcelGenerator
+import org.bukkit.Bukkit
+import org.bukkit.Material
+import org.bukkit.block.Biome
+import org.bukkit.block.data.BlockData
+import kotlin.reflect.KClass
+
+object GeneratorOptionsFactories : PolymorphicOptionsFactories<ParcelGenerator>("name", GeneratorOptions::class, DefaultGeneratorOptionsFactory())
+
+class GeneratorOptions (name: String = "default", options: Any = DefaultGeneratorOptions()) : PolymorphicOptions<ParcelGenerator>(name, options, GeneratorOptionsFactories) {
+ fun newInstance(worldName: String) = factory.newInstance(key, options, worldName)
+}
+
+private class DefaultGeneratorOptionsFactory : PolymorphicOptionsFactory<ParcelGenerator> {
+ override val supportedKeys: List<String> = listOf("default")
+ override val optionsClass: KClass<out Any> get() = DefaultGeneratorOptions::class
+
+ override fun newInstance(key: String, options: Any, vararg extra: Any?): ParcelGenerator {
+ return DefaultParcelGenerator(extra.first() as String, options as DefaultGeneratorOptions)
+ }
+}
+
+class DefaultGeneratorOptions(val defaultBiome: Biome = Biome.JUNGLE,
+ val wallType: BlockData = Bukkit.createBlockData(Material.STONE_SLAB),
+ val floorType: BlockData = Bukkit.createBlockData(Material.QUARTZ_BLOCK),
+ val fillType: BlockData = Bukkit.createBlockData(Material.QUARTZ_BLOCK),
+ val pathMainType: BlockData = Bukkit.createBlockData(Material.SANDSTONE),
+ val pathAltType: BlockData = Bukkit.createBlockData(Material.REDSTONE_BLOCK),
+ val parcelSize: Int = 101,
+ val pathSize: Int = 9,
+ val floorHeight: Int = 64,
+ val offsetX: Int = 0,
val offsetZ: Int = 0) \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/options/MigrationOptions.kt b/src/main/kotlin/io/dico/parcels2/options/MigrationOptions.kt
index 5e36099..7dd752e 100644
--- a/src/main/kotlin/io/dico/parcels2/options/MigrationOptions.kt
+++ b/src/main/kotlin/io/dico/parcels2/options/MigrationOptions.kt
@@ -1,22 +1,22 @@
-package io.dico.parcels2.options
-
-import io.dico.parcels2.storage.migration.Migration
-import io.dico.parcels2.storage.migration.plotme.PlotmeMigration
-import kotlin.reflect.KClass
-
-object MigrationOptionsFactories : PolymorphicOptionsFactories<Migration>("kind", MigrationOptions::class, PlotmeMigrationFactory())
-
-class MigrationOptions(kind: String = "plotme-0.17", options: Any = PlotmeMigrationOptions()) : SimplePolymorphicOptions<Migration>(kind, options, MigrationOptionsFactories)
-
-private class PlotmeMigrationFactory : PolymorphicOptionsFactory<Migration> {
- override val supportedKeys = listOf("plotme-0.17")
- override val optionsClass: KClass<out Any> get() = PlotmeMigrationOptions::class
-
- override fun newInstance(key: String, options: Any, vararg extra: Any?): Migration {
- return PlotmeMigration(options as PlotmeMigrationOptions)
- }
-}
-
-class PlotmeMigrationOptions(val worldsFromTo: Map<String, String> = mapOf("plotworld" to "parcels"),
- val storage: StorageOptions = StorageOptions(options = DataConnectionOptions(database = "plotme")),
+package io.dico.parcels2.options
+
+import io.dico.parcels2.storage.migration.Migration
+import io.dico.parcels2.storage.migration.plotme.PlotmeMigration
+import kotlin.reflect.KClass
+
+object MigrationOptionsFactories : PolymorphicOptionsFactories<Migration>("kind", MigrationOptions::class, PlotmeMigrationFactory())
+
+class MigrationOptions(kind: String = "plotme-0.17", options: Any = PlotmeMigrationOptions()) : SimplePolymorphicOptions<Migration>(kind, options, MigrationOptionsFactories)
+
+private class PlotmeMigrationFactory : PolymorphicOptionsFactory<Migration> {
+ override val supportedKeys = listOf("plotme-0.17")
+ override val optionsClass: KClass<out Any> get() = PlotmeMigrationOptions::class
+
+ override fun newInstance(key: String, options: Any, vararg extra: Any?): Migration {
+ return PlotmeMigration(options as PlotmeMigrationOptions)
+ }
+}
+
+class PlotmeMigrationOptions(val worldsFromTo: Map<String, String> = mapOf("plotworld" to "parcels"),
+ val storage: StorageOptions = StorageOptions(options = DataConnectionOptions(database = "plotme")),
val tableNamesUppercase: Boolean = false) \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/options/Options.kt b/src/main/kotlin/io/dico/parcels2/options/Options.kt
index 35d48ba..412c783 100644
--- a/src/main/kotlin/io/dico/parcels2/options/Options.kt
+++ b/src/main/kotlin/io/dico/parcels2/options/Options.kt
@@ -1,58 +1,58 @@
-package io.dico.parcels2.options
-
-import io.dico.parcels2.TickJobtimeOptions
-import org.bukkit.GameMode
-import org.bukkit.Material
-import java.io.Reader
-import java.io.Writer
-import java.util.EnumSet
-
-class Options {
- var worlds: Map<String, WorldOptions> = hashMapOf()
- private set
- var storage: StorageOptions = StorageOptions()
- var tickJobtime: TickJobtimeOptions = TickJobtimeOptions(20, 1)
- var migration = MigrationOptionsHolder()
-
- fun addWorld(name: String,
- generatorOptions: GeneratorOptions? = null,
- worldOptions: RuntimeWorldOptions? = null) {
- val optionsHolder = WorldOptions(
- generatorOptions ?: GeneratorOptions(),
- worldOptions ?: RuntimeWorldOptions()
- )
-
- (worlds as MutableMap).put(name, optionsHolder)
- }
-
- fun writeTo(writer: Writer) = optionsMapper.writeValue(writer, this)
-
- fun mergeFrom(reader: Reader) = optionsMapper.readerForUpdating(this).readValue<Options>(reader)
-
- override fun toString(): String = optionsMapper.writeValueAsString(this)
-
-}
-
-class WorldOptions(val generator: GeneratorOptions,
- var runtime: RuntimeWorldOptions = RuntimeWorldOptions())
-
-class RuntimeWorldOptions(var gameMode: GameMode? = GameMode.CREATIVE,
- var dayTime: Boolean = true,
- var noWeather: Boolean = true,
- var preventWeatherBlockChanges: Boolean = true,
- var preventBlockSpread: Boolean = true, // TODO
- var dropEntityItems: Boolean = true,
- var doTileDrops: Boolean = false,
- var disableExplosions: Boolean = true,
- var blockPortalCreation: Boolean = true,
- var blockMobSpawning: Boolean = true,
- var blockedItems: Set<Material> = EnumSet.of(Material.FLINT_AND_STEEL, Material.SNOWBALL),
- var axisLimit: Int = 10)
-
-class DataFileOptions(val location: String = "/flatfile-storage/")
-
-class MigrationOptionsHolder {
- var enabled = false
- var disableWhenComplete = true
- var instance: MigrationOptions? = MigrationOptions()
+package io.dico.parcels2.options
+
+import io.dico.parcels2.TickJobtimeOptions
+import org.bukkit.GameMode
+import org.bukkit.Material
+import java.io.Reader
+import java.io.Writer
+import java.util.EnumSet
+
+class Options {
+ var worlds: Map<String, WorldOptions> = hashMapOf()
+ private set
+ var storage: StorageOptions = StorageOptions()
+ var tickJobtime: TickJobtimeOptions = TickJobtimeOptions(20, 1)
+ var migration = MigrationOptionsHolder()
+
+ fun addWorld(name: String,
+ generatorOptions: GeneratorOptions? = null,
+ worldOptions: RuntimeWorldOptions? = null) {
+ val optionsHolder = WorldOptions(
+ generatorOptions ?: GeneratorOptions(),
+ worldOptions ?: RuntimeWorldOptions()
+ )
+
+ (worlds as MutableMap).put(name, optionsHolder)
+ }
+
+ fun writeTo(writer: Writer) = optionsMapper.writeValue(writer, this)
+
+ fun mergeFrom(reader: Reader) = optionsMapper.readerForUpdating(this).readValue<Options>(reader)
+
+ override fun toString(): String = optionsMapper.writeValueAsString(this)
+
+}
+
+class WorldOptions(val generator: GeneratorOptions,
+ var runtime: RuntimeWorldOptions = RuntimeWorldOptions())
+
+class RuntimeWorldOptions(var gameMode: GameMode? = GameMode.CREATIVE,
+ var dayTime: Boolean = true,
+ var noWeather: Boolean = true,
+ var preventWeatherBlockChanges: Boolean = true,
+ var preventBlockSpread: Boolean = true, // TODO
+ var dropEntityItems: Boolean = true,
+ var doTileDrops: Boolean = false,
+ var disableExplosions: Boolean = true,
+ var blockPortalCreation: Boolean = true,
+ var blockMobSpawning: Boolean = true,
+ var blockedItems: Set<Material> = EnumSet.of(Material.FLINT_AND_STEEL, Material.SNOWBALL),
+ var axisLimit: Int = 10)
+
+class DataFileOptions(val location: String = "/flatfile-storage/")
+
+class MigrationOptionsHolder {
+ var enabled = false
+ var disableWhenComplete = true
+ var instance: MigrationOptions? = MigrationOptions()
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/options/OptionsMapper.kt b/src/main/kotlin/io/dico/parcels2/options/OptionsMapper.kt
index 671a25f..d741617 100644
--- a/src/main/kotlin/io/dico/parcels2/options/OptionsMapper.kt
+++ b/src/main/kotlin/io/dico/parcels2/options/OptionsMapper.kt
@@ -1,66 +1,66 @@
-package io.dico.parcels2.options
-
-import com.fasterxml.jackson.core.JsonGenerator
-import com.fasterxml.jackson.core.JsonParser
-import com.fasterxml.jackson.databind.DeserializationContext
-import com.fasterxml.jackson.databind.ObjectMapper
-import com.fasterxml.jackson.databind.PropertyNamingStrategy
-import com.fasterxml.jackson.databind.SerializerProvider
-import com.fasterxml.jackson.databind.deser.std.StdDeserializer
-import com.fasterxml.jackson.databind.ser.std.StdSerializer
-import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
-import com.fasterxml.jackson.module.kotlin.KotlinModule
-import org.bukkit.Bukkit
-import org.bukkit.block.data.BlockData
-
-val optionsMapper = ObjectMapper(YAMLFactory()).apply {
- propertyNamingStrategy = PropertyNamingStrategy.KEBAB_CASE
-
- val kotlinModule = KotlinModule()
-
- with(kotlinModule) {
- /*
- setSerializerModifier(object : BeanSerializerModifier() {
- @Suppress("UNCHECKED_CAST")
- override fun modifySerializer(config: SerializationConfig?, beanDesc: BeanDescription, serializer: JsonSerializer<*>): JsonSerializer<*> {
-
- val newSerializer = if (GeneratorOptions::class.isSuperclassOf(beanDesc.beanClass.kotlin)) {
- GeneratorOptionsSerializer(serializer as JsonSerializer<GeneratorOptions>)
- } else {
- serializer
- }
-
- return super.modifySerializer(config, beanDesc, newSerializer)
- }
- })*/
-
- addSerializer(BlockDataSerializer())
- addDeserializer(BlockData::class.java, BlockDataDeserializer())
-
- GeneratorOptionsFactories.registerSerialization(this)
- StorageOptionsFactories.registerSerialization(this)
- MigrationOptionsFactories.registerSerialization(this)
- }
-
- registerModule(kotlinModule)
-}
-
-private class BlockDataSerializer : StdSerializer<BlockData>(BlockData::class.java) {
-
- override fun serialize(value: BlockData, gen: JsonGenerator, provider: SerializerProvider) {
- gen.writeString(value.asString)
- }
-
-}
-
-private class BlockDataDeserializer : StdDeserializer<BlockData>(BlockData::class.java) {
-
- override fun deserialize(p: JsonParser, ctxt: DeserializationContext): BlockData? {
- try {
- return Bukkit.createBlockData(p.valueAsString)
- } catch (ex: Exception) {
- throw RuntimeException("Exception occurred at ${p.currentLocation}", ex)
- }
- }
-
-}
+package io.dico.parcels2.options
+
+import com.fasterxml.jackson.core.JsonGenerator
+import com.fasterxml.jackson.core.JsonParser
+import com.fasterxml.jackson.databind.DeserializationContext
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.fasterxml.jackson.databind.PropertyNamingStrategy
+import com.fasterxml.jackson.databind.SerializerProvider
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer
+import com.fasterxml.jackson.databind.ser.std.StdSerializer
+import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
+import com.fasterxml.jackson.module.kotlin.KotlinModule
+import org.bukkit.Bukkit
+import org.bukkit.block.data.BlockData
+
+val optionsMapper = ObjectMapper(YAMLFactory()).apply {
+ propertyNamingStrategy = PropertyNamingStrategy.KEBAB_CASE
+
+ val kotlinModule = KotlinModule()
+
+ with(kotlinModule) {
+ /*
+ setSerializerModifier(object : BeanSerializerModifier() {
+ @Suppress("UNCHECKED_CAST")
+ override fun modifySerializer(config: SerializationConfig?, beanDesc: BeanDescription, serializer: JsonSerializer<*>): JsonSerializer<*> {
+
+ val newSerializer = if (GeneratorOptions::class.isSuperclassOf(beanDesc.beanClass.kotlin)) {
+ GeneratorOptionsSerializer(serializer as JsonSerializer<GeneratorOptions>)
+ } else {
+ serializer
+ }
+
+ return super.modifySerializer(config, beanDesc, newSerializer)
+ }
+ })*/
+
+ addSerializer(BlockDataSerializer())
+ addDeserializer(BlockData::class.java, BlockDataDeserializer())
+
+ GeneratorOptionsFactories.registerSerialization(this)
+ StorageOptionsFactories.registerSerialization(this)
+ MigrationOptionsFactories.registerSerialization(this)
+ }
+
+ registerModule(kotlinModule)
+}
+
+private class BlockDataSerializer : StdSerializer<BlockData>(BlockData::class.java) {
+
+ override fun serialize(value: BlockData, gen: JsonGenerator, provider: SerializerProvider) {
+ gen.writeString(value.asString)
+ }
+
+}
+
+private class BlockDataDeserializer : StdDeserializer<BlockData>(BlockData::class.java) {
+
+ override fun deserialize(p: JsonParser, ctxt: DeserializationContext): BlockData? {
+ try {
+ return Bukkit.createBlockData(p.valueAsString)
+ } catch (ex: Exception) {
+ throw RuntimeException("Exception occurred at ${p.currentLocation}", ex)
+ }
+ }
+
+}
diff --git a/src/main/kotlin/io/dico/parcels2/options/PolymorphicOptions.kt b/src/main/kotlin/io/dico/parcels2/options/PolymorphicOptions.kt
index aa60f39..f65efa1 100644
--- a/src/main/kotlin/io/dico/parcels2/options/PolymorphicOptions.kt
+++ b/src/main/kotlin/io/dico/parcels2/options/PolymorphicOptions.kt
@@ -1,89 +1,89 @@
-package io.dico.parcels2.options
-
-import com.fasterxml.jackson.core.JsonGenerator
-import com.fasterxml.jackson.core.JsonParser
-import com.fasterxml.jackson.databind.DeserializationContext
-import com.fasterxml.jackson.databind.JsonDeserializer
-import com.fasterxml.jackson.databind.JsonNode
-import com.fasterxml.jackson.databind.SerializerProvider
-import com.fasterxml.jackson.databind.module.SimpleModule
-import com.fasterxml.jackson.databind.ser.std.StdSerializer
-import io.dico.parcels2.logger
-import kotlin.reflect.KClass
-
-abstract class PolymorphicOptions<T : Any>(val key: String,
- val options: Any,
- factories: PolymorphicOptionsFactories<T>) {
- val factory = factories.getFactory(key)!!
-}
-
-abstract class SimplePolymorphicOptions<T : Any>(key: String, options: Any, factories: PolymorphicOptionsFactories<T>)
- : PolymorphicOptions<T>(key, options, factories) {
- fun newInstance(): T = factory.newInstance(key, options)
-}
-
-interface PolymorphicOptionsFactory<T : Any> {
- val supportedKeys: List<String>
- val optionsClass: KClass<out Any>
- fun newInstance(key: String, options: Any, vararg extra: Any?): T
-}
-
-@Suppress("UNCHECKED_CAST")
-abstract class PolymorphicOptionsFactories<T : Any>(val serializeKeyAs: String,
- rootClass: KClass<out PolymorphicOptions<T>>,
- vararg defaultFactories: PolymorphicOptionsFactory<T>) {
- val rootClass = rootClass as KClass<PolymorphicOptions<T>>
- private val map: MutableMap<String, PolymorphicOptionsFactory<T>> = linkedMapOf()
- val availableKeys: Collection<String> get() = map.keys
-
- fun registerFactory(factory: PolymorphicOptionsFactory<T>) = factory.supportedKeys.forEach { map.putIfAbsent(it.toLowerCase(), factory) }
-
- fun getFactory(key: String): PolymorphicOptionsFactory<T>? = map[key.toLowerCase()]
-
- fun registerSerialization(module: SimpleModule) {
- module.addSerializer(PolymorphicOptionsSerializer(this))
- module.addDeserializer(rootClass.java, PolymorphicOptionsDeserializer(this))
- }
-
- init {
- defaultFactories.forEach { registerFactory(it) }
- }
-}
-
-
-private class PolymorphicOptionsDeserializer<T : Any>(val factories: PolymorphicOptionsFactories<T>) : JsonDeserializer<PolymorphicOptions<T>>() {
-
- override fun deserialize(p: JsonParser, ctxt: DeserializationContext?): PolymorphicOptions<T> {
- val node = p.readValueAsTree<JsonNode>()
- val key = node.get(factories.serializeKeyAs).asText()
- val factory = getFactory(key)
- val optionsNode = node.get("options")
- val options = p.codec.treeToValue(optionsNode, factory.optionsClass.java)
- return factories.rootClass.constructors.first().call(key, options)
- }
-
- private fun getFactory(key: String): PolymorphicOptionsFactory<T> {
- factories.getFactory(key)?.let { return it }
-
- logger.warn("Unknown ${factories.rootClass.simpleName} ${factories.serializeKeyAs}: $key. " +
- "\nAvailable options: ${factories.availableKeys}")
-
- val default = factories.getFactory(factories.availableKeys.first())
- ?: throw IllegalStateException("No default ${factories.rootClass.simpleName} factory registered.")
- return default
- }
-
-}
-
-private class PolymorphicOptionsSerializer<T : Any>(val factories: PolymorphicOptionsFactories<T>) : StdSerializer<PolymorphicOptions<T>>(factories.rootClass.java) {
-
- override fun serialize(value: PolymorphicOptions<T>, gen: JsonGenerator, sp: SerializerProvider?) {
- with(gen) {
- writeStartObject()
- writeStringField(factories.serializeKeyAs, value.key)
- writeFieldName("options")
- writeObject(value.options)
- writeEndObject()
- }
- }
+package io.dico.parcels2.options
+
+import com.fasterxml.jackson.core.JsonGenerator
+import com.fasterxml.jackson.core.JsonParser
+import com.fasterxml.jackson.databind.DeserializationContext
+import com.fasterxml.jackson.databind.JsonDeserializer
+import com.fasterxml.jackson.databind.JsonNode
+import com.fasterxml.jackson.databind.SerializerProvider
+import com.fasterxml.jackson.databind.module.SimpleModule
+import com.fasterxml.jackson.databind.ser.std.StdSerializer
+import io.dico.parcels2.logger
+import kotlin.reflect.KClass
+
+abstract class PolymorphicOptions<T : Any>(val key: String,
+ val options: Any,
+ factories: PolymorphicOptionsFactories<T>) {
+ val factory = factories.getFactory(key)!!
+}
+
+abstract class SimplePolymorphicOptions<T : Any>(key: String, options: Any, factories: PolymorphicOptionsFactories<T>)
+ : PolymorphicOptions<T>(key, options, factories) {
+ fun newInstance(): T = factory.newInstance(key, options)
+}
+
+interface PolymorphicOptionsFactory<T : Any> {
+ val supportedKeys: List<String>
+ val optionsClass: KClass<out Any>
+ fun newInstance(key: String, options: Any, vararg extra: Any?): T
+}
+
+@Suppress("UNCHECKED_CAST")
+abstract class PolymorphicOptionsFactories<T : Any>(val serializeKeyAs: String,
+ rootClass: KClass<out PolymorphicOptions<T>>,
+ vararg defaultFactories: PolymorphicOptionsFactory<T>) {
+ val rootClass = rootClass as KClass<PolymorphicOptions<T>>
+ private val map: MutableMap<String, PolymorphicOptionsFactory<T>> = linkedMapOf()
+ val availableKeys: Collection<String> get() = map.keys
+
+ fun registerFactory(factory: PolymorphicOptionsFactory<T>) = factory.supportedKeys.forEach { map.putIfAbsent(it.toLowerCase(), factory) }
+
+ fun getFactory(key: String): PolymorphicOptionsFactory<T>? = map[key.toLowerCase()]
+
+ fun registerSerialization(module: SimpleModule) {
+ module.addSerializer(PolymorphicOptionsSerializer(this))
+ module.addDeserializer(rootClass.java, PolymorphicOptionsDeserializer(this))
+ }
+
+ init {
+ defaultFactories.forEach { registerFactory(it) }
+ }
+}
+
+
+private class PolymorphicOptionsDeserializer<T : Any>(val factories: PolymorphicOptionsFactories<T>) : JsonDeserializer<PolymorphicOptions<T>>() {
+
+ override fun deserialize(p: JsonParser, ctxt: DeserializationContext?): PolymorphicOptions<T> {
+ val node = p.readValueAsTree<JsonNode>()
+ val key = node.get(factories.serializeKeyAs).asText()
+ val factory = getFactory(key)
+ val optionsNode = node.get("options")
+ val options = p.codec.treeToValue(optionsNode, factory.optionsClass.java)
+ return factories.rootClass.constructors.first().call(key, options)
+ }
+
+ private fun getFactory(key: String): PolymorphicOptionsFactory<T> {
+ factories.getFactory(key)?.let { return it }
+
+ logger.warn("Unknown ${factories.rootClass.simpleName} ${factories.serializeKeyAs}: $key. " +
+ "\nAvailable options: ${factories.availableKeys}")
+
+ val default = factories.getFactory(factories.availableKeys.first())
+ ?: throw IllegalStateException("No default ${factories.rootClass.simpleName} factory registered.")
+ return default
+ }
+
+}
+
+private class PolymorphicOptionsSerializer<T : Any>(val factories: PolymorphicOptionsFactories<T>) : StdSerializer<PolymorphicOptions<T>>(factories.rootClass.java) {
+
+ override fun serialize(value: PolymorphicOptions<T>, gen: JsonGenerator, sp: SerializerProvider?) {
+ with(gen) {
+ writeStartObject()
+ writeStringField(factories.serializeKeyAs, value.key)
+ writeFieldName("options")
+ writeObject(value.options)
+ writeEndObject()
+ }
+ }
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/options/StorageOptions.kt b/src/main/kotlin/io/dico/parcels2/options/StorageOptions.kt
index 3d68701..29aab7a 100644
--- a/src/main/kotlin/io/dico/parcels2/options/StorageOptions.kt
+++ b/src/main/kotlin/io/dico/parcels2/options/StorageOptions.kt
@@ -1,59 +1,59 @@
-package io.dico.parcels2.options
-
-import com.zaxxer.hikari.HikariDataSource
-import io.dico.parcels2.logger
-import io.dico.parcels2.storage.Storage
-import io.dico.parcels2.storage.BackedStorage
-import io.dico.parcels2.storage.exposed.ExposedBacking
-import io.dico.parcels2.storage.getHikariConfig
-import javax.sql.DataSource
-
-object StorageOptionsFactories : PolymorphicOptionsFactories<Storage>("dialect", StorageOptions::class, ConnectionStorageFactory())
-
-class StorageOptions(dialect: String = "mariadb", options: Any = DataConnectionOptions()) : SimplePolymorphicOptions<Storage>(dialect, options, StorageOptionsFactories) {
-
- fun getDataSourceFactory(): DataSourceFactory? {
- return when (factory) {
- is ConnectionStorageFactory -> factory.getDataSourceFactory(key, options)
- else -> return null
- }
- }
-}
-
-typealias DataSourceFactory = () -> DataSource
-
-private class ConnectionStorageFactory : PolymorphicOptionsFactory<Storage> {
- override val optionsClass = DataConnectionOptions::class
- override val supportedKeys: List<String> = listOf("postgresql", "mariadb")
-
- fun getDataSourceFactory(key: String, options: Any): DataSourceFactory {
- val hikariConfig = getHikariConfig(key, options as DataConnectionOptions)
- return { HikariDataSource(hikariConfig) }
- }
-
- override fun newInstance(key: String, options: Any, vararg extra: Any?): Storage {
- return BackedStorage(ExposedBacking(getDataSourceFactory(key, options), (options as DataConnectionOptions).poolSize))
- }
-}
-
-data class DataConnectionOptions(val address: String = "localhost",
- val database: String = "parcels",
- val username: String = "root",
- val password: String = "",
- val poolSize: Int = 4) {
-
- fun splitAddressAndPort(defaultPort: Int = 3306): Pair<String, Int>? {
- val idx = address.indexOf(":").takeUnless { it == -1 } ?: return Pair(address, defaultPort)
-
- val addressName = address.substring(0, idx).takeUnless { it.isBlank() } ?: return null.also {
- logger.error("(Invalidly) blank address in data storage options")
- }
-
- val port = address.substring(idx + 1).toIntOrNull() ?: return null.also {
- logger.error("Invalid port number in data storage options: $it, using $defaultPort as default")
- }
-
- return Pair(addressName, port)
- }
-
+package io.dico.parcels2.options
+
+import com.zaxxer.hikari.HikariDataSource
+import io.dico.parcels2.logger
+import io.dico.parcels2.storage.Storage
+import io.dico.parcels2.storage.BackedStorage
+import io.dico.parcels2.storage.exposed.ExposedBacking
+import io.dico.parcels2.storage.getHikariConfig
+import javax.sql.DataSource
+
+object StorageOptionsFactories : PolymorphicOptionsFactories<Storage>("dialect", StorageOptions::class, ConnectionStorageFactory())
+
+class StorageOptions(dialect: String = "mariadb", options: Any = DataConnectionOptions()) : SimplePolymorphicOptions<Storage>(dialect, options, StorageOptionsFactories) {
+
+ fun getDataSourceFactory(): DataSourceFactory? {
+ return when (factory) {
+ is ConnectionStorageFactory -> factory.getDataSourceFactory(key, options)
+ else -> return null
+ }
+ }
+}
+
+typealias DataSourceFactory = () -> DataSource
+
+private class ConnectionStorageFactory : PolymorphicOptionsFactory<Storage> {
+ override val optionsClass = DataConnectionOptions::class
+ override val supportedKeys: List<String> = listOf("postgresql", "mariadb")
+
+ fun getDataSourceFactory(key: String, options: Any): DataSourceFactory {
+ val hikariConfig = getHikariConfig(key, options as DataConnectionOptions)
+ return { HikariDataSource(hikariConfig) }
+ }
+
+ override fun newInstance(key: String, options: Any, vararg extra: Any?): Storage {
+ return BackedStorage(ExposedBacking(getDataSourceFactory(key, options), (options as DataConnectionOptions).poolSize))
+ }
+}
+
+data class DataConnectionOptions(val address: String = "localhost",
+ val database: String = "parcels",
+ val username: String = "root",
+ val password: String = "",
+ val poolSize: Int = 4) {
+
+ fun splitAddressAndPort(defaultPort: Int = 3306): Pair<String, Int>? {
+ val idx = address.indexOf(":").takeUnless { it == -1 } ?: return Pair(address, defaultPort)
+
+ val addressName = address.substring(0, idx).takeUnless { it.isBlank() } ?: return null.also {
+ logger.error("(Invalidly) blank address in data storage options")
+ }
+
+ val port = address.substring(idx + 1).toIntOrNull() ?: return null.also {
+ logger.error("Invalid port number in data storage options: $it, using $defaultPort as default")
+ }
+
+ return Pair(addressName, port)
+ }
+
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/storage/Backing.kt b/src/main/kotlin/io/dico/parcels2/storage/Backing.kt
index 12d5bc7..63a5db6 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/Backing.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/Backing.kt
@@ -1,69 +1,69 @@
-package io.dico.parcels2.storage
-
-import io.dico.parcels2.*
-import kotlinx.coroutines.Deferred
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.channels.ReceiveChannel
-import kotlinx.coroutines.channels.SendChannel
-import org.joda.time.DateTime
-import java.util.UUID
-import kotlin.coroutines.CoroutineContext
-
-interface Backing {
-
- val name: String
-
- val isConnected: Boolean
-
- val coroutineContext: CoroutineContext
-
- fun launchJob(job: Backing.() -> Unit): Job
-
- fun <T> launchFuture(future: Backing.() -> T): Deferred<T>
-
- fun <T> openChannel(future: Backing.(SendChannel<T>) -> Unit): ReceiveChannel<T>
-
- fun <T> openChannelForWriting(future: Backing.(T) -> Unit): SendChannel<T>
-
-
- fun init()
-
- fun shutdown()
-
-
- fun getWorldCreationTime(worldId: ParcelWorldId): DateTime?
-
- fun setWorldCreationTime(worldId: ParcelWorldId, time: DateTime)
-
- fun getPlayerUuidForName(name: String): UUID?
-
- fun updatePlayerName(uuid: UUID, name: String)
-
- fun transmitParcelData(channel: SendChannel<DataPair>, parcels: Sequence<ParcelId>)
-
- fun transmitAllParcelData(channel: SendChannel<DataPair>)
-
- fun readParcelData(parcel: ParcelId): ParcelDataHolder?
-
- fun getOwnedParcels(user: PlayerProfile): List<ParcelId>
-
- fun getNumParcels(user: PlayerProfile): Int = getOwnedParcels(user).size
-
-
- fun setParcelData(parcel: ParcelId, data: ParcelDataHolder?)
-
- fun setParcelOwner(parcel: ParcelId, owner: PlayerProfile?)
-
- fun setParcelOwnerSignOutdated(parcel: ParcelId, outdated: Boolean)
-
- fun setLocalPrivilege(parcel: ParcelId, player: PlayerProfile, privilege: Privilege)
-
- fun setParcelOptionsInteractConfig(parcel: ParcelId, config: InteractableConfiguration)
-
-
- fun transmitAllGlobalPrivileges(channel: SendChannel<PrivilegePair<PlayerProfile>>)
-
- fun readGlobalPrivileges(owner: PlayerProfile): PrivilegesHolder?
-
- fun setGlobalPrivilege(owner: PlayerProfile, player: PlayerProfile, privilege: Privilege)
-}
+package io.dico.parcels2.storage
+
+import io.dico.parcels2.*
+import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.channels.ReceiveChannel
+import kotlinx.coroutines.channels.SendChannel
+import org.joda.time.DateTime
+import java.util.UUID
+import kotlin.coroutines.CoroutineContext
+
+interface Backing {
+
+ val name: String
+
+ val isConnected: Boolean
+
+ val coroutineContext: CoroutineContext
+
+ fun launchJob(job: Backing.() -> Unit): Job
+
+ fun <T> launchFuture(future: Backing.() -> T): Deferred<T>
+
+ fun <T> openChannel(future: Backing.(SendChannel<T>) -> Unit): ReceiveChannel<T>
+
+ fun <T> openChannelForWriting(future: Backing.(T) -> Unit): SendChannel<T>
+
+
+ fun init()
+
+ fun shutdown()
+
+
+ fun getWorldCreationTime(worldId: ParcelWorldId): DateTime?
+
+ fun setWorldCreationTime(worldId: ParcelWorldId, time: DateTime)
+
+ fun getPlayerUuidForName(name: String): UUID?
+
+ fun updatePlayerName(uuid: UUID, name: String)
+
+ fun transmitParcelData(channel: SendChannel<DataPair>, parcels: Sequence<ParcelId>)
+
+ fun transmitAllParcelData(channel: SendChannel<DataPair>)
+
+ fun readParcelData(parcel: ParcelId): ParcelDataHolder?
+
+ fun getOwnedParcels(user: PlayerProfile): List<ParcelId>
+
+ fun getNumParcels(user: PlayerProfile): Int = getOwnedParcels(user).size
+
+
+ fun setParcelData(parcel: ParcelId, data: ParcelDataHolder?)
+
+ fun setParcelOwner(parcel: ParcelId, owner: PlayerProfile?)
+
+ fun setParcelOwnerSignOutdated(parcel: ParcelId, outdated: Boolean)
+
+ fun setLocalPrivilege(parcel: ParcelId, player: PlayerProfile, privilege: Privilege)
+
+ fun setParcelOptionsInteractConfig(parcel: ParcelId, config: InteractableConfiguration)
+
+
+ fun transmitAllGlobalPrivileges(channel: SendChannel<PrivilegePair<PlayerProfile>>)
+
+ fun readGlobalPrivileges(owner: PlayerProfile): PrivilegesHolder?
+
+ fun setGlobalPrivilege(owner: PlayerProfile, player: PlayerProfile, privilege: Privilege)
+}
diff --git a/src/main/kotlin/io/dico/parcels2/storage/DataConverters.kt b/src/main/kotlin/io/dico/parcels2/storage/DataConverters.kt
index 80f41b2..54f7677 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/DataConverters.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/DataConverters.kt
@@ -1,38 +1,38 @@
-package io.dico.parcels2.storage
-
-import java.lang.IllegalArgumentException
-import java.nio.ByteBuffer
-import java.util.UUID
-
-/* For putting it into the database */
-fun UUID.toByteArray(): ByteArray =
- ByteBuffer.allocate(16).apply {
- putLong(mostSignificantBits)
- putLong(leastSignificantBits)
- }.array()
-
-/* For getting it out of the database */
-fun ByteArray.toUUID(): UUID =
- ByteBuffer.wrap(this).run {
- val mostSignificantBits = getLong()
- val leastSignificantBits = getLong()
- UUID(mostSignificantBits, leastSignificantBits)
- }
-
-/* For putting it into the database */
-fun IntArray.toByteArray(): ByteArray =
- ByteBuffer.allocate(size * Int.SIZE_BYTES).also { buf ->
- buf.asIntBuffer().put(this)
- }.array()
-
-/* For getting it out of the database */
-fun ByteArray.toIntArray(): IntArray {
- if (this.size % Int.SIZE_BYTES != 0)
- throw IllegalArgumentException("Size must be divisible by ${Int.SIZE_BYTES}")
-
- return ByteBuffer.wrap(this).run {
- IntArray(remaining() / 4).also { array ->
- asIntBuffer().get(array)
- }
- }
+package io.dico.parcels2.storage
+
+import java.lang.IllegalArgumentException
+import java.nio.ByteBuffer
+import java.util.UUID
+
+/* For putting it into the database */
+fun UUID.toByteArray(): ByteArray =
+ ByteBuffer.allocate(16).apply {
+ putLong(mostSignificantBits)
+ putLong(leastSignificantBits)
+ }.array()
+
+/* For getting it out of the database */
+fun ByteArray.toUUID(): UUID =
+ ByteBuffer.wrap(this).run {
+ val mostSignificantBits = getLong()
+ val leastSignificantBits = getLong()
+ UUID(mostSignificantBits, leastSignificantBits)
+ }
+
+/* For putting it into the database */
+fun IntArray.toByteArray(): ByteArray =
+ ByteBuffer.allocate(size * Int.SIZE_BYTES).also { buf ->
+ buf.asIntBuffer().put(this)
+ }.array()
+
+/* For getting it out of the database */
+fun ByteArray.toIntArray(): IntArray {
+ if (this.size % Int.SIZE_BYTES != 0)
+ throw IllegalArgumentException("Size must be divisible by ${Int.SIZE_BYTES}")
+
+ return ByteBuffer.wrap(this).run {
+ IntArray(remaining() / 4).also { array ->
+ asIntBuffer().get(array)
+ }
+ }
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/storage/Hikari.kt b/src/main/kotlin/io/dico/parcels2/storage/Hikari.kt
index 480d533..f3030f6 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/Hikari.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/Hikari.kt
@@ -1,73 +1,73 @@
-package io.dico.parcels2.storage
-
-import com.zaxxer.hikari.HikariConfig
-import io.dico.parcels2.options.DataConnectionOptions
-
-fun getHikariConfig(dialectName: String,
- dco: DataConnectionOptions): HikariConfig = HikariConfig().apply {
-
- val (address, port) = dco.splitAddressAndPort() ?: throw IllegalArgumentException("Invalid address: ${dco.address}")
-
- when (dialectName) {
- "postgresql" -> run {
- dataSourceClassName = "org.postgresql.ds.PGSimpleDataSource"
- dataSourceProperties["serverName"] = address
- dataSourceProperties["portNumber"] = port.toString()
- dataSourceProperties["databaseName"] = dco.database
- }
-
- "mariadb" -> run {
- dataSourceClassName = "org.mariadb.jdbc.MariaDbDataSource"
- dataSourceProperties["serverName"] = address
- dataSourceProperties["port"] = port.toString()
- dataSourceProperties["databaseName"] = dco.database
- dataSourceProperties["properties"] = "useUnicode=true;characterEncoding=utf8"
- }
-
- else -> throw IllegalArgumentException("Unsupported dialect: $dialectName")
- }
-
- poolName = "parcels"
- maximumPoolSize = dco.poolSize
- username = dco.username
- password = dco.password
- connectionTimeout = 15000
- leakDetectionThreshold = 10000
- connectionTestQuery = "SELECT 1"
-
-
- /*
-
- addDataSourceProperty("serverName", address)
- addDataSourceProperty("port", port.toString())
- addDataSourceProperty("databaseName", dco.database)
-
- // copied from github.com/lucko/LuckPerms
- if (dialectName.toLowerCase() == "mariadb") {
- addDataSourceProperty("properties", "useUnicode=true;characterEncoding=utf8")
- } else if (dialectName.toLowerCase() == "h2") {
- dataSourceProperties.remove("serverName")
- dataSourceProperties.remove("port")
- dataSourceProperties.remove("databaseName")
- addDataSourceProperty("url", "jdbc:h2:${if (address.isBlank()) "" else "tcp://$address/"}~/${dco.database}")
- } else if (dialectName.toLowerCase() == "mysql") {
- // doesn't exist on the MariaDB driver
- addDataSourceProperty("cachePrepStmts", "true")
- addDataSourceProperty("alwaysSendSetIsolation", "false")
- addDataSourceProperty("cacheServerConfiguration", "true")
- addDataSourceProperty("elideSetAutoCommits", "true")
- addDataSourceProperty("useLocalSessionState", "true")
-
- // already set as default on mariadb
- addDataSourceProperty("useServerPrepStmts", "true")
- addDataSourceProperty("prepStmtCacheSize", "250")
- addDataSourceProperty("prepStmtCacheSqlLimit", "2048")
- addDataSourceProperty("cacheCallableStmts", "true")
-
- // make sure unicode characters can be used.
- addDataSourceProperty("characterEncoding", "utf8")
- addDataSourceProperty("useUnicode", "true")
- } else {
-
- }*/
-}
+package io.dico.parcels2.storage
+
+import com.zaxxer.hikari.HikariConfig
+import io.dico.parcels2.options.DataConnectionOptions
+
+fun getHikariConfig(dialectName: String,
+ dco: DataConnectionOptions): HikariConfig = HikariConfig().apply {
+
+ val (address, port) = dco.splitAddressAndPort() ?: throw IllegalArgumentException("Invalid address: ${dco.address}")
+
+ when (dialectName) {
+ "postgresql" -> run {
+ dataSourceClassName = "org.postgresql.ds.PGSimpleDataSource"
+ dataSourceProperties["serverName"] = address
+ dataSourceProperties["portNumber"] = port.toString()
+ dataSourceProperties["databaseName"] = dco.database
+ }
+
+ "mariadb" -> run {
+ dataSourceClassName = "org.mariadb.jdbc.MariaDbDataSource"
+ dataSourceProperties["serverName"] = address
+ dataSourceProperties["port"] = port.toString()
+ dataSourceProperties["databaseName"] = dco.database
+ dataSourceProperties["properties"] = "useUnicode=true;characterEncoding=utf8"
+ }
+
+ else -> throw IllegalArgumentException("Unsupported dialect: $dialectName")
+ }
+
+ poolName = "parcels"
+ maximumPoolSize = dco.poolSize
+ username = dco.username
+ password = dco.password
+ connectionTimeout = 15000
+ leakDetectionThreshold = 10000
+ connectionTestQuery = "SELECT 1"
+
+
+ /*
+
+ addDataSourceProperty("serverName", address)
+ addDataSourceProperty("port", port.toString())
+ addDataSourceProperty("databaseName", dco.database)
+
+ // copied from github.com/lucko/LuckPerms
+ if (dialectName.toLowerCase() == "mariadb") {
+ addDataSourceProperty("properties", "useUnicode=true;characterEncoding=utf8")
+ } else if (dialectName.toLowerCase() == "h2") {
+ dataSourceProperties.remove("serverName")
+ dataSourceProperties.remove("port")
+ dataSourceProperties.remove("databaseName")
+ addDataSourceProperty("url", "jdbc:h2:${if (address.isBlank()) "" else "tcp://$address/"}~/${dco.database}")
+ } else if (dialectName.toLowerCase() == "mysql") {
+ // doesn't exist on the MariaDB driver
+ addDataSourceProperty("cachePrepStmts", "true")
+ addDataSourceProperty("alwaysSendSetIsolation", "false")
+ addDataSourceProperty("cacheServerConfiguration", "true")
+ addDataSourceProperty("elideSetAutoCommits", "true")
+ addDataSourceProperty("useLocalSessionState", "true")
+
+ // already set as default on mariadb
+ addDataSourceProperty("useServerPrepStmts", "true")
+ addDataSourceProperty("prepStmtCacheSize", "250")
+ addDataSourceProperty("prepStmtCacheSqlLimit", "2048")
+ addDataSourceProperty("cacheCallableStmts", "true")
+
+ // make sure unicode characters can be used.
+ addDataSourceProperty("characterEncoding", "utf8")
+ addDataSourceProperty("useUnicode", "true")
+ } else {
+
+ }*/
+}
diff --git a/src/main/kotlin/io/dico/parcels2/storage/Storage.kt b/src/main/kotlin/io/dico/parcels2/storage/Storage.kt
index d718f20..f76aad6 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/Storage.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/Storage.kt
@@ -1,114 +1,114 @@
-@file:Suppress("NOTHING_TO_INLINE")
-
-package io.dico.parcels2.storage
-
-import io.dico.parcels2.*
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Deferred
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.channels.ReceiveChannel
-import kotlinx.coroutines.channels.SendChannel
-import kotlinx.coroutines.launch
-import org.joda.time.DateTime
-import java.util.UUID
-import kotlin.coroutines.CoroutineContext
-
-typealias DataPair = Pair<ParcelId, ParcelDataHolder?>
-typealias PrivilegePair<TAttach> = Pair<TAttach, PrivilegesHolder>
-
-interface Storage {
- val name: String
- val isConnected: Boolean
-
- fun init(): Job
-
- fun shutdown(): Job
-
-
- fun getWorldCreationTime(worldId: ParcelWorldId): Deferred<DateTime?>
-
- fun setWorldCreationTime(worldId: ParcelWorldId, time: DateTime): Job
-
- fun getPlayerUuidForName(name: String): Deferred<UUID?>
-
- fun updatePlayerName(uuid: UUID, name: String): Job
-
- fun readParcelData(parcel: ParcelId): Deferred<ParcelDataHolder?>
-
- fun transmitParcelData(parcels: Sequence<ParcelId>): ReceiveChannel<DataPair>
-
- fun transmitAllParcelData(): ReceiveChannel<DataPair>
-
- fun getOwnedParcels(user: PlayerProfile): Deferred<List<ParcelId>>
-
- fun getNumParcels(user: PlayerProfile): Deferred<Int>
-
-
- fun setParcelData(parcel: ParcelId, data: ParcelDataHolder?): Job
-
- fun setParcelOwner(parcel: ParcelId, owner: PlayerProfile?): Job
-
- fun setParcelOwnerSignOutdated(parcel: ParcelId, outdated: Boolean): Job
-
- fun setLocalPrivilege(parcel: ParcelId, player: PlayerProfile, privilege: Privilege): Job
-
- fun setParcelOptionsInteractConfig(parcel: ParcelId, config: InteractableConfiguration): Job
-
-
- fun transmitAllGlobalPrivileges(): ReceiveChannel<PrivilegePair<PlayerProfile>>
-
- fun readGlobalPrivileges(owner: PlayerProfile): Deferred<PrivilegesHolder?>
-
- fun setGlobalPrivilege(owner: PlayerProfile, player: PlayerProfile, privilege: Privilege): Job
-
-
- fun getChannelToUpdateParcelData(): SendChannel<Pair<ParcelId, ParcelDataHolder>>
-}
-
-class BackedStorage internal constructor(val b: Backing) : Storage, CoroutineScope {
- override val name get() = b.name
- override val isConnected get() = b.isConnected
- override val coroutineContext: CoroutineContext get() = b.coroutineContext
-
- override fun init() = launch { b.init() }
-
- override fun shutdown() = launch { b.shutdown() }
-
-
- override fun getWorldCreationTime(worldId: ParcelWorldId): Deferred<DateTime?> = b.launchFuture { b.getWorldCreationTime(worldId) }
-
- override fun setWorldCreationTime(worldId: ParcelWorldId, time: DateTime): Job = b.launchJob { b.setWorldCreationTime(worldId, time) }
-
- override fun getPlayerUuidForName(name: String): Deferred<UUID?> = b.launchFuture { b.getPlayerUuidForName(name) }
-
- override fun updatePlayerName(uuid: UUID, name: String): Job = b.launchJob { b.updatePlayerName(uuid, name) }
-
- override fun readParcelData(parcel: ParcelId) = b.launchFuture { b.readParcelData(parcel) }
-
- override fun transmitParcelData(parcels: Sequence<ParcelId>) = b.openChannel<DataPair> { b.transmitParcelData(it, parcels) }
-
- override fun transmitAllParcelData() = b.openChannel<DataPair> { b.transmitAllParcelData(it) }
-
- override fun getOwnedParcels(user: PlayerProfile) = b.launchFuture { b.getOwnedParcels(user) }
-
- override fun getNumParcels(user: PlayerProfile) = b.launchFuture { b.getNumParcels(user) }
-
- override fun setParcelData(parcel: ParcelId, data: ParcelDataHolder?) = b.launchJob { b.setParcelData(parcel, data) }
-
- override fun setParcelOwner(parcel: ParcelId, owner: PlayerProfile?) = b.launchJob { b.setParcelOwner(parcel, owner) }
-
- override fun setParcelOwnerSignOutdated(parcel: ParcelId, outdated: Boolean): Job = b.launchJob { b.setParcelOwnerSignOutdated(parcel, outdated) }
-
- override fun setLocalPrivilege(parcel: ParcelId, player: PlayerProfile, privilege: Privilege) = b.launchJob { b.setLocalPrivilege(parcel, player, privilege) }
-
- override fun setParcelOptionsInteractConfig(parcel: ParcelId, config: InteractableConfiguration) = b.launchJob { b.setParcelOptionsInteractConfig(parcel, config) }
-
-
- override fun transmitAllGlobalPrivileges(): ReceiveChannel<PrivilegePair<PlayerProfile>> = b.openChannel { b.transmitAllGlobalPrivileges(it) }
-
- override fun readGlobalPrivileges(owner: PlayerProfile): Deferred<PrivilegesHolder?> = b.launchFuture { b.readGlobalPrivileges(owner) }
-
- override fun setGlobalPrivilege(owner: PlayerProfile, player: PlayerProfile, privilege: Privilege) = b.launchJob { b.setGlobalPrivilege(owner, player, privilege) }
-
- override fun getChannelToUpdateParcelData(): SendChannel<Pair<ParcelId, ParcelDataHolder>> = b.openChannelForWriting { b.setParcelData(it.first, it.second) }
-}
+@file:Suppress("NOTHING_TO_INLINE")
+
+package io.dico.parcels2.storage
+
+import io.dico.parcels2.*
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.channels.ReceiveChannel
+import kotlinx.coroutines.channels.SendChannel
+import kotlinx.coroutines.launch
+import org.joda.time.DateTime
+import java.util.UUID
+import kotlin.coroutines.CoroutineContext
+
+typealias DataPair = Pair<ParcelId, ParcelDataHolder?>
+typealias PrivilegePair<TAttach> = Pair<TAttach, PrivilegesHolder>
+
+interface Storage {
+ val name: String
+ val isConnected: Boolean
+
+ fun init(): Job
+
+ fun shutdown(): Job
+
+
+ fun getWorldCreationTime(worldId: ParcelWorldId): Deferred<DateTime?>
+
+ fun setWorldCreationTime(worldId: ParcelWorldId, time: DateTime): Job
+
+ fun getPlayerUuidForName(name: String): Deferred<UUID?>
+
+ fun updatePlayerName(uuid: UUID, name: String): Job
+
+ fun readParcelData(parcel: ParcelId): Deferred<ParcelDataHolder?>
+
+ fun transmitParcelData(parcels: Sequence<ParcelId>): ReceiveChannel<DataPair>
+
+ fun transmitAllParcelData(): ReceiveChannel<DataPair>
+
+ fun getOwnedParcels(user: PlayerProfile): Deferred<List<ParcelId>>
+
+ fun getNumParcels(user: PlayerProfile): Deferred<Int>
+
+
+ fun setParcelData(parcel: ParcelId, data: ParcelDataHolder?): Job
+
+ fun setParcelOwner(parcel: ParcelId, owner: PlayerProfile?): Job
+
+ fun setParcelOwnerSignOutdated(parcel: ParcelId, outdated: Boolean): Job
+
+ fun setLocalPrivilege(parcel: ParcelId, player: PlayerProfile, privilege: Privilege): Job
+
+ fun setParcelOptionsInteractConfig(parcel: ParcelId, config: InteractableConfiguration): Job
+
+
+ fun transmitAllGlobalPrivileges(): ReceiveChannel<PrivilegePair<PlayerProfile>>
+
+ fun readGlobalPrivileges(owner: PlayerProfile): Deferred<PrivilegesHolder?>
+
+ fun setGlobalPrivilege(owner: PlayerProfile, player: PlayerProfile, privilege: Privilege): Job
+
+
+ fun getChannelToUpdateParcelData(): SendChannel<Pair<ParcelId, ParcelDataHolder>>
+}
+
+class BackedStorage internal constructor(val b: Backing) : Storage, CoroutineScope {
+ override val name get() = b.name
+ override val isConnected get() = b.isConnected
+ override val coroutineContext: CoroutineContext get() = b.coroutineContext
+
+ override fun init() = launch { b.init() }
+
+ override fun shutdown() = launch { b.shutdown() }
+
+
+ override fun getWorldCreationTime(worldId: ParcelWorldId): Deferred<DateTime?> = b.launchFuture { b.getWorldCreationTime(worldId) }
+
+ override fun setWorldCreationTime(worldId: ParcelWorldId, time: DateTime): Job = b.launchJob { b.setWorldCreationTime(worldId, time) }
+
+ override fun getPlayerUuidForName(name: String): Deferred<UUID?> = b.launchFuture { b.getPlayerUuidForName(name) }
+
+ override fun updatePlayerName(uuid: UUID, name: String): Job = b.launchJob { b.updatePlayerName(uuid, name) }
+
+ override fun readParcelData(parcel: ParcelId) = b.launchFuture { b.readParcelData(parcel) }
+
+ override fun transmitParcelData(parcels: Sequence<ParcelId>) = b.openChannel<DataPair> { b.transmitParcelData(it, parcels) }
+
+ override fun transmitAllParcelData() = b.openChannel<DataPair> { b.transmitAllParcelData(it) }
+
+ override fun getOwnedParcels(user: PlayerProfile) = b.launchFuture { b.getOwnedParcels(user) }
+
+ override fun getNumParcels(user: PlayerProfile) = b.launchFuture { b.getNumParcels(user) }
+
+ override fun setParcelData(parcel: ParcelId, data: ParcelDataHolder?) = b.launchJob { b.setParcelData(parcel, data) }
+
+ override fun setParcelOwner(parcel: ParcelId, owner: PlayerProfile?) = b.launchJob { b.setParcelOwner(parcel, owner) }
+
+ override fun setParcelOwnerSignOutdated(parcel: ParcelId, outdated: Boolean): Job = b.launchJob { b.setParcelOwnerSignOutdated(parcel, outdated) }
+
+ override fun setLocalPrivilege(parcel: ParcelId, player: PlayerProfile, privilege: Privilege) = b.launchJob { b.setLocalPrivilege(parcel, player, privilege) }
+
+ override fun setParcelOptionsInteractConfig(parcel: ParcelId, config: InteractableConfiguration) = b.launchJob { b.setParcelOptionsInteractConfig(parcel, config) }
+
+
+ override fun transmitAllGlobalPrivileges(): ReceiveChannel<PrivilegePair<PlayerProfile>> = b.openChannel { b.transmitAllGlobalPrivileges(it) }
+
+ override fun readGlobalPrivileges(owner: PlayerProfile): Deferred<PrivilegesHolder?> = b.launchFuture { b.readGlobalPrivileges(owner) }
+
+ override fun setGlobalPrivilege(owner: PlayerProfile, player: PlayerProfile, privilege: Privilege) = b.launchJob { b.setGlobalPrivilege(owner, player, privilege) }
+
+ override fun getChannelToUpdateParcelData(): SendChannel<Pair<ParcelId, ParcelDataHolder>> = b.openChannelForWriting { b.setParcelData(it.first, it.second) }
+}
diff --git a/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt b/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt
index e49be79..32065bc 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedBacking.kt
@@ -1,282 +1,282 @@
-@file:Suppress("NOTHING_TO_INLINE", "PARAMETER_NAME_CHANGED_ON_OVERRIDE", "LocalVariableName", "UNUSED_EXPRESSION")
-
-package io.dico.parcels2.storage.exposed
-
-import com.zaxxer.hikari.HikariDataSource
-import io.dico.parcels2.*
-import io.dico.parcels2.PlayerProfile.Star.name
-import io.dico.parcels2.storage.*
-import io.dico.parcels2.util.math.clampMax
-import io.dico.parcels2.util.ext.synchronized
-import kotlinx.coroutines.*
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.channels.ArrayChannel
-import kotlinx.coroutines.channels.LinkedListChannel
-import kotlinx.coroutines.channels.ReceiveChannel
-import kotlinx.coroutines.channels.SendChannel
-import org.jetbrains.exposed.sql.*
-import org.jetbrains.exposed.sql.SchemaUtils.create
-import org.jetbrains.exposed.sql.transactions.transaction
-import org.jetbrains.exposed.sql.vendors.DatabaseDialect
-import org.joda.time.DateTime
-import java.util.UUID
-import javax.sql.DataSource
-
-class ExposedDatabaseException(message: String? = null) : Exception(message)
-
-class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSize: Int) : Backing, CoroutineScope {
- override val name get() = "Exposed"
- override val coroutineContext = Job() + newFixedThreadPoolContext(poolSize, "Parcels StorageThread")
- private var dataSource: DataSource? = null
- private var database: Database? = null
- private var isShutdown: Boolean = false
- override val isConnected get() = database != null
-
- override fun launchJob(job: Backing.() -> Unit): Job = launch { transaction { job() } }
- override fun <T> launchFuture(future: Backing.() -> T): Deferred<T> = async { transaction { future() } }
-
- override fun <T> openChannel(future: Backing.(SendChannel<T>) -> Unit): ReceiveChannel<T> {
- val channel = LinkedListChannel<T>()
- launchJob { future(channel) }
- return channel
- }
-
- override fun <T> openChannelForWriting(action: Backing.(T) -> Unit): SendChannel<T> {
- val channel = ArrayChannel<T>(poolSize * 2)
-
- repeat(poolSize.clampMax(3)) {
- launch {
- try {
- while (true) {
- action(channel.receive())
- }
- } catch (ex: Exception) {
- // channel closed
- }
- }
- }
-
- return channel
- }
-
- private fun <T> transaction(statement: Transaction.() -> T) = transaction(database!!, statement)
-
- companion object {
- init {
- Database.registerDialect("mariadb") {
- Class.forName("org.jetbrains.exposed.sql.vendors.MysqlDialect").newInstance() as DatabaseDialect
- }
- }
- }
-
- override fun init() {
- synchronized {
- if (isShutdown || isConnected) throw IllegalStateException()
- dataSource = dataSourceFactory()
- database = Database.connect(dataSource!!)
- transaction(database!!) {
- create(WorldsT, ProfilesT, ParcelsT, ParcelOptionsT, PrivilegesLocalT, PrivilegesGlobalT)
- }
- }
- }
-
- override fun shutdown() {
- synchronized {
- if (isShutdown) throw IllegalStateException()
- isShutdown = true
- coroutineContext[Job]!!.cancel(CancellationException("ExposedBacking shutdown"))
- dataSource?.let {
- (it as? HikariDataSource)?.close()
- }
- database = null
- }
- }
-
- @Suppress("RedundantObjectTypeCheck")
- private fun PlayerProfile.toOwnerProfile(): PlayerProfile {
- if (this is PlayerProfile.Star) return PlayerProfile.Fake(name)
- return this
- }
-
- private fun PlayerProfile.Unresolved.toResolvedProfile(): PlayerProfile.Real {
- return resolve(getPlayerUuidForName(name) ?: throwException())
- }
-
- private fun PlayerProfile.toResolvedProfile(): PlayerProfile {
- if (this is PlayerProfile.Unresolved) return toResolvedProfile()
- return this
- }
-
- private fun PlayerProfile.toRealProfile(): PlayerProfile.Real = when (this) {
- is PlayerProfile.Real -> this
- is PlayerProfile.Fake -> throw IllegalArgumentException("Fake profiles are not accepted")
- is PlayerProfile.Unresolved -> toResolvedProfile()
- else -> throw InternalError("Case should not be reached")
- }
-
-
- override fun getWorldCreationTime(worldId: ParcelWorldId): DateTime? {
- return WorldsT.getWorldCreationTime(worldId)
- }
-
- override fun setWorldCreationTime(worldId: ParcelWorldId, time: DateTime) {
- WorldsT.setWorldCreationTime(worldId, time)
- }
-
- override fun getPlayerUuidForName(name: String): UUID? {
- return ProfilesT.slice(ProfilesT.uuid).select { ProfilesT.name.upperCase() eq name.toUpperCase() }
- .firstOrNull()?.let { it[ProfilesT.uuid]?.toUUID() }
- }
-
- override fun updatePlayerName(uuid: UUID, name: String) {
- val binaryUuid = uuid.toByteArray()
- ProfilesT.upsert(ProfilesT.uuid) {
- it[ProfilesT.uuid] = binaryUuid
- it[ProfilesT.name] = name
- }
- }
-
- override fun transmitParcelData(channel: SendChannel<DataPair>, parcels: Sequence<ParcelId>) {
- for (parcel in parcels) {
- val data = readParcelData(parcel)
- channel.offer(parcel to data)
- }
- channel.close()
- }
-
- override fun transmitAllParcelData(channel: SendChannel<DataPair>) {
- ParcelsT.selectAll().forEach { row ->
- val parcel = ParcelsT.getItem(row) ?: return@forEach
- val data = rowToParcelData(row)
- channel.offer(parcel to data)
- }
- channel.close()
- }
-
- override fun readParcelData(parcel: ParcelId): ParcelDataHolder? {
- val row = ParcelsT.getRow(parcel) ?: return null
- return rowToParcelData(row)
- }
-
- override fun getOwnedParcels(user: PlayerProfile): List<ParcelId> {
- val user_id = ProfilesT.getId(user.toOwnerProfile()) ?: return emptyList()
- return ParcelsT.select { ParcelsT.owner_id eq user_id }
- .orderBy(ParcelsT.claim_time, isAsc = true)
- .mapNotNull(ParcelsT::getItem)
- .toList()
- }
-
- override fun setParcelData(parcel: ParcelId, data: ParcelDataHolder?) {
- if (data == null) {
- transaction {
- ParcelsT.getId(parcel)?.let { id ->
- ParcelsT.deleteIgnoreWhere { ParcelsT.id eq id }
-
- // Below should cascade automatically
- /*
- PrivilegesLocalT.deleteIgnoreWhere { PrivilegesLocalT.parcel_id eq id }
- ParcelOptionsT.deleteIgnoreWhere(limit = 1) { ParcelOptionsT.parcel_id eq id }
- */
- }
-
- }
- return
- }
-
- transaction {
- val id = ParcelsT.getOrInitId(parcel)
- PrivilegesLocalT.deleteIgnoreWhere { PrivilegesLocalT.attach_id eq id }
- }
-
- setParcelOwner(parcel, data.owner)
-
- for ((profile, privilege) in data.privilegeMap) {
- PrivilegesLocalT.setPrivilege(parcel, profile, privilege)
- }
-
- data.privilegeOfStar.takeIf { it != Privilege.DEFAULT }?.let { privilege ->
- PrivilegesLocalT.setPrivilege(parcel, PlayerProfile.Star, privilege)
- }
-
- setParcelOptionsInteractConfig(parcel, data.interactableConfig)
- }
-
- override fun setParcelOwner(parcel: ParcelId, owner: PlayerProfile?) {
- val id = if (owner == null)
- ParcelsT.getId(parcel) ?: return
- else
- ParcelsT.getOrInitId(parcel)
-
- val owner_id = owner?.let { ProfilesT.getOrInitId(it.toOwnerProfile()) }
- val time = owner?.let { DateTime.now() }
-
- ParcelsT.update({ ParcelsT.id eq id }) {
- it[ParcelsT.owner_id] = owner_id
- it[claim_time] = time
- it[sign_oudated] = false
- }
- }
-
- override fun setParcelOwnerSignOutdated(parcel: ParcelId, outdated: Boolean) {
- val id = ParcelsT.getId(parcel) ?: return
- ParcelsT.update({ ParcelsT.id eq id }) {
- it[sign_oudated] = outdated
- }
- }
-
- override fun setLocalPrivilege(parcel: ParcelId, player: PlayerProfile, privilege: Privilege) {
- PrivilegesLocalT.setPrivilege(parcel, player.toRealProfile(), privilege)
- }
-
- override fun setParcelOptionsInteractConfig(parcel: ParcelId, config: InteractableConfiguration) {
- val bitmaskArray = (config as? BitmaskInteractableConfiguration ?: return).bitmaskArray
- val isAllZero = !bitmaskArray.fold(false) { cur, elem -> cur || elem != 0 }
-
- if (isAllZero) {
- val id = ParcelsT.getId(parcel) ?: return
- ParcelOptionsT.deleteWhere { ParcelOptionsT.parcel_id eq id }
- return
- }
-
- if (bitmaskArray.size != 1) throw IllegalArgumentException()
- val array = bitmaskArray.toByteArray()
- val id = ParcelsT.getOrInitId(parcel)
- ParcelOptionsT.upsert(ParcelOptionsT.parcel_id) {
- it[parcel_id] = id
- it[interact_bitmask] = array
- }
- }
-
- override fun transmitAllGlobalPrivileges(channel: SendChannel<PrivilegePair<PlayerProfile>>) {
- PrivilegesGlobalT.sendAllPrivilegesH(channel)
- channel.close()
- }
-
- override fun readGlobalPrivileges(owner: PlayerProfile): PrivilegesHolder? {
- return PrivilegesGlobalT.readPrivileges(ProfilesT.getId(owner.toOwnerProfile()) ?: return null)
- }
-
- override fun setGlobalPrivilege(owner: PlayerProfile, player: PlayerProfile, privilege: Privilege) {
- PrivilegesGlobalT.setPrivilege(owner, player.toRealProfile(), privilege)
- }
-
- private fun rowToParcelData(row: ResultRow) = ParcelDataHolder().apply {
- owner = row[ParcelsT.owner_id]?.let { ProfilesT.getItem(it) }
- lastClaimTime = row[ParcelsT.claim_time]
- isOwnerSignOutdated = row[ParcelsT.sign_oudated]
-
- val id = row[ParcelsT.id]
- ParcelOptionsT.select { ParcelOptionsT.parcel_id eq id }.firstOrNull()?.let { optrow ->
- val source = optrow[ParcelOptionsT.interact_bitmask].toIntArray()
- val target = (interactableConfig as? BitmaskInteractableConfiguration ?: return@let).bitmaskArray
- System.arraycopy(source, 0, target, 0, source.size.clampMax(target.size))
- }
-
- val privileges = PrivilegesLocalT.readPrivileges(id)
- if (privileges != null) {
- copyPrivilegesFrom(privileges)
- }
- }
-
-}
-
+@file:Suppress("NOTHING_TO_INLINE", "PARAMETER_NAME_CHANGED_ON_OVERRIDE", "LocalVariableName", "UNUSED_EXPRESSION")
+
+package io.dico.parcels2.storage.exposed
+
+import com.zaxxer.hikari.HikariDataSource
+import io.dico.parcels2.*
+import io.dico.parcels2.PlayerProfile.Star.name
+import io.dico.parcels2.storage.*
+import io.dico.parcels2.util.math.clampMax
+import io.dico.parcels2.util.ext.synchronized
+import kotlinx.coroutines.*
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.channels.ArrayChannel
+import kotlinx.coroutines.channels.LinkedListChannel
+import kotlinx.coroutines.channels.ReceiveChannel
+import kotlinx.coroutines.channels.SendChannel
+import org.jetbrains.exposed.sql.*
+import org.jetbrains.exposed.sql.SchemaUtils.create
+import org.jetbrains.exposed.sql.transactions.transaction
+import org.jetbrains.exposed.sql.vendors.DatabaseDialect
+import org.joda.time.DateTime
+import java.util.UUID
+import javax.sql.DataSource
+
+class ExposedDatabaseException(message: String? = null) : Exception(message)
+
+class ExposedBacking(private val dataSourceFactory: () -> DataSource, val poolSize: Int) : Backing, CoroutineScope {
+ override val name get() = "Exposed"
+ override val coroutineContext = Job() + newFixedThreadPoolContext(poolSize, "Parcels StorageThread")
+ private var dataSource: DataSource? = null
+ private var database: Database? = null
+ private var isShutdown: Boolean = false
+ override val isConnected get() = database != null
+
+ override fun launchJob(job: Backing.() -> Unit): Job = launch { transaction { job() } }
+ override fun <T> launchFuture(future: Backing.() -> T): Deferred<T> = async { transaction { future() } }
+
+ override fun <T> openChannel(future: Backing.(SendChannel<T>) -> Unit): ReceiveChannel<T> {
+ val channel = LinkedListChannel<T>()
+ launchJob { future(channel) }
+ return channel
+ }
+
+ override fun <T> openChannelForWriting(action: Backing.(T) -> Unit): SendChannel<T> {
+ val channel = ArrayChannel<T>(poolSize * 2)
+
+ repeat(poolSize.clampMax(3)) {
+ launch {
+ try {
+ while (true) {
+ action(channel.receive())
+ }
+ } catch (ex: Exception) {
+ // channel closed
+ }
+ }
+ }
+
+ return channel
+ }
+
+ private fun <T> transaction(statement: Transaction.() -> T) = transaction(database!!, statement)
+
+ companion object {
+ init {
+ Database.registerDialect("mariadb") {
+ Class.forName("org.jetbrains.exposed.sql.vendors.MysqlDialect").newInstance() as DatabaseDialect
+ }
+ }
+ }
+
+ override fun init() {
+ synchronized {
+ if (isShutdown || isConnected) throw IllegalStateException()
+ dataSource = dataSourceFactory()
+ database = Database.connect(dataSource!!)
+ transaction(database!!) {
+ create(WorldsT, ProfilesT, ParcelsT, ParcelOptionsT, PrivilegesLocalT, PrivilegesGlobalT)
+ }
+ }
+ }
+
+ override fun shutdown() {
+ synchronized {
+ if (isShutdown) throw IllegalStateException()
+ isShutdown = true
+ coroutineContext[Job]!!.cancel(CancellationException("ExposedBacking shutdown"))
+ dataSource?.let {
+ (it as? HikariDataSource)?.close()
+ }
+ database = null
+ }
+ }
+
+ @Suppress("RedundantObjectTypeCheck")
+ private fun PlayerProfile.toOwnerProfile(): PlayerProfile {
+ if (this is PlayerProfile.Star) return PlayerProfile.Fake(name)
+ return this
+ }
+
+ private fun PlayerProfile.Unresolved.toResolvedProfile(): PlayerProfile.Real {
+ return resolve(getPlayerUuidForName(name) ?: throwException())
+ }
+
+ private fun PlayerProfile.toResolvedProfile(): PlayerProfile {
+ if (this is PlayerProfile.Unresolved) return toResolvedProfile()
+ return this
+ }
+
+ private fun PlayerProfile.toRealProfile(): PlayerProfile.Real = when (this) {
+ is PlayerProfile.Real -> this
+ is PlayerProfile.Fake -> throw IllegalArgumentException("Fake profiles are not accepted")
+ is PlayerProfile.Unresolved -> toResolvedProfile()
+ else -> throw InternalError("Case should not be reached")
+ }
+
+
+ override fun getWorldCreationTime(worldId: ParcelWorldId): DateTime? {
+ return WorldsT.getWorldCreationTime(worldId)
+ }
+
+ override fun setWorldCreationTime(worldId: ParcelWorldId, time: DateTime) {
+ WorldsT.setWorldCreationTime(worldId, time)
+ }
+
+ override fun getPlayerUuidForName(name: String): UUID? {
+ return ProfilesT.slice(ProfilesT.uuid).select { ProfilesT.name.upperCase() eq name.toUpperCase() }
+ .firstOrNull()?.let { it[ProfilesT.uuid]?.toUUID() }
+ }
+
+ override fun updatePlayerName(uuid: UUID, name: String) {
+ val binaryUuid = uuid.toByteArray()
+ ProfilesT.upsert(ProfilesT.uuid) {
+ it[ProfilesT.uuid] = binaryUuid
+ it[ProfilesT.name] = name
+ }
+ }
+
+ override fun transmitParcelData(channel: SendChannel<DataPair>, parcels: Sequence<ParcelId>) {
+ for (parcel in parcels) {
+ val data = readParcelData(parcel)
+ channel.offer(parcel to data)
+ }
+ channel.close()
+ }
+
+ override fun transmitAllParcelData(channel: SendChannel<DataPair>) {
+ ParcelsT.selectAll().forEach { row ->
+ val parcel = ParcelsT.getItem(row) ?: return@forEach
+ val data = rowToParcelData(row)
+ channel.offer(parcel to data)
+ }
+ channel.close()
+ }
+
+ override fun readParcelData(parcel: ParcelId): ParcelDataHolder? {
+ val row = ParcelsT.getRow(parcel) ?: return null
+ return rowToParcelData(row)
+ }
+
+ override fun getOwnedParcels(user: PlayerProfile): List<ParcelId> {
+ val user_id = ProfilesT.getId(user.toOwnerProfile()) ?: return emptyList()
+ return ParcelsT.select { ParcelsT.owner_id eq user_id }
+ .orderBy(ParcelsT.claim_time, isAsc = true)
+ .mapNotNull(ParcelsT::getItem)
+ .toList()
+ }
+
+ override fun setParcelData(parcel: ParcelId, data: ParcelDataHolder?) {
+ if (data == null) {
+ transaction {
+ ParcelsT.getId(parcel)?.let { id ->
+ ParcelsT.deleteIgnoreWhere { ParcelsT.id eq id }
+
+ // Below should cascade automatically
+ /*
+ PrivilegesLocalT.deleteIgnoreWhere { PrivilegesLocalT.parcel_id eq id }
+ ParcelOptionsT.deleteIgnoreWhere(limit = 1) { ParcelOptionsT.parcel_id eq id }
+ */
+ }
+
+ }
+ return
+ }
+
+ transaction {
+ val id = ParcelsT.getOrInitId(parcel)
+ PrivilegesLocalT.deleteIgnoreWhere { PrivilegesLocalT.attach_id eq id }
+ }
+
+ setParcelOwner(parcel, data.owner)
+
+ for ((profile, privilege) in data.privilegeMap) {
+ PrivilegesLocalT.setPrivilege(parcel, profile, privilege)
+ }
+
+ data.privilegeOfStar.takeIf { it != Privilege.DEFAULT }?.let { privilege ->
+ PrivilegesLocalT.setPrivilege(parcel, PlayerProfile.Star, privilege)
+ }
+
+ setParcelOptionsInteractConfig(parcel, data.interactableConfig)
+ }
+
+ override fun setParcelOwner(parcel: ParcelId, owner: PlayerProfile?) {
+ val id = if (owner == null)
+ ParcelsT.getId(parcel) ?: return
+ else
+ ParcelsT.getOrInitId(parcel)
+
+ val owner_id = owner?.let { ProfilesT.getOrInitId(it.toOwnerProfile()) }
+ val time = owner?.let { DateTime.now() }
+
+ ParcelsT.update({ ParcelsT.id eq id }) {
+ it[ParcelsT.owner_id] = owner_id
+ it[claim_time] = time
+ it[sign_oudated] = false
+ }
+ }
+
+ override fun setParcelOwnerSignOutdated(parcel: ParcelId, outdated: Boolean) {
+ val id = ParcelsT.getId(parcel) ?: return
+ ParcelsT.update({ ParcelsT.id eq id }) {
+ it[sign_oudated] = outdated
+ }
+ }
+
+ override fun setLocalPrivilege(parcel: ParcelId, player: PlayerProfile, privilege: Privilege) {
+ PrivilegesLocalT.setPrivilege(parcel, player.toRealProfile(), privilege)
+ }
+
+ override fun setParcelOptionsInteractConfig(parcel: ParcelId, config: InteractableConfiguration) {
+ val bitmaskArray = (config as? BitmaskInteractableConfiguration ?: return).bitmaskArray
+ val isAllZero = !bitmaskArray.fold(false) { cur, elem -> cur || elem != 0 }
+
+ if (isAllZero) {
+ val id = ParcelsT.getId(parcel) ?: return
+ ParcelOptionsT.deleteWhere { ParcelOptionsT.parcel_id eq id }
+ return
+ }
+
+ if (bitmaskArray.size != 1) throw IllegalArgumentException()
+ val array = bitmaskArray.toByteArray()
+ val id = ParcelsT.getOrInitId(parcel)
+ ParcelOptionsT.upsert(ParcelOptionsT.parcel_id) {
+ it[parcel_id] = id
+ it[interact_bitmask] = array
+ }
+ }
+
+ override fun transmitAllGlobalPrivileges(channel: SendChannel<PrivilegePair<PlayerProfile>>) {
+ PrivilegesGlobalT.sendAllPrivilegesH(channel)
+ channel.close()
+ }
+
+ override fun readGlobalPrivileges(owner: PlayerProfile): PrivilegesHolder? {
+ return PrivilegesGlobalT.readPrivileges(ProfilesT.getId(owner.toOwnerProfile()) ?: return null)
+ }
+
+ override fun setGlobalPrivilege(owner: PlayerProfile, player: PlayerProfile, privilege: Privilege) {
+ PrivilegesGlobalT.setPrivilege(owner, player.toRealProfile(), privilege)
+ }
+
+ private fun rowToParcelData(row: ResultRow) = ParcelDataHolder().apply {
+ owner = row[ParcelsT.owner_id]?.let { ProfilesT.getItem(it) }
+ lastClaimTime = row[ParcelsT.claim_time]
+ isOwnerSignOutdated = row[ParcelsT.sign_oudated]
+
+ val id = row[ParcelsT.id]
+ ParcelOptionsT.select { ParcelOptionsT.parcel_id eq id }.firstOrNull()?.let { optrow ->
+ val source = optrow[ParcelOptionsT.interact_bitmask].toIntArray()
+ val target = (interactableConfig as? BitmaskInteractableConfiguration ?: return@let).bitmaskArray
+ System.arraycopy(source, 0, target, 0, source.size.clampMax(target.size))
+ }
+
+ val privileges = PrivilegesLocalT.readPrivileges(id)
+ if (privileges != null) {
+ copyPrivilegesFrom(privileges)
+ }
+ }
+
+}
+
diff --git a/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedExtensions.kt b/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedExtensions.kt
index 0245625..7640c0c 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedExtensions.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/exposed/ExposedExtensions.kt
@@ -1,74 +1,74 @@
-package io.dico.parcels2.storage.exposed
-
-import org.jetbrains.exposed.sql.*
-import org.jetbrains.exposed.sql.Function
-import org.jetbrains.exposed.sql.statements.InsertStatement
-import org.jetbrains.exposed.sql.transactions.TransactionManager
-
-class UpsertStatement<Key : Any>(table: Table, conflictColumn: Column<*>? = null, conflictIndex: Index? = null) : InsertStatement<Key>(table, false) {
- val indexName: String
- val indexColumns: List<Column<*>>
-
- init {
- when {
- conflictIndex != null -> {
- indexName = conflictIndex.indexName
- indexColumns = conflictIndex.columns
- }
- conflictColumn != null -> {
- indexName = conflictColumn.name
- indexColumns = listOf(conflictColumn)
- }
- else -> throw IllegalArgumentException()
- }
- }
-
- override fun prepareSQL(transaction: Transaction) = buildString {
- append(super.prepareSQL(transaction))
-
- val dialect = transaction.db.vendor
- if (dialect == "postgresql") {
-
- append(" ON CONFLICT(")
- append(indexName)
- append(") DO UPDATE SET ")
-
- values.keys.filter { it !in indexColumns }.joinTo(this) { "${transaction.identity(it)}=EXCLUDED.${transaction.identity(it)}" }
-
- } else {
-
- append(" ON DUPLICATE KEY UPDATE ")
- values.keys.filter { it !in indexColumns }.joinTo(this) { "${transaction.identity(it)}=VALUES(${transaction.identity(it)})" }
-
- }
- }
-
-}
-
-inline fun <T : Table> T.upsert(conflictColumn: Column<*>? = null, conflictIndex: Index? = null, body: T.(UpsertStatement<Number>) -> Unit) =
- UpsertStatement<Number>(this, conflictColumn, conflictIndex).apply {
- body(this)
- execute(TransactionManager.current())
- }
-
-fun Table.indexR(customIndexName: String? = null, isUnique: Boolean = false, vararg columns: Column<*>): Index {
- val index = Index(columns.toList(), isUnique, customIndexName)
- indices.add(index)
- return index
-}
-
-fun Table.uniqueIndexR(customIndexName: String? = null, vararg columns: Column<*>): Index = indexR(customIndexName, true, *columns)
-
-fun <T : Int?> ExpressionWithColumnType<T>.abs(): Function<T> = Abs(this)
-
-class Abs<T : Int?>(val expr: Expression<T>) : Function<T>(IntegerColumnType()) {
- override fun toSQL(queryBuilder: QueryBuilder): String = "ABS(${expr.toSQL(queryBuilder)})"
-}
-
-fun <T : Comparable<T>> greaterOf(col1: ExpressionWithColumnType<T>, col2: ExpressionWithColumnType<T>): Expression<T> =
- with(SqlExpressionBuilder) {
- case(col1)
- .When(col1.greater(col2), col1)
- .Else(col2)
- }
-
+package io.dico.parcels2.storage.exposed
+
+import org.jetbrains.exposed.sql.*
+import org.jetbrains.exposed.sql.Function
+import org.jetbrains.exposed.sql.statements.InsertStatement
+import org.jetbrains.exposed.sql.transactions.TransactionManager
+
+class UpsertStatement<Key : Any>(table: Table, conflictColumn: Column<*>? = null, conflictIndex: Index? = null) : InsertStatement<Key>(table, false) {
+ val indexName: String
+ val indexColumns: List<Column<*>>
+
+ init {
+ when {
+ conflictIndex != null -> {
+ indexName = conflictIndex.indexName
+ indexColumns = conflictIndex.columns
+ }
+ conflictColumn != null -> {
+ indexName = conflictColumn.name
+ indexColumns = listOf(conflictColumn)
+ }
+ else -> throw IllegalArgumentException()
+ }
+ }
+
+ override fun prepareSQL(transaction: Transaction) = buildString {
+ append(super.prepareSQL(transaction))
+
+ val dialect = transaction.db.vendor
+ if (dialect == "postgresql") {
+
+ append(" ON CONFLICT(")
+ append(indexName)
+ append(") DO UPDATE SET ")
+
+ values.keys.filter { it !in indexColumns }.joinTo(this) { "${transaction.identity(it)}=EXCLUDED.${transaction.identity(it)}" }
+
+ } else {
+
+ append(" ON DUPLICATE KEY UPDATE ")
+ values.keys.filter { it !in indexColumns }.joinTo(this) { "${transaction.identity(it)}=VALUES(${transaction.identity(it)})" }
+
+ }
+ }
+
+}
+
+inline fun <T : Table> T.upsert(conflictColumn: Column<*>? = null, conflictIndex: Index? = null, body: T.(UpsertStatement<Number>) -> Unit) =
+ UpsertStatement<Number>(this, conflictColumn, conflictIndex).apply {
+ body(this)
+ execute(TransactionManager.current())
+ }
+
+fun Table.indexR(customIndexName: String? = null, isUnique: Boolean = false, vararg columns: Column<*>): Index {
+ val index = Index(columns.toList(), isUnique, customIndexName)
+ indices.add(index)
+ return index
+}
+
+fun Table.uniqueIndexR(customIndexName: String? = null, vararg columns: Column<*>): Index = indexR(customIndexName, true, *columns)
+
+fun <T : Int?> ExpressionWithColumnType<T>.abs(): Function<T> = Abs(this)
+
+class Abs<T : Int?>(val expr: Expression<T>) : Function<T>(IntegerColumnType()) {
+ override fun toSQL(queryBuilder: QueryBuilder): String = "ABS(${expr.toSQL(queryBuilder)})"
+}
+
+fun <T : Comparable<T>> greaterOf(col1: ExpressionWithColumnType<T>, col2: ExpressionWithColumnType<T>): Expression<T> =
+ with(SqlExpressionBuilder) {
+ case(col1)
+ .When(col1.greater(col2), col1)
+ .Else(col2)
+ }
+
diff --git a/src/main/kotlin/io/dico/parcels2/storage/exposed/IdTables.kt b/src/main/kotlin/io/dico/parcels2/storage/exposed/IdTables.kt
index 6f6ad6b..8428b3a 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/exposed/IdTables.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/exposed/IdTables.kt
@@ -1,165 +1,165 @@
-@file:Suppress("NOTHING_TO_INLINE", "PARAMETER_NAME_CHANGED_ON_OVERRIDE", "unused", "MemberVisibilityCanBePrivate")
-
-package io.dico.parcels2.storage.exposed
-
-import io.dico.parcels2.ParcelId
-import io.dico.parcels2.ParcelWorldId
-import io.dico.parcels2.PlayerProfile
-import io.dico.parcels2.storage.toByteArray
-import io.dico.parcels2.storage.toUUID
-import org.jetbrains.exposed.sql.*
-import org.jetbrains.exposed.sql.statements.UpdateBuilder
-import org.joda.time.DateTime
-import java.util.UUID
-
-abstract class IdTransactionsTable<TableT : IdTransactionsTable<TableT, QueryObj>, QueryObj>(tableName: String, columnName: String)
- : Table(tableName) {
- val id = integer(columnName).autoIncrement().primaryKey()
-
- @Suppress("UNCHECKED_CAST")
- inline val table: TableT
- get() = this as TableT
-
- internal inline fun getId(where: SqlExpressionBuilder.(TableT) -> Op<Boolean>): Int? {
- return select { where(table) }.firstOrNull()?.let { it[id] }
- }
-
- internal inline fun getOrInitId(getId: () -> Int?, noinline body: TableT.(UpdateBuilder<*>) -> Unit, objName: () -> String): Int {
- return getId() ?: table.insertIgnore(body)[id] ?: getId()
- ?: throw ExposedDatabaseException("This should not happen - failed to insert ${objName()} and get its number")
- }
-
- abstract fun getId(obj: QueryObj): Int?
- abstract fun getOrInitId(obj: QueryObj): Int
- fun getItem(id: Int): QueryObj? = select { this@IdTransactionsTable.id eq id }.firstOrNull()?.let { getItem(it) }
- abstract fun getItem(row: ResultRow): QueryObj?
-
- fun getId(obj: QueryObj, init: Boolean): Int? = if (init) getOrInitId(obj) else getId(obj)
-}
-
-object WorldsT : IdTransactionsTable<WorldsT, ParcelWorldId>("parcels_worlds", "world_id") {
- val name = varchar("name", 50)
- val uid = binary("uid", 16).nullable()
- val creation_time = datetime("creation_time").nullable()
- val index_name = uniqueIndexR("index_name", name)
- val index_uid = uniqueIndexR("index_uid", uid)
-
- internal inline fun getId(worldName: String, binaryUid: ByteArray?): Int? = getId { (name eq worldName).let { if (binaryUid == null) it else it or (uid eq binaryUid) } }
- internal inline fun getId(worldName: String, uid: UUID?): Int? = getId(worldName, uid?.toByteArray())
- internal inline fun getOrInitId(worldName: String, worldUid: UUID?): Int = worldUid?.toByteArray().let { binaryUid ->
- return getOrInitId(
- { getId(worldName, binaryUid) },
- { it[name] = worldName; it[uid] = binaryUid },
- { "world named $worldName" })
- }
-
- override fun getId(world: ParcelWorldId): Int? = getId(world.name, world.uid)
- override fun getOrInitId(world: ParcelWorldId): Int = getOrInitId(world.name, world.uid)
-
- override fun getItem(row: ResultRow): ParcelWorldId {
- return ParcelWorldId(row[name], row[uid]?.toUUID())
- }
-
- fun getWorldCreationTime(worldId: ParcelWorldId): DateTime? {
- val id = getId(worldId) ?: return null
- return select { WorldsT.id eq id }.firstOrNull()?.let { it[WorldsT.creation_time] }
- }
-
- fun setWorldCreationTime(worldId: ParcelWorldId, time: DateTime) {
- val id = getOrInitId(worldId)
- update({ WorldsT.id eq id }) {
- it[WorldsT.creation_time] = time
- }
- }
-}
-
-object ParcelsT : IdTransactionsTable<ParcelsT, ParcelId>("parcels", "parcel_id") {
- val world_id = integer("world_id").references(WorldsT.id)
- val px = integer("px")
- val pz = integer("pz")
- val owner_id = integer("owner_id").references(ProfilesT.id).nullable()
- val sign_oudated = bool("sign_outdated").default(false)
- val claim_time = datetime("claim_time").nullable()
- val index_location = uniqueIndexR("index_location", world_id, px, pz)
-
- private inline fun getId(worldId: Int, parcelX: Int, parcelZ: Int): Int? = getId { world_id.eq(worldId) and px.eq(parcelX) and pz.eq(parcelZ) }
- private inline fun getId(worldName: String, worldUid: UUID?, parcelX: Int, parcelZ: Int): Int? = WorldsT.getId(worldName, worldUid)?.let { getId(it, parcelX, parcelZ) }
- private inline fun getOrInitId(worldName: String, worldUid: UUID?, parcelX: Int, parcelZ: Int): Int {
- val worldId = WorldsT.getOrInitId(worldName, worldUid)
- return getOrInitId(
- { getId(worldId, parcelX, parcelZ) },
- { it[world_id] = worldId; it[px] = parcelX; it[pz] = parcelZ },
- { "parcel at $worldName($parcelX, $parcelZ)" })
- }
-
- override fun getId(parcel: ParcelId): Int? = getId(parcel.worldId.name, parcel.worldId.uid, parcel.x, parcel.z)
- override fun getOrInitId(parcel: ParcelId): Int = getOrInitId(parcel.worldId.name, parcel.worldId.uid, parcel.x, parcel.z)
-
- private inline fun getRow(id: Int): ResultRow? = select { ParcelsT.id eq id }.firstOrNull()
- fun getRow(parcel: ParcelId): ResultRow? = getId(parcel)?.let { getRow(it) }
-
- override fun getItem(row: ResultRow): ParcelId? {
- val worldId = row[world_id]
- val world = WorldsT.getItem(worldId) ?: return null
- return ParcelId(world, row[px], row[pz])
- }
-}
-
-object ProfilesT : IdTransactionsTable<ProfilesT, PlayerProfile>("parcels_profiles", "owner_id") {
- val uuid = binary("uuid", 16).nullable()
- val name = varchar("name", 32).nullable()
-
- // MySQL dialect MUST permit multiple null values for this to work. Server SQL does not allow this. That dialect is shit anyway.
- val uuid_constraint = uniqueIndexR("uuid_constraint", uuid)
- val index_pair = uniqueIndexR("index_pair", uuid, name)
-
-
- private inline fun getId(binaryUuid: ByteArray) = getId { uuid eq binaryUuid }
- private inline fun getId(uuid: UUID) = getId(uuid.toByteArray())
- private inline fun getId(nameIn: String) = getId { uuid.isNull() and (name.lowerCase() eq nameIn.toLowerCase()) }
- private inline fun getRealId(nameIn: String) = getId { uuid.isNotNull() and (name.lowerCase() eq nameIn.toLowerCase()) }
-
- private inline fun getOrInitId(uuid: UUID, name: String?) = uuid.toByteArray().let { binaryUuid ->
- getOrInitId(
- { getId(binaryUuid) },
- { it[this@ProfilesT.uuid] = binaryUuid; it[this@ProfilesT.name] = name },
- { "profile(uuid = $uuid, name = $name)" })
- }
-
- private inline fun getOrInitId(name: String) = getOrInitId(
- { getId(name) },
- { it[ProfilesT.name] = name },
- { "owner(name = $name)" })
-
-
- override fun getId(profile: PlayerProfile): Int? = when (profile) {
- is PlayerProfile.Real -> getId(profile.uuid)
- is PlayerProfile.Fake -> getId(profile.name)
- is PlayerProfile.Unresolved -> getRealId(profile.name)
- else -> throw IllegalArgumentException()
- }
-
- override fun getOrInitId(profile: PlayerProfile): Int = when (profile) {
- is PlayerProfile.Real -> getOrInitId(profile.uuid, profile.nameOrBukkitName)
- is PlayerProfile.Fake -> getOrInitId(profile.name)
- else -> throw IllegalArgumentException() // Unresolved profiles cannot be added to the database
- }
-
- override fun getItem(row: ResultRow): PlayerProfile {
- return PlayerProfile(row[uuid]?.toUUID(), row[name])
- }
-
- fun getRealItem(id: Int): PlayerProfile.Real? {
- return getItem(id) as? PlayerProfile.Real
- }
-
- /*
- fun updatePlayerProfile(profile: PlayerProfile.Real) {
- update({ uuid eq profile.uuid.toByteArray() }) {
- it[name] = profile.nameOrBukkitName
- }
- }*/
-
-}
-
+@file:Suppress("NOTHING_TO_INLINE", "PARAMETER_NAME_CHANGED_ON_OVERRIDE", "unused", "MemberVisibilityCanBePrivate")
+
+package io.dico.parcels2.storage.exposed
+
+import io.dico.parcels2.ParcelId
+import io.dico.parcels2.ParcelWorldId
+import io.dico.parcels2.PlayerProfile
+import io.dico.parcels2.storage.toByteArray
+import io.dico.parcels2.storage.toUUID
+import org.jetbrains.exposed.sql.*
+import org.jetbrains.exposed.sql.statements.UpdateBuilder
+import org.joda.time.DateTime
+import java.util.UUID
+
+abstract class IdTransactionsTable<TableT : IdTransactionsTable<TableT, QueryObj>, QueryObj>(tableName: String, columnName: String)
+ : Table(tableName) {
+ val id = integer(columnName).autoIncrement().primaryKey()
+
+ @Suppress("UNCHECKED_CAST")
+ inline val table: TableT
+ get() = this as TableT
+
+ internal inline fun getId(where: SqlExpressionBuilder.(TableT) -> Op<Boolean>): Int? {
+ return select { where(table) }.firstOrNull()?.let { it[id] }
+ }
+
+ internal inline fun getOrInitId(getId: () -> Int?, noinline body: TableT.(UpdateBuilder<*>) -> Unit, objName: () -> String): Int {
+ return getId() ?: table.insertIgnore(body)[id] ?: getId()
+ ?: throw ExposedDatabaseException("This should not happen - failed to insert ${objName()} and get its number")
+ }
+
+ abstract fun getId(obj: QueryObj): Int?
+ abstract fun getOrInitId(obj: QueryObj): Int
+ fun getItem(id: Int): QueryObj? = select { this@IdTransactionsTable.id eq id }.firstOrNull()?.let { getItem(it) }
+ abstract fun getItem(row: ResultRow): QueryObj?
+
+ fun getId(obj: QueryObj, init: Boolean): Int? = if (init) getOrInitId(obj) else getId(obj)
+}
+
+object WorldsT : IdTransactionsTable<WorldsT, ParcelWorldId>("parcels_worlds", "world_id") {
+ val name = varchar("name", 50)
+ val uid = binary("uid", 16).nullable()
+ val creation_time = datetime("creation_time").nullable()
+ val index_name = uniqueIndexR("index_name", name)
+ val index_uid = uniqueIndexR("index_uid", uid)
+
+ internal inline fun getId(worldName: String, binaryUid: ByteArray?): Int? = getId { (name eq worldName).let { if (binaryUid == null) it else it or (uid eq binaryUid) } }
+ internal inline fun getId(worldName: String, uid: UUID?): Int? = getId(worldName, uid?.toByteArray())
+ internal inline fun getOrInitId(worldName: String, worldUid: UUID?): Int = worldUid?.toByteArray().let { binaryUid ->
+ return getOrInitId(
+ { getId(worldName, binaryUid) },
+ { it[name] = worldName; it[uid] = binaryUid },
+ { "world named $worldName" })
+ }
+
+ override fun getId(world: ParcelWorldId): Int? = getId(world.name, world.uid)
+ override fun getOrInitId(world: ParcelWorldId): Int = getOrInitId(world.name, world.uid)
+
+ override fun getItem(row: ResultRow): ParcelWorldId {
+ return ParcelWorldId(row[name], row[uid]?.toUUID())
+ }
+
+ fun getWorldCreationTime(worldId: ParcelWorldId): DateTime? {
+ val id = getId(worldId) ?: return null
+ return select { WorldsT.id eq id }.firstOrNull()?.let { it[WorldsT.creation_time] }
+ }
+
+ fun setWorldCreationTime(worldId: ParcelWorldId, time: DateTime) {
+ val id = getOrInitId(worldId)
+ update({ WorldsT.id eq id }) {
+ it[WorldsT.creation_time] = time
+ }
+ }
+}
+
+object ParcelsT : IdTransactionsTable<ParcelsT, ParcelId>("parcels", "parcel_id") {
+ val world_id = integer("world_id").references(WorldsT.id)
+ val px = integer("px")
+ val pz = integer("pz")
+ val owner_id = integer("owner_id").references(ProfilesT.id).nullable()
+ val sign_oudated = bool("sign_outdated").default(false)
+ val claim_time = datetime("claim_time").nullable()
+ val index_location = uniqueIndexR("index_location", world_id, px, pz)
+
+ private inline fun getId(worldId: Int, parcelX: Int, parcelZ: Int): Int? = getId { world_id.eq(worldId) and px.eq(parcelX) and pz.eq(parcelZ) }
+ private inline fun getId(worldName: String, worldUid: UUID?, parcelX: Int, parcelZ: Int): Int? = WorldsT.getId(worldName, worldUid)?.let { getId(it, parcelX, parcelZ) }
+ private inline fun getOrInitId(worldName: String, worldUid: UUID?, parcelX: Int, parcelZ: Int): Int {
+ val worldId = WorldsT.getOrInitId(worldName, worldUid)
+ return getOrInitId(
+ { getId(worldId, parcelX, parcelZ) },
+ { it[world_id] = worldId; it[px] = parcelX; it[pz] = parcelZ },
+ { "parcel at $worldName($parcelX, $parcelZ)" })
+ }
+
+ override fun getId(parcel: ParcelId): Int? = getId(parcel.worldId.name, parcel.worldId.uid, parcel.x, parcel.z)
+ override fun getOrInitId(parcel: ParcelId): Int = getOrInitId(parcel.worldId.name, parcel.worldId.uid, parcel.x, parcel.z)
+
+ private inline fun getRow(id: Int): ResultRow? = select { ParcelsT.id eq id }.firstOrNull()
+ fun getRow(parcel: ParcelId): ResultRow? = getId(parcel)?.let { getRow(it) }
+
+ override fun getItem(row: ResultRow): ParcelId? {
+ val worldId = row[world_id]
+ val world = WorldsT.getItem(worldId) ?: return null
+ return ParcelId(world, row[px], row[pz])
+ }
+}
+
+object ProfilesT : IdTransactionsTable<ProfilesT, PlayerProfile>("parcels_profiles", "owner_id") {
+ val uuid = binary("uuid", 16).nullable()
+ val name = varchar("name", 32).nullable()
+
+ // MySQL dialect MUST permit multiple null values for this to work. Server SQL does not allow this. That dialect is shit anyway.
+ val uuid_constraint = uniqueIndexR("uuid_constraint", uuid)
+ val index_pair = uniqueIndexR("index_pair", uuid, name)
+
+
+ private inline fun getId(binaryUuid: ByteArray) = getId { uuid eq binaryUuid }
+ private inline fun getId(uuid: UUID) = getId(uuid.toByteArray())
+ private inline fun getId(nameIn: String) = getId { uuid.isNull() and (name.lowerCase() eq nameIn.toLowerCase()) }
+ private inline fun getRealId(nameIn: String) = getId { uuid.isNotNull() and (name.lowerCase() eq nameIn.toLowerCase()) }
+
+ private inline fun getOrInitId(uuid: UUID, name: String?) = uuid.toByteArray().let { binaryUuid ->
+ getOrInitId(
+ { getId(binaryUuid) },
+ { it[this@ProfilesT.uuid] = binaryUuid; it[this@ProfilesT.name] = name },
+ { "profile(uuid = $uuid, name = $name)" })
+ }
+
+ private inline fun getOrInitId(name: String) = getOrInitId(
+ { getId(name) },
+ { it[ProfilesT.name] = name },
+ { "owner(name = $name)" })
+
+
+ override fun getId(profile: PlayerProfile): Int? = when (profile) {
+ is PlayerProfile.Real -> getId(profile.uuid)
+ is PlayerProfile.Fake -> getId(profile.name)
+ is PlayerProfile.Unresolved -> getRealId(profile.name)
+ else -> throw IllegalArgumentException()
+ }
+
+ override fun getOrInitId(profile: PlayerProfile): Int = when (profile) {
+ is PlayerProfile.Real -> getOrInitId(profile.uuid, profile.nameOrBukkitName)
+ is PlayerProfile.Fake -> getOrInitId(profile.name)
+ else -> throw IllegalArgumentException() // Unresolved profiles cannot be added to the database
+ }
+
+ override fun getItem(row: ResultRow): PlayerProfile {
+ return PlayerProfile(row[uuid]?.toUUID(), row[name])
+ }
+
+ fun getRealItem(id: Int): PlayerProfile.Real? {
+ return getItem(id) as? PlayerProfile.Real
+ }
+
+ /*
+ fun updatePlayerProfile(profile: PlayerProfile.Real) {
+ update({ uuid eq profile.uuid.toByteArray() }) {
+ it[name] = profile.nameOrBukkitName
+ }
+ }*/
+
+}
+
// val ParcelsWithOptionsT = ParcelsT.join(ParcelOptionsT, JoinType.INNER, onColumn = ParcelsT.id, otherColumn = ParcelOptionsT.parcel_id) \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/storage/exposed/ListTables.kt b/src/main/kotlin/io/dico/parcels2/storage/exposed/ListTables.kt
index b9d16fc..26cfc7a 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/exposed/ListTables.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/exposed/ListTables.kt
@@ -1,111 +1,111 @@
-@file:Suppress("PropertyName", "LocalVariableName", "NOTHING_TO_INLINE")
-
-package io.dico.parcels2.storage.exposed
-
-import io.dico.parcels2.*
-import io.dico.parcels2.Privilege.DEFAULT
-import io.dico.parcels2.util.ext.alsoIfTrue
-import kotlinx.coroutines.channels.SendChannel
-import org.jetbrains.exposed.sql.*
-
-object PrivilegesLocalT : PrivilegesTable<ParcelId>("parcels_privilege_local", ParcelsT)
-object PrivilegesGlobalT : PrivilegesTable<PlayerProfile>("parcels_privilege_global", ProfilesT)
-
-object ParcelOptionsT : Table("parcels_options") {
- val parcel_id = integer("parcel_id").primaryKey().references(ParcelsT.id, ReferenceOption.CASCADE)
- val interact_bitmask = binary("interact_bitmask", 4)
-}
-
-typealias PrivilegesSendChannel<AttachT> = SendChannel<Pair<AttachT, PrivilegesHolder>>
-
-sealed class PrivilegesTable<AttachT>(name: String, val idTable: IdTransactionsTable<*, AttachT>) : Table(name) {
- val attach_id = integer("attach_id").references(idTable.id, ReferenceOption.CASCADE)
- val profile_id = integer("profile_id").references(ProfilesT.id, ReferenceOption.CASCADE)
- val privilege = integer("privilege")
- val index_pair = uniqueIndexR("index_pair", attach_id, profile_id)
-
- fun setPrivilege(attachedOn: AttachT, player: PlayerProfile.Real, privilege: Privilege) {
- privilege.requireNonTransient()
-
- if (privilege == DEFAULT) {
- val player_id = ProfilesT.getId(player) ?: return
- idTable.getId(attachedOn)?.let { holder ->
- deleteWhere { (attach_id eq holder) and (profile_id eq player_id) }
- }
- return
- }
-
- val holder = idTable.getOrInitId(attachedOn)
- val player_id = ProfilesT.getOrInitId(player)
- upsert(conflictIndex = index_pair) {
- it[attach_id] = holder
- it[profile_id] = player_id
- it[this.privilege] = privilege.number
- }
- }
-
- fun readPrivileges(id: Int): PrivilegesHolder? {
- val list = slice(profile_id, privilege).select { attach_id eq id }
- val result = PrivilegesHolder()
- for (row in list) {
- val profile = ProfilesT.getRealItem(row[profile_id]) ?: continue
- result.setRawStoredPrivilege(profile, Privilege.getByNumber(row[privilege]) ?: continue)
- }
- return result
- }
-
- fun sendAllPrivilegesH(channel: PrivilegesSendChannel<AttachT>) {
- val iterator = selectAll().orderBy(attach_id).iterator()
-
- if (iterator.hasNext()) {
- var row = iterator.next()
- var id: Int = row[attach_id]
- var attach: AttachT? = null
- var map: PrivilegesHolder? = null
-
- fun initAttachAndMap() {
- attach = idTable.getItem(id)
- map = attach?.let { PrivilegesHolder() }
- }
-
- fun sendIfPresent() {
- if (attach != null && map != null) {
- channel.offer(attach!! to map!!)
- }
- attach = null
- map = null
- }
-
- initAttachAndMap()
-
- do {
- val rowId = row[attach_id]
- if (rowId != id) {
- sendIfPresent()
- id = rowId
- initAttachAndMap()
- }
-
- if (attach == null) {
- continue // owner not found for this owner id
- }
-
- val profile = ProfilesT.getRealItem(row[profile_id])
- if (profile == null) {
- logger.error("Privilege from database is null, id ${row[profile_id]}")
- continue
- }
- val privilege = Privilege.getByNumber(row[privilege])
- if (privilege == null) {
- logger.error("Privilege from database is null, number ${row[this.privilege]}")
- continue
- }
- map!!.setRawStoredPrivilege(profile, privilege)
-
- } while (iterator.hasNext().alsoIfTrue { row = iterator.next() })
-
- sendIfPresent()
- }
- }
-
-}
+@file:Suppress("PropertyName", "LocalVariableName", "NOTHING_TO_INLINE")
+
+package io.dico.parcels2.storage.exposed
+
+import io.dico.parcels2.*
+import io.dico.parcels2.Privilege.DEFAULT
+import io.dico.parcels2.util.ext.alsoIfTrue
+import kotlinx.coroutines.channels.SendChannel
+import org.jetbrains.exposed.sql.*
+
+object PrivilegesLocalT : PrivilegesTable<ParcelId>("parcels_privilege_local", ParcelsT)
+object PrivilegesGlobalT : PrivilegesTable<PlayerProfile>("parcels_privilege_global", ProfilesT)
+
+object ParcelOptionsT : Table("parcels_options") {
+ val parcel_id = integer("parcel_id").primaryKey().references(ParcelsT.id, ReferenceOption.CASCADE)
+ val interact_bitmask = binary("interact_bitmask", 4)
+}
+
+typealias PrivilegesSendChannel<AttachT> = SendChannel<Pair<AttachT, PrivilegesHolder>>
+
+sealed class PrivilegesTable<AttachT>(name: String, val idTable: IdTransactionsTable<*, AttachT>) : Table(name) {
+ val attach_id = integer("attach_id").references(idTable.id, ReferenceOption.CASCADE)
+ val profile_id = integer("profile_id").references(ProfilesT.id, ReferenceOption.CASCADE)
+ val privilege = integer("privilege")
+ val index_pair = uniqueIndexR("index_pair", attach_id, profile_id)
+
+ fun setPrivilege(attachedOn: AttachT, player: PlayerProfile.Real, privilege: Privilege) {
+ privilege.requireNonTransient()
+
+ if (privilege == DEFAULT) {
+ val player_id = ProfilesT.getId(player) ?: return
+ idTable.getId(attachedOn)?.let { holder ->
+ deleteWhere { (attach_id eq holder) and (profile_id eq player_id) }
+ }
+ return
+ }
+
+ val holder = idTable.getOrInitId(attachedOn)
+ val player_id = ProfilesT.getOrInitId(player)
+ upsert(conflictIndex = index_pair) {
+ it[attach_id] = holder
+ it[profile_id] = player_id
+ it[this.privilege] = privilege.number
+ }
+ }
+
+ fun readPrivileges(id: Int): PrivilegesHolder? {
+ val list = slice(profile_id, privilege).select { attach_id eq id }
+ val result = PrivilegesHolder()
+ for (row in list) {
+ val profile = ProfilesT.getRealItem(row[profile_id]) ?: continue
+ result.setRawStoredPrivilege(profile, Privilege.getByNumber(row[privilege]) ?: continue)
+ }
+ return result
+ }
+
+ fun sendAllPrivilegesH(channel: PrivilegesSendChannel<AttachT>) {
+ val iterator = selectAll().orderBy(attach_id).iterator()
+
+ if (iterator.hasNext()) {
+ var row = iterator.next()
+ var id: Int = row[attach_id]
+ var attach: AttachT? = null
+ var map: PrivilegesHolder? = null
+
+ fun initAttachAndMap() {
+ attach = idTable.getItem(id)
+ map = attach?.let { PrivilegesHolder() }
+ }
+
+ fun sendIfPresent() {
+ if (attach != null && map != null) {
+ channel.offer(attach!! to map!!)
+ }
+ attach = null
+ map = null
+ }
+
+ initAttachAndMap()
+
+ do {
+ val rowId = row[attach_id]
+ if (rowId != id) {
+ sendIfPresent()
+ id = rowId
+ initAttachAndMap()
+ }
+
+ if (attach == null) {
+ continue // owner not found for this owner id
+ }
+
+ val profile = ProfilesT.getRealItem(row[profile_id])
+ if (profile == null) {
+ logger.error("Privilege from database is null, id ${row[profile_id]}")
+ continue
+ }
+ val privilege = Privilege.getByNumber(row[privilege])
+ if (privilege == null) {
+ logger.error("Privilege from database is null, number ${row[this.privilege]}")
+ continue
+ }
+ map!!.setRawStoredPrivilege(profile, privilege)
+
+ } while (iterator.hasNext().alsoIfTrue { row = iterator.next() })
+
+ sendIfPresent()
+ }
+ }
+
+}
diff --git a/src/main/kotlin/io/dico/parcels2/storage/migration/Migration.kt b/src/main/kotlin/io/dico/parcels2/storage/migration/Migration.kt
index acc7c5e..a512f2a 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/migration/Migration.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/migration/Migration.kt
@@ -1,9 +1,9 @@
-package io.dico.parcels2.storage.migration
-
-import io.dico.parcels2.storage.Storage
-import kotlinx.coroutines.Job
-
-interface Migration {
- fun migrateTo(storage: Storage): Job
-}
-
+package io.dico.parcels2.storage.migration
+
+import io.dico.parcels2.storage.Storage
+import kotlinx.coroutines.Job
+
+interface Migration {
+ fun migrateTo(storage: Storage): Job
+}
+
diff --git a/src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeMigration.kt b/src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeMigration.kt
index 831fe42..954da5d 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeMigration.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeMigration.kt
@@ -1,118 +1,118 @@
-@file:Suppress("RedundantSuspendModifier", "DEPRECATION")
-
-package io.dico.parcels2.storage.migration.plotme
-
-import com.zaxxer.hikari.HikariDataSource
-import io.dico.parcels2.*
-import io.dico.parcels2.options.PlotmeMigrationOptions
-import io.dico.parcels2.storage.Storage
-import io.dico.parcels2.storage.exposed.abs
-import io.dico.parcels2.storage.exposed.greaterOf
-import io.dico.parcels2.storage.migration.Migration
-import io.dico.parcels2.storage.migration.plotme.PlotmeTables.PlotmePlotPlayerMap
-import io.dico.parcels2.storage.migration.plotme.PlotmeTables.PlotmeTable
-import io.dico.parcels2.storage.toUUID
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.newFixedThreadPoolContext
-import org.jetbrains.exposed.sql.*
-import org.slf4j.LoggerFactory
-import java.sql.Blob
-import java.util.UUID
-import javax.sql.DataSource
-
-class PlotmeMigration(val options: PlotmeMigrationOptions) : Migration {
- private var dataSource: DataSource? = null
- private var database: Database? = null
- private var isShutdown: Boolean = false
- private val mlogger = LoggerFactory.getLogger("PlotMe Migrator")
- private val tables = PlotmeTables(options.tableNamesUppercase)
- val dispatcher = newFixedThreadPoolContext(1, "PlotMe Migration Thread")
-
- private fun <T> transaction(statement: Transaction.() -> T) = org.jetbrains.exposed.sql.transactions.transaction(database!!, statement)
-
- override fun migrateTo(storage: Storage): Job {
- return launch(dispatcher) {
- init()
- doWork(storage)
- shutdown()
- }
- }
-
- fun init() {
- if (isShutdown || database != null) throw IllegalStateException()
- dataSource = options.storage.getDataSourceFactory()!!()
- database = Database.connect(dataSource!!)
- }
-
- fun shutdown() {
- if (isShutdown) throw IllegalStateException()
- dataSource?.let {
- (it as? HikariDataSource)?.close()
- }
- database = null
- isShutdown = true
- }
-
- suspend fun doWork(target: Storage) = with (tables) {
- val exit = transaction {
- (!PlotmePlots.exists()).also {
- if (it) mlogger.warn("Plotme tables don't appear to exist. Exiting.")
- }
- }
- if (exit) return
-
- val worldCache = options.worldsFromTo.mapValues { ParcelWorldId(it.value) }
-
- fun getParcelId(table: PlotmeTable, row: ResultRow): ParcelId? {
- val world = worldCache[row[table.world_name]] ?: return null
- return ParcelId(world, row[table.px], row[table.pz])
- }
-
- fun PlotmePlotPlayerMap.transmitPlotmeAddedTable(kind: Privilege) {
- selectAll().forEach { row ->
- val parcel = getParcelId(this, row) ?: return@forEach
- val profile = PrivilegeKey.safe(row[player_uuid]?.toUUID(), row[player_name]) ?: return@forEach
- target.setLocalPrivilege(parcel, profile, kind)
- }
- }
-
- mlogger.info("Transmitting data from plotmeplots table")
- var count = 0
- transaction {
-
- PlotmePlots.selectAll()
- .orderBy(PlotmePlots.world_name)
- .orderBy(greaterOf(PlotmePlots.px.abs(), PlotmePlots.pz.abs()))
- .forEach { row ->
- val parcel = getParcelId(PlotmePlots, row) ?: return@forEach
- val owner = PlayerProfile.safe(row[PlotmePlots.owner_uuid]?.toUUID(), row[PlotmePlots.owner_name])
- target.setParcelOwner(parcel, owner)
- target.setParcelOwnerSignOutdated(parcel, true)
- ++count
- }
- }
-
- mlogger.info("Transmitting data from plotmeallowed table")
- transaction {
- PlotmeAllowed.transmitPlotmeAddedTable(Privilege.CAN_BUILD)
- }
-
- mlogger.info("Transmitting data from plotmedenied table")
- transaction {
- PlotmeDenied.transmitPlotmeAddedTable(Privilege.BANNED)
- }
-
- mlogger.warn("Data has been **transmitted**. $count plots were migrated to the parcels database.")
- mlogger.warn("Loading parcel data might take a while as enqueued transactions from this migration are completed.")
- }
-
- private fun Blob.toUUID(): UUID? {
- val ba = ByteArray(16)
- val count = binaryStream.read(ba, 0, 16)
- if (count < 16) return null
- return ba.toUUID()
- }
-
-
+@file:Suppress("RedundantSuspendModifier", "DEPRECATION")
+
+package io.dico.parcels2.storage.migration.plotme
+
+import com.zaxxer.hikari.HikariDataSource
+import io.dico.parcels2.*
+import io.dico.parcels2.options.PlotmeMigrationOptions
+import io.dico.parcels2.storage.Storage
+import io.dico.parcels2.storage.exposed.abs
+import io.dico.parcels2.storage.exposed.greaterOf
+import io.dico.parcels2.storage.migration.Migration
+import io.dico.parcels2.storage.migration.plotme.PlotmeTables.PlotmePlotPlayerMap
+import io.dico.parcels2.storage.migration.plotme.PlotmeTables.PlotmeTable
+import io.dico.parcels2.storage.toUUID
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.newFixedThreadPoolContext
+import org.jetbrains.exposed.sql.*
+import org.slf4j.LoggerFactory
+import java.sql.Blob
+import java.util.UUID
+import javax.sql.DataSource
+
+class PlotmeMigration(val options: PlotmeMigrationOptions) : Migration {
+ private var dataSource: DataSource? = null
+ private var database: Database? = null
+ private var isShutdown: Boolean = false
+ private val mlogger = LoggerFactory.getLogger("PlotMe Migrator")
+ private val tables = PlotmeTables(options.tableNamesUppercase)
+ val dispatcher = newFixedThreadPoolContext(1, "PlotMe Migration Thread")
+
+ private fun <T> transaction(statement: Transaction.() -> T) = org.jetbrains.exposed.sql.transactions.transaction(database!!, statement)
+
+ override fun migrateTo(storage: Storage): Job {
+ return launch(dispatcher) {
+ init()
+ doWork(storage)
+ shutdown()
+ }
+ }
+
+ fun init() {
+ if (isShutdown || database != null) throw IllegalStateException()
+ dataSource = options.storage.getDataSourceFactory()!!()
+ database = Database.connect(dataSource!!)
+ }
+
+ fun shutdown() {
+ if (isShutdown) throw IllegalStateException()
+ dataSource?.let {
+ (it as? HikariDataSource)?.close()
+ }
+ database = null
+ isShutdown = true
+ }
+
+ suspend fun doWork(target: Storage) = with (tables) {
+ val exit = transaction {
+ (!PlotmePlots.exists()).also {
+ if (it) mlogger.warn("Plotme tables don't appear to exist. Exiting.")
+ }
+ }
+ if (exit) return
+
+ val worldCache = options.worldsFromTo.mapValues { ParcelWorldId(it.value) }
+
+ fun getParcelId(table: PlotmeTable, row: ResultRow): ParcelId? {
+ val world = worldCache[row[table.world_name]] ?: return null
+ return ParcelId(world, row[table.px], row[table.pz])
+ }
+
+ fun PlotmePlotPlayerMap.transmitPlotmeAddedTable(kind: Privilege) {
+ selectAll().forEach { row ->
+ val parcel = getParcelId(this, row) ?: return@forEach
+ val profile = PrivilegeKey.safe(row[player_uuid]?.toUUID(), row[player_name]) ?: return@forEach
+ target.setLocalPrivilege(parcel, profile, kind)
+ }
+ }
+
+ mlogger.info("Transmitting data from plotmeplots table")
+ var count = 0
+ transaction {
+
+ PlotmePlots.selectAll()
+ .orderBy(PlotmePlots.world_name)
+ .orderBy(greaterOf(PlotmePlots.px.abs(), PlotmePlots.pz.abs()))
+ .forEach { row ->
+ val parcel = getParcelId(PlotmePlots, row) ?: return@forEach
+ val owner = PlayerProfile.safe(row[PlotmePlots.owner_uuid]?.toUUID(), row[PlotmePlots.owner_name])
+ target.setParcelOwner(parcel, owner)
+ target.setParcelOwnerSignOutdated(parcel, true)
+ ++count
+ }
+ }
+
+ mlogger.info("Transmitting data from plotmeallowed table")
+ transaction {
+ PlotmeAllowed.transmitPlotmeAddedTable(Privilege.CAN_BUILD)
+ }
+
+ mlogger.info("Transmitting data from plotmedenied table")
+ transaction {
+ PlotmeDenied.transmitPlotmeAddedTable(Privilege.BANNED)
+ }
+
+ mlogger.warn("Data has been **transmitted**. $count plots were migrated to the parcels database.")
+ mlogger.warn("Loading parcel data might take a while as enqueued transactions from this migration are completed.")
+ }
+
+ private fun Blob.toUUID(): UUID? {
+ val ba = ByteArray(16)
+ val count = binaryStream.read(ba, 0, 16)
+ if (count < 16) return null
+ return ba.toUUID()
+ }
+
+
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeTables.kt b/src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeTables.kt
index dc788c8..b276e11 100644
--- a/src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeTables.kt
+++ b/src/main/kotlin/io/dico/parcels2/storage/migration/plotme/PlotmeTables.kt
@@ -1,31 +1,31 @@
-package io.dico.parcels2.storage.migration.plotme
-
-import org.jetbrains.exposed.sql.Table
-
-class PlotmeTables(val uppercase: Boolean) {
- fun String.toCorrectCase() = if (uppercase) this else toLowerCase()
-
- val PlotmePlots = PlotmePlotsT()
- val PlotmeAllowed = PlotmeAllowedT()
- val PlotmeDenied = PlotmeDeniedT()
-
- inner abstract class PlotmeTable(name: String) : Table(name) {
- val px = integer("idX").primaryKey()
- val pz = integer("idZ").primaryKey()
- val world_name = varchar("world", 32).primaryKey()
- }
-
- inner abstract class PlotmePlotPlayerMap(name: String) : PlotmeTable(name) {
- val player_name = varchar("player", 32)
- val player_uuid = blob("playerid").nullable()
- }
-
- inner class PlotmePlotsT : PlotmeTable("plotmePlots".toCorrectCase()) {
- val owner_name = varchar("owner", 32)
- val owner_uuid = blob("ownerid").nullable()
- }
-
- inner class PlotmeAllowedT : PlotmePlotPlayerMap("plotmeAllowed".toCorrectCase())
- inner class PlotmeDeniedT : PlotmePlotPlayerMap("plotmeDenied".toCorrectCase())
-}
-
+package io.dico.parcels2.storage.migration.plotme
+
+import org.jetbrains.exposed.sql.Table
+
+class PlotmeTables(val uppercase: Boolean) {
+ fun String.toCorrectCase() = if (uppercase) this else toLowerCase()
+
+ val PlotmePlots = PlotmePlotsT()
+ val PlotmeAllowed = PlotmeAllowedT()
+ val PlotmeDenied = PlotmeDeniedT()
+
+ inner abstract class PlotmeTable(name: String) : Table(name) {
+ val px = integer("idX").primaryKey()
+ val pz = integer("idZ").primaryKey()
+ val world_name = varchar("world", 32).primaryKey()
+ }
+
+ inner abstract class PlotmePlotPlayerMap(name: String) : PlotmeTable(name) {
+ val player_name = varchar("player", 32)
+ val player_uuid = blob("playerid").nullable()
+ }
+
+ inner class PlotmePlotsT : PlotmeTable("plotmePlots".toCorrectCase()) {
+ val owner_name = varchar("owner", 32)
+ val owner_uuid = blob("ownerid").nullable()
+ }
+
+ inner class PlotmeAllowedT : PlotmePlotPlayerMap("plotmeAllowed".toCorrectCase())
+ inner class PlotmeDeniedT : PlotmePlotPlayerMap("plotmeDenied".toCorrectCase())
+}
+
diff --git a/src/main/kotlin/io/dico/parcels2/util/BukkitUtil.kt b/src/main/kotlin/io/dico/parcels2/util/BukkitUtil.kt
index 618eaed..a4a6da9 100644
--- a/src/main/kotlin/io/dico/parcels2/util/BukkitUtil.kt
+++ b/src/main/kotlin/io/dico/parcels2/util/BukkitUtil.kt
@@ -1,14 +1,14 @@
-package io.dico.parcels2.util
-
-import io.dico.parcels2.util.ext.isValid
-import org.bukkit.Bukkit
-import org.bukkit.OfflinePlayer
-import java.util.UUID
-
-fun getPlayerName(uuid: UUID): String? = getOfflinePlayer(uuid)?.name
-
-fun getOfflinePlayer(uuid: UUID): OfflinePlayer? = Bukkit.getOfflinePlayer(uuid).takeIf { it.isValid }
-
-fun getOfflinePlayer(name: String): OfflinePlayer? = Bukkit.getOfflinePlayer(name).takeIf { it.isValid }
-
-fun isServerThread(): Boolean = Thread.currentThread().name == "Server thread"
+package io.dico.parcels2.util
+
+import io.dico.parcels2.util.ext.isValid
+import org.bukkit.Bukkit
+import org.bukkit.OfflinePlayer
+import java.util.UUID
+
+fun getPlayerName(uuid: UUID): String? = getOfflinePlayer(uuid)?.name
+
+fun getOfflinePlayer(uuid: UUID): OfflinePlayer? = Bukkit.getOfflinePlayer(uuid).takeIf { it.isValid }
+
+fun getOfflinePlayer(name: String): OfflinePlayer? = Bukkit.getOfflinePlayer(name).takeIf { it.isValid }
+
+fun isServerThread(): Boolean = Thread.currentThread().name == "Server thread"
diff --git a/src/main/kotlin/io/dico/parcels2/util/MainThreadDispatcher.kt b/src/main/kotlin/io/dico/parcels2/util/MainThreadDispatcher.kt
index 3eb2e81..3904026 100644
--- a/src/main/kotlin/io/dico/parcels2/util/MainThreadDispatcher.kt
+++ b/src/main/kotlin/io/dico/parcels2/util/MainThreadDispatcher.kt
@@ -1,43 +1,43 @@
-package io.dico.parcels2.util
-
-import kotlinx.coroutines.CancellableContinuation
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.Delay
-import kotlinx.coroutines.Runnable
-import kotlinx.coroutines.timeunit.TimeUnit
-import org.bukkit.plugin.Plugin
-import kotlin.coroutines.CoroutineContext
-
-abstract class MainThreadDispatcher : CoroutineDispatcher(), Delay {
- abstract val mainThread: Thread
- abstract fun runOnMainThread(task: Runnable)
-}
-
-@Suppress("FunctionName")
-fun MainThreadDispatcher(plugin: Plugin): MainThreadDispatcher {
- return object : MainThreadDispatcher() {
- override val mainThread: Thread = Thread.currentThread()
-
- override fun dispatch(context: CoroutineContext, block: Runnable) {
- doDispatch(block)
- }
-
- override fun runOnMainThread(task: Runnable) {
- doDispatch(task)
- }
-
- private fun doDispatch(task: Runnable) {
- if (Thread.currentThread() === mainThread) task.run()
- else plugin.server.scheduler.runTaskLater(plugin, task, 0)
- }
-
- override fun scheduleResumeAfterDelay(time: Long, unit: TimeUnit, continuation: CancellableContinuation<Unit>) {
- val task = Runnable {
- with (continuation) { resumeUndispatched(Unit) }
- }
-
- val millis = unit.toMillis(time)
- plugin.server.scheduler.runTaskLater(plugin, task, (millis + 25) / 50 - 1)
- }
- }
+package io.dico.parcels2.util
+
+import kotlinx.coroutines.CancellableContinuation
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.Delay
+import kotlinx.coroutines.Runnable
+import kotlinx.coroutines.timeunit.TimeUnit
+import org.bukkit.plugin.Plugin
+import kotlin.coroutines.CoroutineContext
+
+abstract class MainThreadDispatcher : CoroutineDispatcher(), Delay {
+ abstract val mainThread: Thread
+ abstract fun runOnMainThread(task: Runnable)
+}
+
+@Suppress("FunctionName")
+fun MainThreadDispatcher(plugin: Plugin): MainThreadDispatcher {
+ return object : MainThreadDispatcher() {
+ override val mainThread: Thread = Thread.currentThread()
+
+ override fun dispatch(context: CoroutineContext, block: Runnable) {
+ doDispatch(block)
+ }
+
+ override fun runOnMainThread(task: Runnable) {
+ doDispatch(task)
+ }
+
+ private fun doDispatch(task: Runnable) {
+ if (Thread.currentThread() === mainThread) task.run()
+ else plugin.server.scheduler.runTaskLater(plugin, task, 0)
+ }
+
+ override fun scheduleResumeAfterDelay(time: Long, unit: TimeUnit, continuation: CancellableContinuation<Unit>) {
+ val task = Runnable {
+ with (continuation) { resumeUndispatched(Unit) }
+ }
+
+ val millis = unit.toMillis(time)
+ plugin.server.scheduler.runTaskLater(plugin, task, (millis + 25) / 50 - 1)
+ }
+ }
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/util/PluginScheduler.kt b/src/main/kotlin/io/dico/parcels2/util/PluginScheduler.kt
index f29ba2b..268a083 100644
--- a/src/main/kotlin/io/dico/parcels2/util/PluginScheduler.kt
+++ b/src/main/kotlin/io/dico/parcels2/util/PluginScheduler.kt
@@ -1,20 +1,20 @@
-package io.dico.parcels2.util
-
-import org.bukkit.plugin.Plugin
-import org.bukkit.scheduler.BukkitTask
-
-interface PluginScheduler {
- val plugin: Plugin
-
- fun schedule(delay: Int, task: () -> Unit): BukkitTask {
- return plugin.server.scheduler.runTaskLater(plugin, task, delay.toLong())
- }
-
- fun scheduleRepeating(delay: Int, interval: Int, task: () -> Unit): BukkitTask {
- return plugin.server.scheduler.runTaskTimer(plugin, task, delay.toLong(), interval.toLong())
- }
-}
-
-@Suppress("NOTHING_TO_INLINE")
-inline fun PluginScheduler.schedule(noinline task: () -> Unit) = schedule(0, task)
-
+package io.dico.parcels2.util
+
+import org.bukkit.plugin.Plugin
+import org.bukkit.scheduler.BukkitTask
+
+interface PluginScheduler {
+ val plugin: Plugin
+
+ fun schedule(delay: Int, task: () -> Unit): BukkitTask {
+ return plugin.server.scheduler.runTaskLater(plugin, task, delay.toLong())
+ }
+
+ fun scheduleRepeating(delay: Int, interval: Int, task: () -> Unit): BukkitTask {
+ return plugin.server.scheduler.runTaskTimer(plugin, task, delay.toLong(), interval.toLong())
+ }
+}
+
+@Suppress("NOTHING_TO_INLINE")
+inline fun PluginScheduler.schedule(noinline task: () -> Unit) = schedule(0, task)
+
diff --git a/src/main/kotlin/io/dico/parcels2/util/ext/Material.kt b/src/main/kotlin/io/dico/parcels2/util/ext/Material.kt
index e160e55..1351b5d 100644
--- a/src/main/kotlin/io/dico/parcels2/util/ext/Material.kt
+++ b/src/main/kotlin/io/dico/parcels2/util/ext/Material.kt
@@ -1,108 +1,108 @@
-package io.dico.parcels2.util.ext
-
-import org.bukkit.Material
-import org.bukkit.Material.*
-
-/*
-colors:
-WHITE_$, ORANGE_$, MAGENTA_$, LIGHT_BLUE_$, YELLOW_$, LIME_$, PINK_$, GRAY_$, LIGHT_GRAY_$, CYAN_$, PURPLE_$, BLUE_$, BROWN_$, GREEN_$, RED_$, BLACK_$,
-wood:
-OAK_$, BIRCH_$, SPRUCE_$, JUNGLE_$, ACACIA_$, DARK_OAK_$,
- */
-
-val Material.isBed
- get() = when (this) {
- WHITE_BED,
- ORANGE_BED,
- MAGENTA_BED,
- LIGHT_BLUE_BED,
- YELLOW_BED,
- LIME_BED,
- PINK_BED,
- GRAY_BED,
- LIGHT_GRAY_BED,
- CYAN_BED,
- PURPLE_BED,
- BLUE_BED,
- BROWN_BED,
- GREEN_BED,
- RED_BED,
- BLACK_BED -> true
- else -> false
- }
-
-val Material.isWoodDoor
- get() = when (this) {
- OAK_DOOR,
- BIRCH_DOOR,
- SPRUCE_DOOR,
- JUNGLE_DOOR,
- ACACIA_DOOR,
- DARK_OAK_DOOR -> true
- else -> false
- }
-
-val Material.isWoodTrapdoor
- get() = when (this) {
- OAK_TRAPDOOR,
- BIRCH_TRAPDOOR,
- SPRUCE_TRAPDOOR,
- JUNGLE_TRAPDOOR,
- ACACIA_TRAPDOOR,
- DARK_OAK_TRAPDOOR -> true
- else -> false
- }
-
-val Material.isWoodFenceGate
- get() = when (this) {
- OAK_FENCE_GATE,
- BIRCH_FENCE_GATE,
- SPRUCE_FENCE_GATE,
- JUNGLE_FENCE_GATE,
- ACACIA_FENCE_GATE,
- DARK_OAK_FENCE_GATE -> true
- else -> false
- }
-
-val Material.isWoodButton
- get() = when (this) {
- OAK_BUTTON,
- BIRCH_BUTTON,
- SPRUCE_BUTTON,
- JUNGLE_BUTTON,
- ACACIA_BUTTON,
- DARK_OAK_BUTTON -> true
- else -> false
- }
-
-private fun getMaterialPrefixed(prefix: String, name: String): Material {
- return Material.getMaterial("${prefix}_$name") ?: throw IllegalArgumentException("Material ${prefix}_$name doesn't exist")
-}
-
-fun getMaterialsWithWoodTypePrefix(name: String) = arrayOf(
- getMaterialPrefixed("OAK", name),
- getMaterialPrefixed("BIRCH", name),
- getMaterialPrefixed("SPRUCE", name),
- getMaterialPrefixed("JUNGLE", name),
- getMaterialPrefixed("ACACIA", name),
- getMaterialPrefixed("DARK_OAK", name)
-)
-
-fun getMaterialsWithWoolColorPrefix(name: String) = arrayOf(
- getMaterialPrefixed("WHITE", name),
- getMaterialPrefixed("ORANGE", name),
- getMaterialPrefixed("MAGENTA", name),
- getMaterialPrefixed("LIGHT_BLUE", name),
- getMaterialPrefixed("YELLOW", name),
- getMaterialPrefixed("LIME", name),
- getMaterialPrefixed("PINK", name),
- getMaterialPrefixed("GRAY", name),
- getMaterialPrefixed("LIGHT_GRAY", name),
- getMaterialPrefixed("CYAN", name),
- getMaterialPrefixed("PURPLE", name),
- getMaterialPrefixed("BLUE", name),
- getMaterialPrefixed("BROWN", name),
- getMaterialPrefixed("GREEN", name),
- getMaterialPrefixed("RED", name),
- getMaterialPrefixed("BLACK", name)
+package io.dico.parcels2.util.ext
+
+import org.bukkit.Material
+import org.bukkit.Material.*
+
+/*
+colors:
+WHITE_$, ORANGE_$, MAGENTA_$, LIGHT_BLUE_$, YELLOW_$, LIME_$, PINK_$, GRAY_$, LIGHT_GRAY_$, CYAN_$, PURPLE_$, BLUE_$, BROWN_$, GREEN_$, RED_$, BLACK_$,
+wood:
+OAK_$, BIRCH_$, SPRUCE_$, JUNGLE_$, ACACIA_$, DARK_OAK_$,
+ */
+
+val Material.isBed
+ get() = when (this) {
+ WHITE_BED,
+ ORANGE_BED,
+ MAGENTA_BED,
+ LIGHT_BLUE_BED,
+ YELLOW_BED,
+ LIME_BED,
+ PINK_BED,
+ GRAY_BED,
+ LIGHT_GRAY_BED,
+ CYAN_BED,
+ PURPLE_BED,
+ BLUE_BED,
+ BROWN_BED,
+ GREEN_BED,
+ RED_BED,
+ BLACK_BED -> true
+ else -> false
+ }
+
+val Material.isWoodDoor
+ get() = when (this) {
+ OAK_DOOR,
+ BIRCH_DOOR,
+ SPRUCE_DOOR,
+ JUNGLE_DOOR,
+ ACACIA_DOOR,
+ DARK_OAK_DOOR -> true
+ else -> false
+ }
+
+val Material.isWoodTrapdoor
+ get() = when (this) {
+ OAK_TRAPDOOR,
+ BIRCH_TRAPDOOR,
+ SPRUCE_TRAPDOOR,
+ JUNGLE_TRAPDOOR,
+ ACACIA_TRAPDOOR,
+ DARK_OAK_TRAPDOOR -> true
+ else -> false
+ }
+
+val Material.isWoodFenceGate
+ get() = when (this) {
+ OAK_FENCE_GATE,
+ BIRCH_FENCE_GATE,
+ SPRUCE_FENCE_GATE,
+ JUNGLE_FENCE_GATE,
+ ACACIA_FENCE_GATE,
+ DARK_OAK_FENCE_GATE -> true
+ else -> false
+ }
+
+val Material.isWoodButton
+ get() = when (this) {
+ OAK_BUTTON,
+ BIRCH_BUTTON,
+ SPRUCE_BUTTON,
+ JUNGLE_BUTTON,
+ ACACIA_BUTTON,
+ DARK_OAK_BUTTON -> true
+ else -> false
+ }
+
+private fun getMaterialPrefixed(prefix: String, name: String): Material {
+ return Material.getMaterial("${prefix}_$name") ?: throw IllegalArgumentException("Material ${prefix}_$name doesn't exist")
+}
+
+fun getMaterialsWithWoodTypePrefix(name: String) = arrayOf(
+ getMaterialPrefixed("OAK", name),
+ getMaterialPrefixed("BIRCH", name),
+ getMaterialPrefixed("SPRUCE", name),
+ getMaterialPrefixed("JUNGLE", name),
+ getMaterialPrefixed("ACACIA", name),
+ getMaterialPrefixed("DARK_OAK", name)
+)
+
+fun getMaterialsWithWoolColorPrefix(name: String) = arrayOf(
+ getMaterialPrefixed("WHITE", name),
+ getMaterialPrefixed("ORANGE", name),
+ getMaterialPrefixed("MAGENTA", name),
+ getMaterialPrefixed("LIGHT_BLUE", name),
+ getMaterialPrefixed("YELLOW", name),
+ getMaterialPrefixed("LIME", name),
+ getMaterialPrefixed("PINK", name),
+ getMaterialPrefixed("GRAY", name),
+ getMaterialPrefixed("LIGHT_GRAY", name),
+ getMaterialPrefixed("CYAN", name),
+ getMaterialPrefixed("PURPLE", name),
+ getMaterialPrefixed("BLUE", name),
+ getMaterialPrefixed("BROWN", name),
+ getMaterialPrefixed("GREEN", name),
+ getMaterialPrefixed("RED", name),
+ getMaterialPrefixed("BLACK", name)
) \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/util/ext/Misc.kt b/src/main/kotlin/io/dico/parcels2/util/ext/Misc.kt
index 75aba35..e5e8aa9 100644
--- a/src/main/kotlin/io/dico/parcels2/util/ext/Misc.kt
+++ b/src/main/kotlin/io/dico/parcels2/util/ext/Misc.kt
@@ -1,81 +1,81 @@
-package io.dico.parcels2.util.ext
-
-import io.dico.dicore.Formatting
-import io.dico.parcels2.logger
-import java.io.File
-
-fun File.tryCreate(): Boolean {
- if (exists()) {
- return !isDirectory
- }
- val parent = parentFile
- if (parent == null || !(parent.exists() || parent.mkdirs()) || !createNewFile()) {
- logger.warn("Failed to create file $canonicalPath")
- return false
- }
- return true
-}
-
-inline fun Boolean.alsoIfTrue(block: () -> Unit): Boolean = also { if (it) block() }
-inline fun Boolean.alsoIfFalse(block: () -> Unit): Boolean = also { if (!it) block() }
-
-inline fun <R> Any.synchronized(block: () -> R): R = synchronized(this, block)
-
-//inline fun <T> T?.isNullOr(condition: T.() -> Boolean): Boolean = this == null || condition()
-//inline fun <T> T?.isPresentAnd(condition: T.() -> Boolean): Boolean = this != null && condition()
-inline fun <T> T?.ifNullRun(block: () -> Unit): T? {
- if (this == null) block()
- return this
-}
-
-inline fun <T, U> MutableMap<T, U>.editLoop(block: EditLoopScope<T, U>.(T, U) -> Unit) {
- return EditLoopScope(this).doEditLoop(block)
-}
-
-inline fun <T, U> MutableMap<T, U>.editLoop(block: EditLoopScope<T, U>.() -> Unit) {
- return EditLoopScope(this).doEditLoop(block)
-}
-
-class EditLoopScope<T, U>(val _map: MutableMap<T, U>) {
- private var iterator: MutableIterator<MutableMap.MutableEntry<T, U>>? = null
- lateinit var _entry: MutableMap.MutableEntry<T, U>
-
- inline val key get() = _entry.key
- inline var value
- get() = _entry.value
- set(target) = run { _entry.setValue(target) }
-
- inline fun doEditLoop(block: EditLoopScope<T, U>.() -> Unit) {
- val it = _initIterator()
- while (it.hasNext()) {
- _entry = it.next()
- block()
- }
- }
-
- inline fun doEditLoop(block: EditLoopScope<T, U>.(T, U) -> Unit) {
- val it = _initIterator()
- while (it.hasNext()) {
- val entry = it.next().also { _entry = it }
- block(entry.key, entry.value)
- }
- }
-
- fun remove() {
- iterator!!.remove()
- }
-
- fun _initIterator(): MutableIterator<MutableMap.MutableEntry<T, U>> {
- iterator?.let { throw IllegalStateException() }
- return _map.entries.iterator().also { iterator = it }
- }
-
-}
-
-operator fun Formatting.plus(other: Formatting) = toString() + other
-operator fun Formatting.plus(other: String) = toString() + other
-
-inline fun <T> Pair<T, T>.forEach(block: (T) -> Unit) {
- block(first)
- block(second)
-}
+package io.dico.parcels2.util.ext
+
+import io.dico.dicore.Formatting
+import io.dico.parcels2.logger
+import java.io.File
+
+fun File.tryCreate(): Boolean {
+ if (exists()) {
+ return !isDirectory
+ }
+ val parent = parentFile
+ if (parent == null || !(parent.exists() || parent.mkdirs()) || !createNewFile()) {
+ logger.warn("Failed to create file $canonicalPath")
+ return false
+ }
+ return true
+}
+
+inline fun Boolean.alsoIfTrue(block: () -> Unit): Boolean = also { if (it) block() }
+inline fun Boolean.alsoIfFalse(block: () -> Unit): Boolean = also { if (!it) block() }
+
+inline fun <R> Any.synchronized(block: () -> R): R = synchronized(this, block)
+
+//inline fun <T> T?.isNullOr(condition: T.() -> Boolean): Boolean = this == null || condition()
+//inline fun <T> T?.isPresentAnd(condition: T.() -> Boolean): Boolean = this != null && condition()
+inline fun <T> T?.ifNullRun(block: () -> Unit): T? {
+ if (this == null) block()
+ return this
+}
+
+inline fun <T, U> MutableMap<T, U>.editLoop(block: EditLoopScope<T, U>.(T, U) -> Unit) {
+ return EditLoopScope(this).doEditLoop(block)
+}
+
+inline fun <T, U> MutableMap<T, U>.editLoop(block: EditLoopScope<T, U>.() -> Unit) {
+ return EditLoopScope(this).doEditLoop(block)
+}
+
+class EditLoopScope<T, U>(val _map: MutableMap<T, U>) {
+ private var iterator: MutableIterator<MutableMap.MutableEntry<T, U>>? = null
+ lateinit var _entry: MutableMap.MutableEntry<T, U>
+
+ inline val key get() = _entry.key
+ inline var value
+ get() = _entry.value
+ set(target) = run { _entry.setValue(target) }
+
+ inline fun doEditLoop(block: EditLoopScope<T, U>.() -> Unit) {
+ val it = _initIterator()
+ while (it.hasNext()) {
+ _entry = it.next()
+ block()
+ }
+ }
+
+ inline fun doEditLoop(block: EditLoopScope<T, U>.(T, U) -> Unit) {
+ val it = _initIterator()
+ while (it.hasNext()) {
+ val entry = it.next().also { _entry = it }
+ block(entry.key, entry.value)
+ }
+ }
+
+ fun remove() {
+ iterator!!.remove()
+ }
+
+ fun _initIterator(): MutableIterator<MutableMap.MutableEntry<T, U>> {
+ iterator?.let { throw IllegalStateException() }
+ return _map.entries.iterator().also { iterator = it }
+ }
+
+}
+
+operator fun Formatting.plus(other: Formatting) = toString() + other
+operator fun Formatting.plus(other: String) = toString() + other
+
+inline fun <T> Pair<T, T>.forEach(block: (T) -> Unit) {
+ block(first)
+ block(second)
+}
diff --git a/src/main/kotlin/io/dico/parcels2/util/ext/Player.kt b/src/main/kotlin/io/dico/parcels2/util/ext/Player.kt
index a7f21c5..4c502a0 100644
--- a/src/main/kotlin/io/dico/parcels2/util/ext/Player.kt
+++ b/src/main/kotlin/io/dico/parcels2/util/ext/Player.kt
@@ -1,57 +1,57 @@
-package io.dico.parcels2.util.ext
-
-import io.dico.dicore.Formatting
-import io.dico.parcels2.ParcelsPlugin
-import io.dico.parcels2.logger
-import org.bukkit.OfflinePlayer
-import org.bukkit.entity.Player
-import org.bukkit.permissions.Permissible
-import org.bukkit.plugin.java.JavaPlugin
-
-inline val OfflinePlayer.uuid get() = uniqueId
-
-@Suppress("UsePropertyAccessSyntax")
-inline val OfflinePlayer.isValid
- get() = isOnline() || hasPlayedBefore()
-
-const val PERM_BAN_BYPASS = "parcels.admin.bypass.ban"
-const val PERM_BUILD_ANYWHERE = "parcels.admin.bypass.build"
-const val PERM_ADMIN_MANAGE = "parcels.admin.manage"
-
-inline val Permissible.hasPermBanBypass get() = hasPermission(PERM_BAN_BYPASS)
-inline val Permissible.hasPermGamemodeBypass get() = hasPermission("parcels.admin.bypass.gamemode")
-inline val Permissible.hasPermBuildAnywhere get() = hasPermission(PERM_BUILD_ANYWHERE)
-inline val Permissible.hasPermAdminManage get() = hasPermission(PERM_ADMIN_MANAGE)
-inline val Permissible.hasParcelHomeOthers get() = hasPermission("parcels.command.home.others")
-inline val Permissible.hasPermRandomSpecific get() = hasPermission("parcels.command.random.specific")
-val Player.parcelLimit: Int
- get() {
- for (info in effectivePermissions) {
- val perm = info.permission
- if (perm.startsWith("parcels.limit.")) {
- val limitString = perm.substring("parcels.limit.".length)
- if (limitString == "*") {
- return Int.MAX_VALUE
- }
- return limitString.toIntOrNull() ?: DEFAULT_LIMIT.also {
- logger.warn("$name has permission '$perm'. The suffix can not be parsed to an integer (or *).")
- }
- }
- }
- return DEFAULT_LIMIT
- }
-
-private const val DEFAULT_LIMIT = 1
-private val prefix = Formatting.translateChars('&', "&4[&c${JavaPlugin.getPlugin(ParcelsPlugin::class.java).name}&4] &a")
-
-fun Player.sendParcelMessage(except: Boolean = false, nopermit: Boolean = false, message: String) {
- if (except) {
- sendMessage(prefix + Formatting.YELLOW + Formatting.translateChars('&', message))
- } else if (nopermit) {
- sendMessage(prefix + Formatting.RED + Formatting.translateChars('&', message))
- } else {
- sendMessage(prefix + Formatting.translateChars('&', message))
- }
-}
-
-const val PLAYER_NAME_PLACEHOLDER = ":unknown_name:"
+package io.dico.parcels2.util.ext
+
+import io.dico.dicore.Formatting
+import io.dico.parcels2.ParcelsPlugin
+import io.dico.parcels2.logger
+import org.bukkit.OfflinePlayer
+import org.bukkit.entity.Player
+import org.bukkit.permissions.Permissible
+import org.bukkit.plugin.java.JavaPlugin
+
+inline val OfflinePlayer.uuid get() = uniqueId
+
+@Suppress("UsePropertyAccessSyntax")
+inline val OfflinePlayer.isValid
+ get() = isOnline() || hasPlayedBefore()
+
+const val PERM_BAN_BYPASS = "parcels.admin.bypass.ban"
+const val PERM_BUILD_ANYWHERE = "parcels.admin.bypass.build"
+const val PERM_ADMIN_MANAGE = "parcels.admin.manage"
+
+inline val Permissible.hasPermBanBypass get() = hasPermission(PERM_BAN_BYPASS)
+inline val Permissible.hasPermGamemodeBypass get() = hasPermission("parcels.admin.bypass.gamemode")
+inline val Permissible.hasPermBuildAnywhere get() = hasPermission(PERM_BUILD_ANYWHERE)
+inline val Permissible.hasPermAdminManage get() = hasPermission(PERM_ADMIN_MANAGE)
+inline val Permissible.hasParcelHomeOthers get() = hasPermission("parcels.command.home.others")
+inline val Permissible.hasPermRandomSpecific get() = hasPermission("parcels.command.random.specific")
+val Player.parcelLimit: Int
+ get() {
+ for (info in effectivePermissions) {
+ val perm = info.permission
+ if (perm.startsWith("parcels.limit.")) {
+ val limitString = perm.substring("parcels.limit.".length)
+ if (limitString == "*") {
+ return Int.MAX_VALUE
+ }
+ return limitString.toIntOrNull() ?: DEFAULT_LIMIT.also {
+ logger.warn("$name has permission '$perm'. The suffix can not be parsed to an integer (or *).")
+ }
+ }
+ }
+ return DEFAULT_LIMIT
+ }
+
+private const val DEFAULT_LIMIT = 1
+private val prefix = Formatting.translateChars('&', "&4[&c${JavaPlugin.getPlugin(ParcelsPlugin::class.java).name}&4] &a")
+
+fun Player.sendParcelMessage(except: Boolean = false, nopermit: Boolean = false, message: String) {
+ if (except) {
+ sendMessage(prefix + Formatting.YELLOW + Formatting.translateChars('&', message))
+ } else if (nopermit) {
+ sendMessage(prefix + Formatting.RED + Formatting.translateChars('&', message))
+ } else {
+ sendMessage(prefix + Formatting.translateChars('&', message))
+ }
+}
+
+const val PLAYER_NAME_PLACEHOLDER = ":unknown_name:"
diff --git a/src/main/kotlin/io/dico/parcels2/util/math/Dimension.kt b/src/main/kotlin/io/dico/parcels2/util/math/Dimension.kt
index cf67148..5b16860 100644
--- a/src/main/kotlin/io/dico/parcels2/util/math/Dimension.kt
+++ b/src/main/kotlin/io/dico/parcels2/util/math/Dimension.kt
@@ -1,19 +1,19 @@
-package io.dico.parcels2.util.math
-
-enum class Dimension {
- X,
- Y,
- Z;
-
- val otherDimensions
- get() = when (this) {
- X -> Y to Z
- Y -> X to Z
- Z -> X to Y
- }
-
- companion object {
- private val values = values()
- operator fun get(ordinal: Int) = values[ordinal]
- }
+package io.dico.parcels2.util.math
+
+enum class Dimension {
+ X,
+ Y,
+ Z;
+
+ val otherDimensions
+ get() = when (this) {
+ X -> Y to Z
+ Y -> X to Z
+ Z -> X to Y
+ }
+
+ companion object {
+ private val values = values()
+ operator fun get(ordinal: Int) = values[ordinal]
+ }
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/util/math/Math.kt b/src/main/kotlin/io/dico/parcels2/util/math/Math.kt
index 12c3e9f..5f8deef 100644
--- a/src/main/kotlin/io/dico/parcels2/util/math/Math.kt
+++ b/src/main/kotlin/io/dico/parcels2/util/math/Math.kt
@@ -1,42 +1,42 @@
-package io.dico.parcels2.util.math
-
-fun Double.floor(): Int {
- val down = toInt()
- if (down.toDouble() != this && (java.lang.Double.doubleToRawLongBits(this).ushr(63).toInt()) == 1) {
- return down - 1
- }
- return down
-}
-
-infix fun Int.umod(divisor: Int): Int {
- val out = this % divisor
- if (out < 0) {
- return out + divisor
- }
- return out
-}
-
-val Int.even: Boolean get() = and(1) == 0
-
-fun IntRange.clamp(min: Int, max: Int): IntRange {
- if (first < min) {
- if (last > max) {
- return IntRange(min, max)
- }
- return IntRange(min, last)
- }
- if (last > max) {
- return IntRange(first, max)
- }
- return this
-}
-
-// the name coerceAtMost is bad
-fun Int.clampMax(max: Int) = coerceAtMost(max)
-fun Double.clampMin(min: Double) = coerceAtLeast(min)
-fun Double.clampMax(max: Double) = coerceAtMost(max)
-
-// Why does this not exist?
-infix fun Int.ceilDiv(divisor: Int): Int {
- return -Math.floorDiv(-this, divisor)
+package io.dico.parcels2.util.math
+
+fun Double.floor(): Int {
+ val down = toInt()
+ if (down.toDouble() != this && (java.lang.Double.doubleToRawLongBits(this).ushr(63).toInt()) == 1) {
+ return down - 1
+ }
+ return down
+}
+
+infix fun Int.umod(divisor: Int): Int {
+ val out = this % divisor
+ if (out < 0) {
+ return out + divisor
+ }
+ return out
+}
+
+val Int.even: Boolean get() = and(1) == 0
+
+fun IntRange.clamp(min: Int, max: Int): IntRange {
+ if (first < min) {
+ if (last > max) {
+ return IntRange(min, max)
+ }
+ return IntRange(min, last)
+ }
+ if (last > max) {
+ return IntRange(first, max)
+ }
+ return this
+}
+
+// the name coerceAtMost is bad
+fun Int.clampMax(max: Int) = coerceAtMost(max)
+fun Double.clampMin(min: Double) = coerceAtLeast(min)
+fun Double.clampMax(max: Double) = coerceAtMost(max)
+
+// Why does this not exist?
+infix fun Int.ceilDiv(divisor: Int): Int {
+ return -Math.floorDiv(-this, divisor)
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/util/math/Region.kt b/src/main/kotlin/io/dico/parcels2/util/math/Region.kt
index cdbd497..cdcbe0e 100644
--- a/src/main/kotlin/io/dico/parcels2/util/math/Region.kt
+++ b/src/main/kotlin/io/dico/parcels2/util/math/Region.kt
@@ -1,37 +1,37 @@
-package io.dico.parcels2.util.math
-
-data class Region(val origin: Vec3i, val size: Vec3i) {
- val blockCount: Int get() = size.x * size.y * size.z
-
- val center: Vec3d
- get() {
- val x = (origin.x + size.x) / 2.0
- val y = (origin.y + size.y) / 2.0
- val z = (origin.z + size.z) / 2.0
- return Vec3d(x, y, z)
- }
-
- val end: Vec3i
- get() = origin + size
-
- val max: Vec3i
- get() = Vec3i(origin.x + size.x - 1, origin.y + size.y - 1, origin.z + size.z - 1)
-
- fun withSize(size: Vec3i): Region {
- if (size == this.size) return this
- return Region(origin, size)
- }
-
- operator fun contains(loc: Vec3i): Boolean = getFirstUncontainedDimensionOf(loc) == null
-
- fun getFirstUncontainedDimensionOf(loc: Vec3i): Dimension? {
- val max = max
- return when {
- loc.x !in origin.x..max.x -> Dimension.X
- loc.z !in origin.z..max.z -> Dimension.Z
- loc.y !in origin.y..max.y -> Dimension.Y
- else -> null
- }
- }
-
+package io.dico.parcels2.util.math
+
+data class Region(val origin: Vec3i, val size: Vec3i) {
+ val blockCount: Int get() = size.x * size.y * size.z
+
+ val center: Vec3d
+ get() {
+ val x = (origin.x + size.x) / 2.0
+ val y = (origin.y + size.y) / 2.0
+ val z = (origin.z + size.z) / 2.0
+ return Vec3d(x, y, z)
+ }
+
+ val end: Vec3i
+ get() = origin + size
+
+ val max: Vec3i
+ get() = Vec3i(origin.x + size.x - 1, origin.y + size.y - 1, origin.z + size.z - 1)
+
+ fun withSize(size: Vec3i): Region {
+ if (size == this.size) return this
+ return Region(origin, size)
+ }
+
+ operator fun contains(loc: Vec3i): Boolean = getFirstUncontainedDimensionOf(loc) == null
+
+ fun getFirstUncontainedDimensionOf(loc: Vec3i): Dimension? {
+ val max = max
+ return when {
+ loc.x !in origin.x..max.x -> Dimension.X
+ loc.z !in origin.z..max.z -> Dimension.Z
+ loc.y !in origin.y..max.y -> Dimension.Y
+ else -> null
+ }
+ }
+
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/util/math/Vec2i.kt b/src/main/kotlin/io/dico/parcels2/util/math/Vec2i.kt
index 5945120..3b25526 100644
--- a/src/main/kotlin/io/dico/parcels2/util/math/Vec2i.kt
+++ b/src/main/kotlin/io/dico/parcels2/util/math/Vec2i.kt
@@ -1,9 +1,9 @@
-package io.dico.parcels2.util.math
-
-data class Vec2i(
- val x: Int,
- val z: Int
-) {
- fun add(ox: Int, oz: Int) = Vec2i(x + ox, z + oz)
- fun toChunk() = Vec2i(x shr 4, z shr 4)
-}
+package io.dico.parcels2.util.math
+
+data class Vec2i(
+ val x: Int,
+ val z: Int
+) {
+ fun add(ox: Int, oz: Int) = Vec2i(x + ox, z + oz)
+ fun toChunk() = Vec2i(x shr 4, z shr 4)
+}
diff --git a/src/main/kotlin/io/dico/parcels2/util/math/Vec3d.kt b/src/main/kotlin/io/dico/parcels2/util/math/Vec3d.kt
index 787f46c..72b6dcd 100644
--- a/src/main/kotlin/io/dico/parcels2/util/math/Vec3d.kt
+++ b/src/main/kotlin/io/dico/parcels2/util/math/Vec3d.kt
@@ -1,53 +1,53 @@
-package io.dico.parcels2.util.math
-
-import org.bukkit.Location
-import kotlin.math.sqrt
-
-data class Vec3d(
- val x: Double,
- val y: Double,
- val z: Double
-) {
- constructor(loc: Location) : this(loc.x, loc.y, loc.z)
-
- operator fun plus(o: Vec3d) = Vec3d(x + o.x, y + o.y, z + o.z)
- operator fun minus(o: Vec3i) = Vec3d(x - o.x, y - o.y, z - o.z)
- infix fun addX(o: Double) = Vec3d(x + o, y, z)
- infix fun addY(o: Double) = Vec3d(x, y + o, z)
- infix fun addZ(o: Double) = Vec3d(x, y, z + o)
- infix fun withX(o: Double) = Vec3d(o, y, z)
- infix fun withY(o: Double) = Vec3d(x, o, z)
- infix fun withZ(o: Double) = Vec3d(x, y, o)
- fun add(ox: Double, oy: Double, oz: Double) = Vec3d(x + ox, y + oy, z + oz)
- fun toVec3i() = Vec3i(x.floor(), y.floor(), z.floor())
-
- fun distanceSquared(o: Vec3d): Double {
- val dx = o.x - x
- val dy = o.y - y
- val dz = o.z - z
- return dx * dx + dy * dy + dz * dz
- }
-
- fun distance(o: Vec3d) = sqrt(distanceSquared(o))
-
- operator fun get(dimension: Dimension) =
- when (dimension) {
- Dimension.X -> x
- Dimension.Y -> y
- Dimension.Z -> z
- }
-
- fun with(dimension: Dimension, value: Double) =
- when (dimension) {
- Dimension.X -> withX(value)
- Dimension.Y -> withY(value)
- Dimension.Z -> withZ(value)
- }
-
- fun add(dimension: Dimension, value: Double) =
- when (dimension) {
- Dimension.X -> addX(value)
- Dimension.Y -> addY(value)
- Dimension.Z -> addZ(value)
- }
+package io.dico.parcels2.util.math
+
+import org.bukkit.Location
+import kotlin.math.sqrt
+
+data class Vec3d(
+ val x: Double,
+ val y: Double,
+ val z: Double
+) {
+ constructor(loc: Location) : this(loc.x, loc.y, loc.z)
+
+ operator fun plus(o: Vec3d) = Vec3d(x + o.x, y + o.y, z + o.z)
+ operator fun minus(o: Vec3i) = Vec3d(x - o.x, y - o.y, z - o.z)
+ infix fun addX(o: Double) = Vec3d(x + o, y, z)
+ infix fun addY(o: Double) = Vec3d(x, y + o, z)
+ infix fun addZ(o: Double) = Vec3d(x, y, z + o)
+ infix fun withX(o: Double) = Vec3d(o, y, z)
+ infix fun withY(o: Double) = Vec3d(x, o, z)
+ infix fun withZ(o: Double) = Vec3d(x, y, o)
+ fun add(ox: Double, oy: Double, oz: Double) = Vec3d(x + ox, y + oy, z + oz)
+ fun toVec3i() = Vec3i(x.floor(), y.floor(), z.floor())
+
+ fun distanceSquared(o: Vec3d): Double {
+ val dx = o.x - x
+ val dy = o.y - y
+ val dz = o.z - z
+ return dx * dx + dy * dy + dz * dz
+ }
+
+ fun distance(o: Vec3d) = sqrt(distanceSquared(o))
+
+ operator fun get(dimension: Dimension) =
+ when (dimension) {
+ Dimension.X -> x
+ Dimension.Y -> y
+ Dimension.Z -> z
+ }
+
+ fun with(dimension: Dimension, value: Double) =
+ when (dimension) {
+ Dimension.X -> withX(value)
+ Dimension.Y -> withY(value)
+ Dimension.Z -> withZ(value)
+ }
+
+ fun add(dimension: Dimension, value: Double) =
+ when (dimension) {
+ Dimension.X -> addX(value)
+ Dimension.Y -> addY(value)
+ Dimension.Z -> addZ(value)
+ }
} \ No newline at end of file
diff --git a/src/main/kotlin/io/dico/parcels2/util/math/Vec3i.kt b/src/main/kotlin/io/dico/parcels2/util/math/Vec3i.kt
index b25764e..484ad13 100644
--- a/src/main/kotlin/io/dico/parcels2/util/math/Vec3i.kt
+++ b/src/main/kotlin/io/dico/parcels2/util/math/Vec3i.kt
@@ -1,105 +1,105 @@
-package io.dico.parcels2.util.math
-
-import org.bukkit.Location
-import org.bukkit.World
-import org.bukkit.block.Block
-import org.bukkit.block.BlockFace
-
-data class Vec3i(
- val x: Int,
- val y: Int,
- val z: Int
-) {
- constructor(loc: Location) : this(loc.blockX, loc.blockY, loc.blockZ)
- constructor(block: Block) : this(block.x, block.y, block.z)
-
- fun toVec2i() = Vec2i(x, z)
- operator fun plus(o: Vec3i) = Vec3i(x + o.x, y + o.y, z + o.z)
- operator fun minus(o: Vec3i) = Vec3i(x - o.x, y - o.y, z - o.z)
- infix fun addX(o: Int) = Vec3i(x + o, y, z)
- infix fun addY(o: Int) = Vec3i(x, y + o, z)
- infix fun addZ(o: Int) = Vec3i(x, y, z + o)
- infix fun withX(o: Int) = Vec3i(o, y, z)
- infix fun withY(o: Int) = Vec3i(x, o, z)
- infix fun withZ(o: Int) = Vec3i(x, y, o)
- fun add(ox: Int, oy: Int, oz: Int) = Vec3i(x + ox, y + oy, z + oz)
- fun neg() = Vec3i(-x, -y, -z)
- fun clampMax(o: Vec3i) = Vec3i(x.clampMax(o.x), y.clampMax(o.y), z.clampMax(o.z))
-
- operator fun get(dimension: Dimension) =
- when (dimension) {
- Dimension.X -> x
- Dimension.Y -> y
- Dimension.Z -> z
- }
-
- fun with(dimension: Dimension, value: Int) =
- when (dimension) {
- Dimension.X -> withX(value)
- Dimension.Y -> withY(value)
- Dimension.Z -> withZ(value)
- }
-
- fun add(dimension: Dimension, value: Int) =
- when (dimension) {
- Dimension.X -> addX(value)
- Dimension.Y -> addY(value)
- Dimension.Z -> addZ(value)
- }
-
- companion object {
- private operator fun invoke(face: BlockFace) = Vec3i(face.modX, face.modY, face.modZ)
- val down = Vec3i(BlockFace.DOWN)
- val up = Vec3i(BlockFace.UP)
- val north = Vec3i(BlockFace.NORTH)
- val east = Vec3i(BlockFace.EAST)
- val south = Vec3i(BlockFace.SOUTH)
- val west = Vec3i(BlockFace.WEST)
-
- fun convert(face: BlockFace) = when (face) {
- BlockFace.DOWN -> down
- BlockFace.UP -> up
- BlockFace.NORTH -> north
- BlockFace.EAST -> east
- BlockFace.SOUTH -> south
- BlockFace.WEST -> west
- else -> Vec3i(face)
- }
- }
-}
-
-@Suppress("NOTHING_TO_INLINE")
-inline operator fun World.get(vec: Vec3i): Block = getBlockAt(vec.x, vec.y, vec.z)
-
-/*
-private /*inline */class IVec3i(private val data: Long) {
-
- private companion object {
- const val mask = 0x001F_FFFF
- const val max: Int = 0x000F_FFFF // +1048575
- const val min: Int = -max - 1 // -1048575 // 0xFFF0_0000
-
- @Suppress("NOTHING_TO_INLINE")
- inline fun Int.compressIntoLong(offset: Int): Long {
- if (this !in min..max) throw IllegalArgumentException()
- return and(mask).toLong().shl(offset)
- }
-
- @Suppress("NOTHING_TO_INLINE")
- inline fun Long.extractInt(offset: Int): Int {
- val result = ushr(offset).toInt().and(mask)
- return if (result > max) result or mask.inv() else result
- }
- }
-
- constructor(x: Int, y: Int, z: Int) : this(
- x.compressIntoLong(42)
- or y.compressIntoLong(21)
- or z.compressIntoLong(0))
-
- val x: Int get() = data.extractInt(42)
- val y: Int get() = data.extractInt(21)
- val z: Int get() = data.extractInt(0)
-
-}
-*/
+package io.dico.parcels2.util.math
+
+import org.bukkit.Location
+import org.bukkit.World
+import org.bukkit.block.Block
+import org.bukkit.block.BlockFace
+
+data class Vec3i(
+ val x: Int,
+ val y: Int,
+ val z: Int
+) {
+ constructor(loc: Location) : this(loc.blockX, loc.blockY, loc.blockZ)
+ constructor(block: Block) : this(block.x, block.y, block.z)
+
+ fun toVec2i() = Vec2i(x, z)
+ operator fun plus(o: Vec3i) = Vec3i(x + o.x, y + o.y, z + o.z)
+ operator fun minus(o: Vec3i) = Vec3i(x - o.x, y - o.y, z - o.z)
+ infix fun addX(o: Int) = Vec3i(x + o, y, z)
+ infix fun addY(o: Int) = Vec3i(x, y + o, z)
+ infix fun addZ(o: Int) = Vec3i(x, y, z + o)
+ infix fun withX(o: Int) = Vec3i(o, y, z)
+ infix fun withY(o: Int) = Vec3i(x, o, z)
+ infix fun withZ(o: Int) = Vec3i(x, y, o)
+ fun add(ox: Int, oy: Int, oz: Int) = Vec3i(x + ox, y + oy, z + oz)
+ fun neg() = Vec3i(-x, -y, -z)
+ fun clampMax(o: Vec3i) = Vec3i(x.clampMax(o.x), y.clampMax(o.y), z.clampMax(o.z))
+
+ operator fun get(dimension: Dimension) =
+ when (dimension) {
+ Dimension.X -> x
+ Dimension.Y -> y
+ Dimension.Z -> z
+ }
+
+ fun with(dimension: Dimension, value: Int) =
+ when (dimension) {
+ Dimension.X -> withX(value)
+ Dimension.Y -> withY(value)
+ Dimension.Z -> withZ(value)
+ }
+
+ fun add(dimension: Dimension, value: Int) =
+ when (dimension) {
+ Dimension.X -> addX(value)
+ Dimension.Y -> addY(value)
+ Dimension.Z -> addZ(value)
+ }
+
+ companion object {
+ private operator fun invoke(face: BlockFace) = Vec3i(face.modX, face.modY, face.modZ)
+ val down = Vec3i(BlockFace.DOWN)
+ val up = Vec3i(BlockFace.UP)
+ val north = Vec3i(BlockFace.NORTH)
+ val east = Vec3i(BlockFace.EAST)
+ val south = Vec3i(BlockFace.SOUTH)
+ val west = Vec3i(BlockFace.WEST)
+
+ fun convert(face: BlockFace) = when (face) {
+ BlockFace.DOWN -> down
+ BlockFace.UP -> up
+ BlockFace.NORTH -> north
+ BlockFace.EAST -> east
+ BlockFace.SOUTH -> south
+ BlockFace.WEST -> west
+ else -> Vec3i(face)
+ }
+ }
+}
+
+@Suppress("NOTHING_TO_INLINE")
+inline operator fun World.get(vec: Vec3i): Block = getBlockAt(vec.x, vec.y, vec.z)
+
+/*
+private /*inline */class IVec3i(private val data: Long) {
+
+ private companion object {
+ const val mask = 0x001F_FFFF
+ const val max: Int = 0x000F_FFFF // +1048575
+ const val min: Int = -max - 1 // -1048575 // 0xFFF0_0000
+
+ @Suppress("NOTHING_TO_INLINE")
+ inline fun Int.compressIntoLong(offset: Int): Long {
+ if (this !in min..max) throw IllegalArgumentException()
+ return and(mask).toLong().shl(offset)
+ }
+
+ @Suppress("NOTHING_TO_INLINE")
+ inline fun Long.extractInt(offset: Int): Int {
+ val result = ushr(offset).toInt().and(mask)
+ return if (result > max) result or mask.inv() else result
+ }
+ }
+
+ constructor(x: Int, y: Int, z: Int) : this(
+ x.compressIntoLong(42)
+ or y.compressIntoLong(21)
+ or z.compressIntoLong(0))
+
+ val x: Int get() = data.extractInt(42)
+ val y: Int get() = data.extractInt(21)
+ val z: Int get() = data.extractInt(0)
+
+}
+*/
diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml
index eff524c..c2e272e 100644
--- a/src/main/resources/logback.xml
+++ b/src/main/resources/logback.xml
@@ -1,17 +1,17 @@
-<configuration>
- <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
- <encoder>
- <!-- old pattern <pattern>%d{HH:mm:ss.SSS} %magenta(%-8.-8(%thread)) %highlight(%-5level) %boldCyan(%8.-32logger{32}) - %msg\n</pattern>-->
- <pattern>%magenta(%-16.-16(%thread)) %highlight(%-5level) %boldCyan(%6.-32logger{32}) - %msg</pattern>
- </encoder>
- </appender>
-
- <root level="debug">
- <appender-ref ref="STDOUT" />
- </root>
-
- <logger name="com.zaxxer.hikari.pool.HikariPool" level="info"/>
- <logger name="com.zaxxer.hikari.pool.PoolBase" level="info"/>
- <logger name="com.zaxxer.hikari.HikariConfig" level="info"/>
- <logger name="Exposed" level="info"/>
+<configuration>
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <!-- old pattern <pattern>%d{HH:mm:ss.SSS} %magenta(%-8.-8(%thread)) %highlight(%-5level) %boldCyan(%8.-32logger{32}) - %msg\n</pattern>-->
+ <pattern>%magenta(%-16.-16(%thread)) %highlight(%-5level) %boldCyan(%6.-32logger{32}) - %msg</pattern>
+ </encoder>
+ </appender>
+
+ <root level="debug">
+ <appender-ref ref="STDOUT" />
+ </root>
+
+ <logger name="com.zaxxer.hikari.pool.HikariPool" level="info"/>
+ <logger name="com.zaxxer.hikari.pool.PoolBase" level="info"/>
+ <logger name="com.zaxxer.hikari.HikariConfig" level="info"/>
+ <logger name="Exposed" level="info"/>
</configuration> \ No newline at end of file
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
index 19c68da..8830377 100644
--- a/src/main/resources/plugin.yml
+++ b/src/main/resources/plugin.yml
@@ -1,6 +1,6 @@
-name: Parcels
-author: Dico
-main: io.dico.parcels2.ParcelsPlugin
-version: 0.1
-api-version: 1.13
+name: Parcels
+author: Dico
+main: io.dico.parcels2.ParcelsPlugin
+version: 0.1
+api-version: 1.13
load: STARTUP \ No newline at end of file
diff --git a/todo.md b/todo.md
index 6f3b12a..cb073df 100644
--- a/todo.md
+++ b/todo.md
@@ -1,103 +1,103 @@
-# Parcels Todo list
-
-Commands
--
-Basically all admin commands.
-* ~~setowner~~
-* ~~dispose~~
-* ~~reset~~
-* ~~swap~~
-* New admin commands that I can't think of right now.
-
-Also
-* ~~setbiome~~
-* random
-
-~~Modify home command:~~
-* ~~Make `:` not be required if prior component cannot be parsed to an int~~
-* ~~Listen for command events that use plotme-style argument, and transform the command~~
-
-~~Add permissions to commands (replace or fix `IContextFilter` from command lib
-to allow inheriting permissions properly).~~
-
-Parcel Options
--
-
-Parcel options apply to any player with `DEFAULT` added status.
-They affect what their permissions might be within the parcel.
-
-Apart from `/p option inputs`, `/p option inventory`, the following might be considered.
-
-~~Move existing options to "interact" namespace (`/p o interact`)
-Add classes for different things you can interact with~~
-
-~~Then,~~
-~~* Split `/p option interact inputs` into a list of interactible block types.~~
-~~The list could include container blocks, merging the existing inventory option.~~
-* Players cannot launch projectiles in locations where they can't build.~~
-This could become optional.
-
-* Option to control spreading and/or forming of blocks such as grass and ice within the parcel.~~
-
-Block Management
--
-~~Update the parcel corner with owner info when a player flies into the parcel (after migrations).
-Parcels has a player head in that corner in addition to the sign that PlotMe uses.~~
-
-~~Commands that modify parcel blocks must be kept track of to prevent multiple
-from running simultaneously in the same parcel. `hasBlockVisitors` field must be updated.
-In general, spamming the commands must be caught at all cost to avoid lots of lag.~~
-
-~~Swap - schematic is in place, but proper placement order must be enforced to make sure that attachable
-blocks are placed properly. Alternatively, if a block change method can be found that doesn't
-cause block updates, that would be preferred subject to having good performance.~~
-
-~~Change `RegionTraversal` to allow traversing different parts of a region in a different order.
-This could apply to clearing of plots, for example. It would be better if the bottom 64 (floor height)
-layers are done upwards, and the rest downwards.~~
-
-Events
--
-Prevent block spreading subject to conditions.
-
-Scan through blocks that were added since original Parcels implementation,
-that might introduce things that need to be checked or listened for.
-
-~~WorldEdit Listener.~~
-
-Limit number of beacons in a parcel and/or avoid potion effects being applied outside the parcel.
-
-Database
--
-Find and patch ways to add new useless entries (for regular players at least)
-
-Prevent invalid player names from being saved to the database.
-Here, invalid player names mean names that contain invalid characters.
-
-Use an atomic GET OR INSERT query so that parallel execution doesn't cause problems
-(as is currently the case when migrating).
-
-Implement a container that doesn't require loading all parcel data on startup (Complex).
-
-~~Update player profiles in the database on join to account for name changes.~~
-
-~~Store player status on parcel (allowed, default banned) as a number to allow for future additions to this set of possibilities~~
-
-
-After testing on Redstoner
--
-
-Clear (and swap) entities on /p clear etc
-Fix command lag
-Chorus fruit can grow outside plots
-Vines can grow outside plots
-Ghasts, bats, phantoms and magma cubes can be spawned with eggs
-ParcelTarget doesn't report a world that wasn't found correctly
-Jumping on turtle eggs is considered as interacting with pressure plates
-Setbiome internal error when progress reporting is attached
-Unclaim doesn't clear the plot. It probably should.
-Players can shoot boats and minecarts.
-You can use disabled items by rightclicking air.
-Tab complete isn't working correctly.
-~~Bed use in nether and end might not have to be blocked.~~
-
+# Parcels Todo list
+
+Commands
+-
+Basically all admin commands.
+* ~~setowner~~
+* ~~dispose~~
+* ~~reset~~
+* ~~swap~~
+* New admin commands that I can't think of right now.
+
+Also
+* ~~setbiome~~
+* random
+
+~~Modify home command:~~
+* ~~Make `:` not be required if prior component cannot be parsed to an int~~
+* ~~Listen for command events that use plotme-style argument, and transform the command~~
+
+~~Add permissions to commands (replace or fix `IContextFilter` from command lib
+to allow inheriting permissions properly).~~
+
+Parcel Options
+-
+
+Parcel options apply to any player with `DEFAULT` added status.
+They affect what their permissions might be within the parcel.
+
+Apart from `/p option inputs`, `/p option inventory`, the following might be considered.
+
+~~Move existing options to "interact" namespace (`/p o interact`)
+Add classes for different things you can interact with~~
+
+~~Then,~~
+~~* Split `/p option interact inputs` into a list of interactible block types.~~
+~~The list could include container blocks, merging the existing inventory option.~~
+* Players cannot launch projectiles in locations where they can't build.~~
+This could become optional.
+
+* Option to control spreading and/or forming of blocks such as grass and ice within the parcel.~~
+
+Block Management
+-
+~~Update the parcel corner with owner info when a player flies into the parcel (after migrations).
+Parcels has a player head in that corner in addition to the sign that PlotMe uses.~~
+
+~~Commands that modify parcel blocks must be kept track of to prevent multiple
+from running simultaneously in the same parcel. `hasBlockVisitors` field must be updated.
+In general, spamming the commands must be caught at all cost to avoid lots of lag.~~
+
+~~Swap - schematic is in place, but proper placement order must be enforced to make sure that attachable
+blocks are placed properly. Alternatively, if a block change method can be found that doesn't
+cause block updates, that would be preferred subject to having good performance.~~
+
+~~Change `RegionTraversal` to allow traversing different parts of a region in a different order.
+This could apply to clearing of plots, for example. It would be better if the bottom 64 (floor height)
+layers are done upwards, and the rest downwards.~~
+
+Events
+-
+Prevent block spreading subject to conditions.
+
+Scan through blocks that were added since original Parcels implementation,
+that might introduce things that need to be checked or listened for.
+
+~~WorldEdit Listener.~~
+
+Limit number of beacons in a parcel and/or avoid potion effects being applied outside the parcel.
+
+Database
+-
+Find and patch ways to add new useless entries (for regular players at least)
+
+Prevent invalid player names from being saved to the database.
+Here, invalid player names mean names that contain invalid characters.
+
+Use an atomic GET OR INSERT query so that parallel execution doesn't cause problems
+(as is currently the case when migrating).
+
+Implement a container that doesn't require loading all parcel data on startup (Complex).
+
+~~Update player profiles in the database on join to account for name changes.~~
+
+~~Store player status on parcel (allowed, default banned) as a number to allow for future additions to this set of possibilities~~
+
+
+After testing on Redstoner
+-
+
+Clear (and swap) entities on /p clear etc
+Fix command lag
+Chorus fruit can grow outside plots
+Vines can grow outside plots
+Ghasts, bats, phantoms and magma cubes can be spawned with eggs
+ParcelTarget doesn't report a world that wasn't found correctly
+Jumping on turtle eggs is considered as interacting with pressure plates
+Setbiome internal error when progress reporting is attached
+Unclaim doesn't clear the plot. It probably should.
+Players can shoot boats and minecarts.
+You can use disabled items by rightclicking air.
+Tab complete isn't working correctly.
+~~Bed use in nether and end might not have to be blocked.~~
+