From 3549f04a339635628f76c1de14365997f21a7b51 Mon Sep 17 00:00:00 2001 From: wizjany Date: Sat, 12 Mar 2022 17:21:50 -0500 Subject: [PATCH 1/6] Build script updates. Bump gradle. Copy a bunch from WE. Hopefully nothing breaks. --- .gitattributes | 34 +++- buildSrc/build.gradle.kts | 8 +- buildSrc/src/main/kotlin/CommonConfig.kt | 2 +- buildSrc/src/main/kotlin/CommonJavaConfig.kt | 86 +++++++++ buildSrc/src/main/kotlin/GradleExtras.kt | 4 +- buildSrc/src/main/kotlin/LibsConfig.kt | 46 +++-- buildSrc/src/main/kotlin/PlatformConfig.kt | 62 +------ buildSrc/src/main/kotlin/Versions.kt | 11 +- gradle.properties | 2 +- gradle/wrapper/gradle-wrapper.jar | Bin 59536 -> 59821 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew.bat | 178 +++++++++---------- worldguard-core/build.gradle.kts | 4 +- worldguard-libs/bukkit/build.gradle.kts | 1 + 14 files changed, 265 insertions(+), 175 deletions(-) create mode 100644 buildSrc/src/main/kotlin/CommonJavaConfig.kt diff --git a/.gitattributes b/.gitattributes index cc8847fd1..2df98e026 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,34 @@ -*.java diff=java +* text=auto eol=lf +# Force Batch files to CRLF +*.bat eol=crlf -text +# Java sources +*.java text diff=java +*.kt text diff=java +*.gradle text diff=java +*.gradle.kts text diff=java + +# These files are text and should be normalized (Convert crlf => lf) +*.css text diff=css +*.df text +*.htm text diff=html +*.html text diff=html +*.js text +*.jsp text +*.jspf text +*.jspx text +*.properties text +*.tld text +*.tag text +*.tagx text +*.xml text + +# These files are binary and should be left untouched +# (binary is a macro for -text -diff) +*.class binary +*.dll binary +*.ear binary +*.jar binary +*.so binary +*.war binary +*.jks binary diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 6d0184124..ef3a43b2d 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -11,7 +11,7 @@ repositories { dependencies { implementation(gradleApi()) implementation("gradle.plugin.org.cadixdev.gradle:licenser:0.6.1") - implementation("org.ajoberstar.grgit:grgit-gradle:4.1.0") - implementation("com.github.jengelman.gradle.plugins:shadow:6.1.0") - implementation("org.jfrog.buildinfo:build-info-extractor-gradle:4.21.0") -} \ No newline at end of file + implementation("org.ajoberstar.grgit:grgit-gradle:4.1.1") + implementation("gradle.plugin.com.github.johnrengelman:shadow:7.1.2") + implementation("org.jfrog.buildinfo:build-info-extractor-gradle:4.27.1") +} diff --git a/buildSrc/src/main/kotlin/CommonConfig.kt b/buildSrc/src/main/kotlin/CommonConfig.kt index 2a01ea900..6126bb49a 100644 --- a/buildSrc/src/main/kotlin/CommonConfig.kt +++ b/buildSrc/src/main/kotlin/CommonConfig.kt @@ -25,7 +25,7 @@ fun Project.applyCommonConfiguration() { plugins.withId("java") { the().toolchain { - languageVersion.set(JavaLanguageVersion.of(16)) + languageVersion.set(JavaLanguageVersion.of(17)) } } diff --git a/buildSrc/src/main/kotlin/CommonJavaConfig.kt b/buildSrc/src/main/kotlin/CommonJavaConfig.kt new file mode 100644 index 000000000..ca74a3b68 --- /dev/null +++ b/buildSrc/src/main/kotlin/CommonJavaConfig.kt @@ -0,0 +1,86 @@ +import org.gradle.api.Project +import org.gradle.api.plugins.JavaPluginExtension +import org.gradle.api.plugins.quality.CheckstyleExtension +import org.gradle.api.tasks.compile.JavaCompile +import org.gradle.api.tasks.javadoc.Javadoc +import org.gradle.api.tasks.testing.Test +import org.gradle.external.javadoc.StandardJavadocDocletOptions +import org.gradle.kotlin.dsl.apply +import org.gradle.kotlin.dsl.configure +import org.gradle.kotlin.dsl.dependencies +import org.gradle.kotlin.dsl.get +import org.gradle.kotlin.dsl.withType + +fun Project.applyCommonJavaConfiguration(sourcesJar: Boolean, javaRelease: Int = 8, banSlf4j: Boolean = true) { + applyCommonConfiguration() + apply(plugin = "eclipse") + apply(plugin = "idea") + apply(plugin = "checkstyle") + + tasks + .withType() + .matching { it.name == "compileJava" || it.name == "compileTestJava" } + .configureEach { + val disabledLint = listOf( + "processing", "path", "fallthrough", "serial" + ) + options.release.set(javaRelease) + options.compilerArgs.addAll(listOf("-Xlint:all") + disabledLint.map { "-Xlint:-$it" }) + options.isDeprecation = true + options.encoding = "UTF-8" + options.compilerArgs.add("-parameters") + } + + configure { + configFile = rootProject.file("config/checkstyle/checkstyle.xml") + toolVersion = "9.1" + } + + tasks.withType().configureEach { + useJUnitPlatform() + } + + dependencies { + "compileOnly"("com.google.code.findbugs:jsr305:${Versions.FINDBUGS}") + "testImplementation"("org.junit.jupiter:junit-jupiter-api:${Versions.JUNIT}") + "testImplementation"("org.junit.jupiter:junit-jupiter-params:${Versions.JUNIT}") + "testImplementation"("org.mockito:mockito-core:${Versions.MOCKITO}") + "testImplementation"("org.mockito:mockito-junit-jupiter:${Versions.MOCKITO}") + "testRuntimeOnly"("org.junit.jupiter:junit-jupiter-engine:${Versions.JUNIT}") + } + + // Java 8 turns on doclint which we fail + tasks.withType().configureEach { + options.encoding = "UTF-8" + (options as StandardJavadocDocletOptions).apply { + addStringOption("Xdoclint:none", "-quiet") + tags( + "apiNote:a:API Note:", + "implSpec:a:Implementation Requirements:", + "implNote:a:Implementation Note:" + ) + } + } + + configure { + disableAutoTargetJvm() + withJavadocJar() + if (sourcesJar) { + withSourcesJar() + } + } + + if (banSlf4j) { + configurations["compileClasspath"].apply { + resolutionStrategy.componentSelection { + withModule("org.slf4j:slf4j-api") { + reject("No SLF4J allowed on compile classpath") + } + } + } + } + + tasks.named("check").configure { + dependsOn("checkstyleMain", "checkstyleTest") + } +} diff --git a/buildSrc/src/main/kotlin/GradleExtras.kt b/buildSrc/src/main/kotlin/GradleExtras.kt index e7d1e0ede..beedf1607 100644 --- a/buildSrc/src/main/kotlin/GradleExtras.kt +++ b/buildSrc/src/main/kotlin/GradleExtras.kt @@ -1,6 +1,6 @@ import org.gradle.api.Project import org.gradle.api.plugins.ExtraPropertiesExtension -import org.gradle.api.plugins.JavaPluginConvention +import org.gradle.api.plugins.JavaPluginExtension import org.gradle.api.tasks.SourceSetContainer import org.gradle.kotlin.dsl.getByType import org.gradle.kotlin.dsl.the @@ -9,4 +9,4 @@ val Project.ext: ExtraPropertiesExtension get() = extensions.getByType() val Project.sourceSets: SourceSetContainer - get() = the().sourceSets + get() = the().sourceSets diff --git a/buildSrc/src/main/kotlin/LibsConfig.kt b/buildSrc/src/main/kotlin/LibsConfig.kt index 3a78ce604..9acb5a109 100644 --- a/buildSrc/src/main/kotlin/LibsConfig.kt +++ b/buildSrc/src/main/kotlin/LibsConfig.kt @@ -1,6 +1,7 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import org.gradle.api.Plugin import org.gradle.api.Project +import org.gradle.api.artifacts.ExternalModuleDependency import org.gradle.api.artifacts.ModuleDependency import org.gradle.api.attributes.Bundling import org.gradle.api.attributes.Category @@ -52,22 +53,22 @@ fun Project.applyLibrariesConfiguration() { } val altConfigFiles = { artifactType: String -> val deps = configurations["shade"].incoming.dependencies - .filterIsInstance() - .map { it.copy() } - .map { dependency -> - dependency.artifact { - name = dependency.name - type = artifactType - extension = "jar" - classifier = artifactType - } - dependency + .filterIsInstance() + .map { it.copy() } + .map { dependency -> + dependency.artifact { + name = dependency.name + type = artifactType + extension = "jar" + classifier = artifactType } + dependency + } files(configurations.detachedConfiguration(*deps.toTypedArray()) - .resolvedConfiguration.lenientConfiguration.artifacts - .filter { it.classifier == artifactType } - .map { zipTree(it.file) }) + .resolvedConfiguration.lenientConfiguration.artifacts + .filter { it.classifier == artifactType } + .map { zipTree(it.file) }) } tasks.register("sourcesJar") { from({ @@ -161,11 +162,26 @@ fun Project.applyLibrariesConfiguration() { applyCommonArtifactoryConfig() } +// A horrible hack because `softwareComponentFactory` has to be gotten via plugin +// gradle why internal open class LibsConfigPluginHack @Inject constructor( - private val softwareComponentFactory: SoftwareComponentFactory + private val softwareComponentFactory: SoftwareComponentFactory ) : Plugin { override fun apply(project: Project) { val libsComponents = softwareComponentFactory.adhoc("libs") project.components.add(libsComponents) } -} \ No newline at end of file +} + +fun Project.constrainDependenciesToLibsCore() { + evaluationDependsOn(":worldguard-libs:core") + val coreDeps = project(":worldguard-libs:core").configurations["shade"].dependencies + .filterIsInstance() + dependencies.constraints { + for (coreDep in coreDeps) { + add("shade", "${coreDep.group}:${coreDep.name}:${coreDep.version}") { + because("libs should align with libs:core") + } + } + } +} diff --git a/buildSrc/src/main/kotlin/PlatformConfig.kt b/buildSrc/src/main/kotlin/PlatformConfig.kt index 659af3990..48fe104b0 100644 --- a/buildSrc/src/main/kotlin/PlatformConfig.kt +++ b/buildSrc/src/main/kotlin/PlatformConfig.kt @@ -1,78 +1,30 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import org.gradle.api.Project import org.gradle.api.component.AdhocComponentWithVariants -import org.gradle.api.plugins.JavaPluginExtension -import org.gradle.api.plugins.quality.CheckstyleExtension import org.gradle.api.publish.PublishingExtension import org.gradle.api.publish.maven.MavenPublication -import org.gradle.api.tasks.javadoc.Javadoc -import org.gradle.api.tasks.testing.Test -import org.gradle.external.javadoc.StandardJavadocDocletOptions import org.gradle.kotlin.dsl.apply import org.gradle.kotlin.dsl.configure -import org.gradle.kotlin.dsl.dependencies import org.gradle.kotlin.dsl.get import org.gradle.kotlin.dsl.named import org.gradle.kotlin.dsl.register -import org.gradle.kotlin.dsl.withType -import org.gradle.kotlin.dsl.the -fun Project.applyPlatformAndCoreConfiguration() { +fun Project.applyPlatformAndCoreConfiguration(javaRelease: Int = 17) { applyCommonConfiguration() apply(plugin = "java") - apply(plugin = "eclipse") - apply(plugin = "idea") apply(plugin = "maven-publish") - apply(plugin = "checkstyle") apply(plugin = "com.jfrog.artifactory") + applyCommonJavaConfiguration( + sourcesJar = true, + javaRelease = javaRelease, + banSlf4j = false + ) ext["internalVersion"] = "$version+${rootProject.ext["gitCommitHash"]}" - configure { - configFile = rootProject.file("config/checkstyle/checkstyle.xml") - toolVersion = "8.34" - } - - tasks.withType().configureEach { - useJUnitPlatform() - } - - dependencies { - "compileOnly"("com.google.code.findbugs:jsr305:3.0.2") - "testCompileOnly"("com.google.code.findbugs:jsr305:3.0.2") - "testImplementation"("org.junit.jupiter:junit-jupiter-api:${Versions.JUNIT}") - "testImplementation"("org.junit.jupiter:junit-jupiter-params:${Versions.JUNIT}") - "testImplementation"("org.mockito:mockito-core:${Versions.MOCKITO}") - "testImplementation"("org.mockito:mockito-junit-jupiter:${Versions.MOCKITO}") - "testRuntimeOnly"("org.junit.jupiter:junit-jupiter-engine:${Versions.JUNIT}") - } - - // Java 8 turns on doclint which we fail - tasks.withType().configureEach { - (options as StandardJavadocDocletOptions).apply { - addStringOption("Xdoclint:none", "-quiet") - tags( - "apiNote:a:API Note:", - "implSpec:a:Implementation Requirements:", - "implNote:a:Implementation Note:" - ) - } - } - - the().withJavadocJar() - - if (name == "worldguard-core" || name == "worldguard-bukkit") { - the().withSourcesJar() - } - - tasks.named("check").configure { - dependsOn("checkstyleMain", "checkstyleTest") - } - configure { publications { register("maven") { - from(components["java"]) versionMapping { usage("java-api") { fromResolutionOf("runtimeClasspath") @@ -105,8 +57,10 @@ fun Project.applyShadowConfiguration() { exclude("GradleStart**") exclude(".cache") exclude("LICENSE*") + exclude("META-INF/maven/**") } val javaComponent = components["java"] as AdhocComponentWithVariants + // I don't think we want this published (it's the shadow jar) javaComponent.withVariantsFromConfiguration(configurations["shadowRuntimeElements"]) { skip() } diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index f90e6d749..2c086d6c0 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -1,9 +1,10 @@ object Versions { -// const val PISTON = "0.4.3" -// const val AUTO_VALUE = "1.6.5" - const val WORLDEDIT = "7.2.7" - const val JUNIT = "5.7.0" + const val WORLDEDIT = "7.2.9" + const val PISTON = "0.5.7" + const val AUTO_VALUE = "1.9" + const val JUNIT = "5.8.1" + const val MOCKITO = "4.3.1" const val SQUIRRELID = "0.3.0" const val GUAVA = "31.0.1-jre" - const val MOCKITO = "3.7.7" + const val FINDBUGS = "3.0.2" } diff --git a/gradle.properties b/gradle.properties index c29993560..2c13f50cb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,2 @@ group=com.sk89q.worldguard -version=7.0.8-SNAPSHOT +version=7.1.0-SNAPSHOT diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7454180f2ae8848c63b8b4dea2cb829da983f2fa..41d9927a4d4fb3f96a785543079b8df6723c946b 100644 GIT binary patch delta 8958 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e750102e0..00e33edef 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew.bat b/gradlew.bat index 107acd32c..ac1b06f93 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,89 +1,89 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@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 Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@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="-Xmx64m" "-Xms64m" - -@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 execute - -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 execute - -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 - -: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 %* - -: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 +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@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 Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@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="-Xmx64m" "-Xms64m" + +@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 execute + +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 execute + +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 + +: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 %* + +: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/worldguard-core/build.gradle.kts b/worldguard-core/build.gradle.kts index 35e2a22b7..33be66e5c 100644 --- a/worldguard-core/build.gradle.kts +++ b/worldguard-core/build.gradle.kts @@ -8,10 +8,10 @@ dependencies { "api"(project(":worldguard-libs:core")) "api"("com.sk89q.worldedit:worldedit-core:${Versions.WORLDEDIT}") "implementation"("org.flywaydb:flyway-core:3.0") - "implementation"("org.yaml:snakeyaml:1.29") + "implementation"("org.yaml:snakeyaml:1.30") "implementation"("com.google.guava:guava:${Versions.GUAVA}") - "compileOnly"("com.google.code.findbugs:jsr305:1.3.9") + "compileOnly"("com.google.code.findbugs:jsr305:${Versions.FINDBUGS}") "testImplementation"("org.hamcrest:hamcrest-library:1.2.1") } diff --git a/worldguard-libs/bukkit/build.gradle.kts b/worldguard-libs/bukkit/build.gradle.kts index 388618cea..7eb9124f2 100644 --- a/worldguard-libs/bukkit/build.gradle.kts +++ b/worldguard-libs/bukkit/build.gradle.kts @@ -1 +1,2 @@ applyLibrariesConfiguration() +constrainDependenciesToLibsCore() \ No newline at end of file From bc63119373d4603e5b040460c41e712275a4d062 Mon Sep 17 00:00:00 2001 From: wizjany Date: Sat, 12 Mar 2022 17:32:40 -0500 Subject: [PATCH 2/6] Fix publish. --- buildSrc/src/main/kotlin/PlatformConfig.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/buildSrc/src/main/kotlin/PlatformConfig.kt b/buildSrc/src/main/kotlin/PlatformConfig.kt index 48fe104b0..9b49b6f57 100644 --- a/buildSrc/src/main/kotlin/PlatformConfig.kt +++ b/buildSrc/src/main/kotlin/PlatformConfig.kt @@ -25,6 +25,7 @@ fun Project.applyPlatformAndCoreConfiguration(javaRelease: Int = 17) { configure { publications { register("maven") { + from(components["java"]) versionMapping { usage("java-api") { fromResolutionOf("runtimeClasspath") From 8d5953a55009d500551771641db17eb0b6bb5abe Mon Sep 17 00:00:00 2001 From: JOO200 Date: Wed, 29 Dec 2021 21:20:54 +0100 Subject: [PATCH 3/6] apidomains: Added custom domains to WorldGuard This change allows third party plugins to dynamically add custom domains to WorldGuard. --- .../worldguard/bukkit/WorldGuardPlugin.java | 2 + .../java/com/sk89q/worldguard/WorldGuard.java | 13 ++ .../commands/CommandInputContext.java | 112 ++++++++++++ .../commands/region/MemberCommands.java | 22 ++- .../commands/region/RegionCommands.java | 4 +- .../commands/region/RegionCommandsBase.java | 7 +- .../worldguard/commands/task/RegionAdder.java | 18 +- .../worldguard/domains/CustomDomain.java | 108 +++++++++++ .../worldguard/domains/DefaultDomain.java | 145 ++++++++++++--- .../domains/registry/CustomDomainContext.java | 98 ++++++++++ .../registry/DomainConflictException.java | 26 +++ .../domains/registry/DomainFactory.java | 28 +++ .../domains/registry/DomainRegistry.java | 94 ++++++++++ .../InvalidDomainFormatException.java | 28 +++ .../registry/SimpleDomainRegistry.java | 167 ++++++++++++++++++ .../domains/registry/UnknownDomain.java | 88 +++++++++ .../permission/RegionPermissionModel.java | 3 +- .../protection/flags/FlagContext.java | 97 ++-------- .../managers/storage/file/YamlRegionFile.java | 16 ++ .../protection/util/DomainInputResolver.java | 53 +++++- .../util/WorldGuardExceptionConverter.java | 6 + .../worldguard/domains/CustomUUIDDomain.java | 64 +++++++ .../worldguard/domains/DefaultDomainTest.java | 6 + 23 files changed, 1077 insertions(+), 128 deletions(-) create mode 100644 worldguard-core/src/main/java/com/sk89q/worldguard/commands/CommandInputContext.java create mode 100644 worldguard-core/src/main/java/com/sk89q/worldguard/domains/CustomDomain.java create mode 100644 worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/CustomDomainContext.java create mode 100644 worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/DomainConflictException.java create mode 100644 worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/DomainFactory.java create mode 100644 worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/DomainRegistry.java create mode 100644 worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/InvalidDomainFormatException.java create mode 100644 worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/SimpleDomainRegistry.java create mode 100644 worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/UnknownDomain.java create mode 100644 worldguard-core/src/test/java/com/sk89q/worldguard/domains/CustomUUIDDomain.java diff --git a/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java b/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java index 86aefb550..3ba997921 100644 --- a/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java +++ b/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java @@ -65,6 +65,7 @@ import com.sk89q.worldguard.commands.GeneralCommands; import com.sk89q.worldguard.commands.ProtectionCommands; import com.sk89q.worldguard.commands.ToggleCommands; +import com.sk89q.worldguard.domains.registry.SimpleDomainRegistry; import com.sk89q.worldguard.protection.flags.Flag; import com.sk89q.worldguard.protection.flags.Flags; import com.sk89q.worldguard.protection.flags.registry.SimpleFlagRegistry; @@ -212,6 +213,7 @@ public void onEnable() { }); ((SimpleFlagRegistry) WorldGuard.getInstance().getFlagRegistry()).setInitialized(true); + ((SimpleDomainRegistry) WorldGuard.getInstance().getDomainRegistry()).setInitialized(true); // Enable metrics final Metrics metrics = new Metrics(this, BSTATS_PLUGIN_ID); // bStats plugin id diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/WorldGuard.java b/worldguard-core/src/main/java/com/sk89q/worldguard/WorldGuard.java index 637eee7bd..cc287c6c3 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/WorldGuard.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/WorldGuard.java @@ -24,6 +24,8 @@ import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; import com.sk89q.minecraft.util.commands.CommandException; +import com.sk89q.worldguard.domains.registry.DomainRegistry; +import com.sk89q.worldguard.domains.registry.SimpleDomainRegistry; import com.sk89q.worldguard.util.profile.cache.HashMapCache; import com.sk89q.worldguard.util.profile.cache.ProfileCache; import com.sk89q.worldguard.util.profile.cache.SQLiteCache; @@ -55,6 +57,7 @@ public final class WorldGuard { private WorldGuardPlatform platform; private final SimpleFlagRegistry flagRegistry = new SimpleFlagRegistry(); + private final SimpleDomainRegistry domainRegistry = new SimpleDomainRegistry(); private final Supervisor supervisor = new SimpleSupervisor(); private ProfileCache profileCache; private ProfileService profileService; @@ -116,6 +119,16 @@ public FlagRegistry getFlagRegistry() { return this.flagRegistry; } + + /** + * Get the domain registry. + * + * @return the domain registry + */ + public DomainRegistry getDomainRegistry() { + return this.domainRegistry; + } + /** * Get the supervisor. * diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/commands/CommandInputContext.java b/worldguard-core/src/main/java/com/sk89q/worldguard/commands/CommandInputContext.java new file mode 100644 index 000000000..7c6de508b --- /dev/null +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/commands/CommandInputContext.java @@ -0,0 +1,112 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.commands; + +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldguard.LocalPlayer; +import com.sk89q.worldguard.protection.flags.InvalidFlagFormat; + +import javax.annotation.Nullable; +import java.util.Map; + +public abstract class CommandInputContext { + protected final Actor sender; + protected final String input; + + protected Map context; + + protected CommandInputContext(Actor sender, String input, Map values) { + this.sender = sender; + this.input = input; + this.context = values; + } + + public void put(String name, Object value) { + context.put(name, value); + } + + public Actor getSender() { + return sender; + } + + public String getUserInput() { + return input; + } + + /** + * Gets the CommandSender as a player. + * + * @return Player + * @throws T if the sender is not a player + */ + public LocalPlayer getPlayerSender() throws T { + if (sender.isPlayer() && sender instanceof LocalPlayer) { + return (LocalPlayer) sender; + } else { + throw createException("Not a player"); + } + } + + public Integer getUserInputAsInt() throws T { + try { + return Integer.parseInt(input); + } catch (NumberFormatException e) { + throw createException("Not a number: " + input); + } + } + + public Double getUserInputAsDouble() throws T { + try { + return Double.parseDouble(input); + } catch (NumberFormatException e) { + throw createException("Not a number: " + input); + } + } + + protected abstract T createException(String str); + + /** + * Get an object from the context by key name. + * May return null if the object does not exist in the context. + * + * @param name key name of the object + * @return the object matching the key, or null + */ + @Nullable + public Object get(String name) { + return get(name, null); + } + + /** + * Get an object from the context by key name. + * Will only return null if + * a) you provide null as the default + * b) the key has explicity been set to null + * + * @param name key name of the object + * @return the object matching the key + */ + @Nullable + public Object get(String name, Object defaultValue) { + Object obj; + return (((obj = context.get(name)) != null) || context.containsKey(name) + ? obj : defaultValue); + } +} diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/commands/region/MemberCommands.java b/worldguard-core/src/main/java/com/sk89q/worldguard/commands/region/MemberCommands.java index e41eb1b93..45bb9f1b3 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/commands/region/MemberCommands.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/commands/region/MemberCommands.java @@ -25,16 +25,27 @@ import com.sk89q.minecraft.util.commands.CommandPermissionsException; import com.sk89q.worldedit.command.util.AsyncCommandBuilder; import com.sk89q.worldedit.extension.platform.Actor; -import com.sk89q.worldedit.util.auth.AuthorizationException; +import com.sk89q.worldedit.util.formatting.component.ErrorFormat; +import com.sk89q.worldedit.util.formatting.text.Component; +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.util.formatting.text.event.ClickEvent; +import com.sk89q.worldedit.util.formatting.text.event.HoverEvent; +import com.sk89q.worldedit.util.formatting.text.format.TextColor; import com.sk89q.worldedit.world.World; import com.sk89q.worldguard.LocalPlayer; import com.sk89q.worldguard.WorldGuard; import com.sk89q.worldguard.domains.DefaultDomain; +import com.sk89q.worldguard.domains.registry.DomainFactory; +import com.sk89q.worldguard.domains.registry.DomainRegistry; +import com.sk89q.worldguard.internal.permission.RegionPermissionModel; import com.sk89q.worldguard.protection.managers.RegionManager; import com.sk89q.worldguard.protection.regions.ProtectedRegion; import com.sk89q.worldguard.protection.util.DomainInputResolver; import com.sk89q.worldguard.protection.util.DomainInputResolver.UserLocatorPolicy; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Map; import java.util.concurrent.Callable; public class MemberCommands extends RegionCommandsBase { @@ -67,6 +78,8 @@ public void addMember(CommandContext args, Actor sender) throws CommandException DomainInputResolver resolver = new DomainInputResolver( WorldGuard.getInstance().getProfileService(), args.getParsedPaddedSlice(1, 0)); resolver.setLocatorPolicy(args.hasFlag('n') ? UserLocatorPolicy.NAME_ONLY : UserLocatorPolicy.UUID_ONLY); + resolver.setActor(sender); + resolver.setRegion(region); final String description = String.format("Adding members to the region '%s' on '%s'", region.getId(), world.getName()); @@ -101,7 +114,8 @@ public void addOwner(CommandContext args, Actor sender) throws CommandException DomainInputResolver resolver = new DomainInputResolver( WorldGuard.getInstance().getProfileService(), args.getParsedPaddedSlice(1, 0)); resolver.setLocatorPolicy(args.hasFlag('n') ? UserLocatorPolicy.NAME_ONLY : UserLocatorPolicy.UUID_ONLY); - + resolver.setActor(sender); + resolver.setRegion(region); final String description = String.format("Adding owners to the region '%s' on '%s'", region.getId(), world.getName()); AsyncCommandBuilder.wrap(checkedAddOwners(sender, manager, region, world, resolver), sender) @@ -174,6 +188,8 @@ public void removeMember(CommandContext args, Actor sender) throws CommandExcept DomainInputResolver resolver = new DomainInputResolver( WorldGuard.getInstance().getProfileService(), args.getParsedPaddedSlice(1, 0)); resolver.setLocatorPolicy(args.hasFlag('n') ? UserLocatorPolicy.NAME_ONLY : UserLocatorPolicy.UUID_AND_NAME); + resolver.setActor(sender); + resolver.setRegion(region); callable = resolver; } @@ -217,6 +233,8 @@ public void removeOwner(CommandContext args, Actor sender) throws CommandExcepti DomainInputResolver resolver = new DomainInputResolver( WorldGuard.getInstance().getProfileService(), args.getParsedPaddedSlice(1, 0)); resolver.setLocatorPolicy(args.hasFlag('n') ? UserLocatorPolicy.NAME_ONLY : UserLocatorPolicy.UUID_AND_NAME); + resolver.setActor(sender); + resolver.setRegion(region); callable = resolver; } diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/commands/region/RegionCommands.java b/worldguard-core/src/main/java/com/sk89q/worldguard/commands/region/RegionCommands.java index 9299ad02b..104184955 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/commands/region/RegionCommands.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/commands/region/RegionCommands.java @@ -160,7 +160,7 @@ public void define(CommandContext args, Actor sender) throws CommandException { region = checkRegionFromSelection(sender, id); } - RegionAdder task = new RegionAdder(manager, region); + RegionAdder task = new RegionAdder(manager, region, sender); task.addOwnersFromCommand(args, 2); final String description = String.format("Adding region '%s'", region.getId()); @@ -214,7 +214,7 @@ public void redefine(CommandContext args, Actor sender) throws CommandException region.copyFrom(existing); - RegionAdder task = new RegionAdder(manager, region); + RegionAdder task = new RegionAdder(manager, region, sender); final String description = String.format("Updating region '%s'", region.getId()); AsyncCommandBuilder.wrap(task, sender) diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/commands/region/RegionCommandsBase.java b/worldguard-core/src/main/java/com/sk89q/worldguard/commands/region/RegionCommandsBase.java index 7d2ca5a82..163048965 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/commands/region/RegionCommandsBase.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/commands/region/RegionCommandsBase.java @@ -32,18 +32,19 @@ import com.sk89q.worldedit.regions.Polygonal2DRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.RegionSelector; -import com.sk89q.worldedit.regions.selector.CuboidRegionSelector; -import com.sk89q.worldedit.regions.selector.Polygonal2DRegionSelector; import com.sk89q.worldedit.util.formatting.component.ErrorFormat; import com.sk89q.worldedit.util.formatting.component.SubtleFormat; import com.sk89q.worldedit.util.formatting.text.TextComponent; import com.sk89q.worldedit.util.formatting.text.event.ClickEvent; import com.sk89q.worldedit.util.formatting.text.event.HoverEvent; import com.sk89q.worldedit.util.formatting.text.format.TextColor; -import com.sk89q.worldedit.util.formatting.text.format.TextDecoration; import com.sk89q.worldedit.world.World; import com.sk89q.worldguard.LocalPlayer; import com.sk89q.worldguard.WorldGuard; +import com.sk89q.worldguard.domains.CustomDomain; +import com.sk89q.worldguard.domains.DefaultDomain; +import com.sk89q.worldguard.domains.registry.CustomDomainContext; +import com.sk89q.worldguard.domains.registry.InvalidDomainFormat; import com.sk89q.worldguard.internal.permission.RegionPermissionModel; import com.sk89q.worldguard.protection.ApplicableRegionSet; import com.sk89q.worldguard.protection.flags.Flag; diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/commands/task/RegionAdder.java b/worldguard-core/src/main/java/com/sk89q/worldguard/commands/task/RegionAdder.java index 932b85827..dc7a957da 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/commands/task/RegionAdder.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/commands/task/RegionAdder.java @@ -20,6 +20,7 @@ package com.sk89q.worldguard.commands.task; import com.sk89q.minecraft.util.commands.CommandContext; +import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldguard.WorldGuard; import com.sk89q.worldguard.domains.DefaultDomain; import com.sk89q.worldguard.protection.managers.RegionManager; @@ -39,6 +40,7 @@ public class RegionAdder implements Callable { private final RegionManager manager; private final ProtectedRegion region; + private final Actor actor; @Nullable private String[] ownersInput; private UserLocatorPolicy locatorPolicy = UserLocatorPolicy.UUID_ONLY; @@ -46,15 +48,26 @@ public class RegionAdder implements Callable { /** * Create a new instance. * - * @param manager the manage + * @param manager the manager * @param region the region */ public RegionAdder(RegionManager manager, ProtectedRegion region) { + this(manager, region, null); + } + + /** + * Create a new instance. + * @param manager the manager + * @param region the region + * @param actor the actor + */ + public RegionAdder(RegionManager manager, ProtectedRegion region, Actor actor) { checkNotNull(manager); checkNotNull(region); this.manager = manager; this.region = region; + this.actor = actor; } /** @@ -75,6 +88,9 @@ public ProtectedRegion call() throws Exception { if (ownersInput != null) { DomainInputResolver resolver = new DomainInputResolver(WorldGuard.getInstance().getProfileService(), ownersInput); resolver.setLocatorPolicy(locatorPolicy); + resolver.setActor(actor); + resolver.setRegion(region); + DefaultDomain domain = resolver.call(); region.getOwners().addAll(domain); } diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/domains/CustomDomain.java b/worldguard-core/src/main/java/com/sk89q/worldguard/domains/CustomDomain.java new file mode 100644 index 000000000..1a410bee9 --- /dev/null +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/domains/CustomDomain.java @@ -0,0 +1,108 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.domains; + +import com.sk89q.worldguard.LocalPlayer; +import com.sk89q.worldguard.domains.registry.CustomDomainContext; +import com.sk89q.worldguard.domains.registry.InvalidDomainFormatException; +import com.sk89q.worldguard.util.ChangeTracked; + +import java.util.regex.Pattern; + +import static com.google.common.base.Preconditions.checkNotNull; + +public abstract class CustomDomain implements Domain, ChangeTracked { + private static final Pattern VALID_NAME = Pattern.compile("^[a-z0-9\\-]{1,40}$"); + + private final String name; + private boolean dirty; + + public CustomDomain(String name) { + if (name == null ||!isValidName(name)) { + throw new IllegalArgumentException("Invalid Domain name used."); + } + this.name = name; + } + + /** + * Get the name of the domain resolver. + * + * @return The name of the domain + */ + public String getName() { + return name; + } + + /** + * Parse a given input to fill the context of the CustomDomain. + * + * @param context the {@link CustomDomainContext} + * @throws InvalidDomainFormatException Raised if the input is invalid + */ + public abstract void parseInput(CustomDomainContext context) throws InvalidDomainFormatException; + + /** + * Convert a raw type that was loaded (from a YAML file, for example) + * into the custom domain. + * + * @param o The object + */ + public abstract void unmarshal(Object o); + + /** + * Convert the current Domain to a storable foramt + * + * @return The marshalled type + */ + public abstract Object marshal(); + + /** + * Test whether a flag name is valid. + * + * @param name The flag name + * @return Whether the name is valid + */ + public static boolean isValidName(String name) { + checkNotNull(name, "name"); + // g is already reserved by the group domain + return VALID_NAME.matcher(name).matches() && !name.equalsIgnoreCase("g"); + } + + + @Override + public boolean contains(LocalPlayer player) { + return contains(player.getUniqueId()); + } + + @Override + public int size() { + return 1; + } + + @Override + public boolean isDirty() { + return dirty; + } + + @Override + public void setDirty(boolean dirty) { + this.dirty = dirty; + } +} diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/domains/DefaultDomain.java b/worldguard-core/src/main/java/com/sk89q/worldguard/domains/DefaultDomain.java index 3030a9c10..975d1a586 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/domains/DefaultDomain.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/domains/DefaultDomain.java @@ -22,8 +22,6 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; -import com.sk89q.worldguard.util.profile.Profile; -import com.sk89q.worldguard.util.profile.cache.ProfileCache; import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.util.formatting.text.TextComponent; import com.sk89q.worldedit.util.formatting.text.event.ClickEvent; @@ -31,9 +29,14 @@ import com.sk89q.worldedit.util.formatting.text.format.TextColor; import com.sk89q.worldguard.LocalPlayer; import com.sk89q.worldguard.util.ChangeTracked; +import com.sk89q.worldguard.util.profile.Profile; +import com.sk89q.worldguard.util.profile.cache.ProfileCache; import javax.annotation.Nullable; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -50,6 +53,9 @@ public class DefaultDomain implements Domain, ChangeTracked { private PlayerDomain playerDomain = new PlayerDomain(); private GroupDomain groupDomain = new GroupDomain(); + private Set customDomains = new HashSet<>(); + private boolean customDomainsChanged = false; + /** * Create a new domain. */ @@ -64,6 +70,7 @@ public DefaultDomain() { public DefaultDomain(DefaultDomain existing) { setPlayerDomain(existing.getPlayerDomain()); setGroupDomain(existing.getGroupDomain()); + setCustomDomains(existing.getCustomDomains()); } /** @@ -104,6 +111,62 @@ public void setGroupDomain(GroupDomain groupDomain) { this.groupDomain = new GroupDomain(groupDomain); } + /** + * Add new custom domains + * + * @param customDomain a domain + */ + public void addCustomDomain(CustomDomain customDomain) { + checkNotNull(customDomain); + removeCustomDomain(customDomain.getName()); + this.customDomains.add(customDomain); + customDomainsChanged = true; + } + + /** + * Remove a custom domain matched by the name + * + * @param name the name + */ + public void removeCustomDomain(String name) { + checkNotNull(name); + if (this.customDomains.removeIf(d -> d.getName().equalsIgnoreCase(name))) { + customDomainsChanged = true; + } + } + + /** + * Remove a custom domain + * + * @param customDomain a domain + */ + public void removeCustomDomain(CustomDomain customDomain) { + checkNotNull(customDomain); + if (this.customDomains.remove(customDomain)) { + customDomainsChanged = true; + } + } + + /** + * Set the api domains to a specified value + * + * @param customDomains the domains + */ + public void setCustomDomains(Collection customDomains) { + checkNotNull(customDomains); + this.customDomains = new HashSet<>(customDomains); + customDomainsChanged = true; + } + + /** + * Get all api domains + * + * @return a unmodifiable copy of the domains + */ + public Set getCustomDomains() { + return Collections.unmodifiableSet(this.customDomains); + } + /** * Add the given player to the domain, identified by the player's name. * @@ -175,6 +238,9 @@ public void addAll(DefaultDomain other) { for (String group : other.getGroups()) { addGroup(group); } + for (CustomDomain domain : other.getCustomDomains()) { + addCustomDomain(domain); + } } /** @@ -193,6 +259,9 @@ public void removeAll(DefaultDomain other) { for (String group : other.getGroups()) { removeGroup(group); } + for (CustomDomain domain : other.getCustomDomains()) { + removeCustomDomain(domain.getName()); + } } /** @@ -242,12 +311,12 @@ public Set getGroups() { @Override public boolean contains(LocalPlayer player) { - return playerDomain.contains(player) || groupDomain.contains(player); + return playerDomain.contains(player) || groupDomain.contains(player) || customDomains.stream().anyMatch(d -> d.contains(player)); } @Override public boolean contains(UUID uniqueId) { - return playerDomain.contains(uniqueId); + return playerDomain.contains(uniqueId) || customDomains.stream().anyMatch(d -> d.contains(uniqueId)); } @Override @@ -257,7 +326,7 @@ public boolean contains(String playerName) { @Override public int size() { - return groupDomain.size() + playerDomain.size(); + return groupDomain.size() + playerDomain.size() + customDomains.size(); } @Override @@ -275,7 +344,6 @@ public String toPlayersString() { } public String toPlayersString(@Nullable ProfileCache cache) { - StringBuilder str = new StringBuilder(); List output = new ArrayList<>(); for (String name : playerDomain.getPlayers()) { @@ -299,13 +367,7 @@ public String toPlayersString(@Nullable ProfileCache cache) { } output.sort(String.CASE_INSENSITIVE_ORDER); - for (Iterator it = output.iterator(); it.hasNext();) { - str.append(it.next()); - if (it.hasNext()) { - str.append(", "); - } - } - return str.toString(); + return String.join(", ", output); } public String toGroupsString() { @@ -320,25 +382,20 @@ public String toGroupsString() { return str.toString(); } - public String toUserFriendlyString() { - StringBuilder str = new StringBuilder(); - - if (playerDomain.size() > 0) { - str.append(toPlayersString()); - } - - if (groupDomain.size() > 0) { - if (str.length() > 0) { - str.append("; "); - } - - str.append(toGroupsString()); + public String toCustomDomainsString() { + List output = new ArrayList<>(); + for (CustomDomain customDomain : customDomains) { + output.add(customDomain.getName() + ":" + customDomain.toString()); } + output.sort(String.CASE_INSENSITIVE_ORDER); + return String.join(", ", output); + } - return str.toString(); + public String toUserFriendlyString() { + return toUserFriendlyString(null); } - public String toUserFriendlyString(ProfileCache cache) { + public String toUserFriendlyString(@Nullable ProfileCache cache) { StringBuilder str = new StringBuilder(); if (playerDomain.size() > 0) { @@ -352,6 +409,12 @@ public String toUserFriendlyString(ProfileCache cache) { str.append(toGroupsString()); } + if (!customDomains.isEmpty()) { + if (str.length() > 0) { + str.append("; "); + } + str.append(toCustomDomainsString()); + } return str.toString(); } @@ -367,6 +430,12 @@ public Component toUserFriendlyComponent(@Nullable ProfileCache cache) { } builder.append(toGroupsComponent()); } + if (!customDomains.isEmpty()) { + if (playerDomain.size() > 0 || groupDomain.size() > 0) { + builder.append(TextComponent.of("; ")); + } + builder.append(toCustomDomainsComponent()); + } return builder.build(); } @@ -442,21 +511,39 @@ private Component toPlayersComponent(ProfileCache cache) { return builder.build(); } + private Component toCustomDomainsComponent() { + final TextComponent.Builder builder = TextComponent.builder(""); + for (Iterator it = customDomains.iterator(); it.hasNext(); ) { + CustomDomain domain = it.next(); + builder.append(TextComponent.of(domain.getName() + ":", TextColor.LIGHT_PURPLE)) + .append(TextComponent.of(domain.toString(), TextColor.GOLD)); + if (it.hasNext()) { + builder.append(TextComponent.of(", ")); + } + } + return builder.build().hoverEvent(HoverEvent.of(HoverEvent.Action.SHOW_TEXT, TextComponent.of("CustomDomain"))); + } + + @Override public boolean isDirty() { - return playerDomain.isDirty() || groupDomain.isDirty(); + return playerDomain.isDirty() || groupDomain.isDirty() || + customDomainsChanged || customDomains.stream().anyMatch(ChangeTracked::isDirty); } @Override public void setDirty(boolean dirty) { playerDomain.setDirty(dirty); groupDomain.setDirty(dirty); + customDomainsChanged = dirty; + customDomains.forEach(d -> d.setDirty(dirty)); } @Override public String toString() { return "{players=" + playerDomain + ", groups=" + groupDomain + + ", custom=" + customDomains + '}'; } diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/CustomDomainContext.java b/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/CustomDomainContext.java new file mode 100644 index 000000000..cdcfd4614 --- /dev/null +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/CustomDomainContext.java @@ -0,0 +1,98 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.domains.registry; + +import com.google.common.collect.Maps; +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldguard.commands.CommandInputContext; + +import javax.annotation.Nullable; +import java.util.Map; + +public final class CustomDomainContext extends CommandInputContext { + + private CustomDomainContext(Actor sender, String input, Map values) { + super(sender, input, values); + } + + + public static CustomDomainContext.CustomDomainContextBuilder create() { + return new CustomDomainContext.CustomDomainContextBuilder(); + } + + /** + * Create a copy of this CustomDomainContext, with optional substitutions for values + * + *

If any supplied variable is null, it will be ignored. + * If a map is supplied, it will override this CustomDomainContext's values of the same key, + * but unprovided keys will not be overriden and will be returned as shallow copies.

+ * + * @param commandSender CommandSender for the new CustomDomainContext to run under + * @param s String of the user input for the new CustomDomainContext + * @param values map of values to override from the current CustomDomainContext + * @return a copy of this CustomDomainContext + */ + public CustomDomainContext copyWith(@Nullable Actor commandSender, @Nullable String s, @Nullable Map values) { + Map map = Maps.newHashMap(); + map.putAll(context); + if (values != null) { + map.putAll(values); + } + return new CustomDomainContext(commandSender == null ? this.sender : commandSender, s == null ? this.input : s, map); + } + + @Override + protected InvalidDomainFormatException createException(String str) { + return new InvalidDomainFormatException(str); + } + + public static class CustomDomainContextBuilder { + private Actor sender; + private String input; + private Map map = Maps.newHashMap(); + + public CustomDomainContextBuilder setSender(Actor sender) { + this.sender = sender; + return this; + } + + public CustomDomainContextBuilder setInput(String input) { + this.input = input; + return this; + } + + public CustomDomainContextBuilder setObject(String key, Object value) { + this.map.put(key, value); + return this; + } + + public boolean tryAddToMap(String key, Object value) { + if (map.containsKey(key)) return false; + this.map.put(key, value); + return true; + } + + public CustomDomainContext build() { + return new CustomDomainContext(sender, input, map); + } + } + +} + diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/DomainConflictException.java b/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/DomainConflictException.java new file mode 100644 index 000000000..4ab7e933b --- /dev/null +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/DomainConflictException.java @@ -0,0 +1,26 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.domains.registry; + +public class DomainConflictException extends RuntimeException { + public DomainConflictException(String message) { + super(message); + } +} diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/DomainFactory.java b/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/DomainFactory.java new file mode 100644 index 000000000..5d901bd3e --- /dev/null +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/DomainFactory.java @@ -0,0 +1,28 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + + +package com.sk89q.worldguard.domains.registry; + +import com.sk89q.worldguard.domains.CustomDomain; + +@FunctionalInterface +public interface DomainFactory { + T create(String name); +} diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/DomainRegistry.java b/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/DomainRegistry.java new file mode 100644 index 000000000..5fd8ac46b --- /dev/null +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/DomainRegistry.java @@ -0,0 +1,94 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.domains.registry; + +import com.sk89q.worldguard.domains.CustomDomain; + +import javax.annotation.Nullable; +import java.util.List; +import java.util.Map; + +public interface DomainRegistry extends Iterable> { + + /** + * Register a new Domain + * + *

There may be an appropiate time to register domains. if domains are + * registered outside this time, then an exception may be thrown.

+ * + * @param domain The domain + * @throws DomainConflictException Thrown when already an existing domain exists with the same name + * @throws IllegalStateException If it is not the right time to register new domains + */ + void register(String name, DomainFactory domain) throws DomainConflictException; + + /** + * Register a collection of domains. + * + *

There may be an appropriate time to register domains. If domains are + * registered outside this time, then an exception may be thrown.

+ * + *

If there is a domain conflict, then an error will be logged but + * no exception will be thrown.

+ * + * @param domains a collection of domain factories + * @throws IllegalStateException If it is not the right time to register new domains + */ + void registerAll(Map> domains); + + /** + * Get the domain by its name. + * + * @param name The name + * @return The domain, if it has been registered + */ + @Nullable + DomainFactory get(String name); + + /** + * Try to get a domain by its name + */ + @Nullable + CustomDomain createDomain(String name); + + /** + * Get all domains keyed by the registered name + * + * @return All domains + */ + Map> getAll(); + + /** + * Unmarshal a raw map of values into a list of domains with their + * unmarshalled values. + * + * @param rawValues The raw values map + * @param createUnknown Whether "just in time" domains should be created for unknown domains + * @return The unmarshalled domain list + */ + List unmarshal(Map rawValues, boolean createUnknown); + + /** + * Get the number of registered domains. + * + * @return The number of registered domains + */ + int size(); +} diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/InvalidDomainFormatException.java b/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/InvalidDomainFormatException.java new file mode 100644 index 000000000..1d6397eff --- /dev/null +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/InvalidDomainFormatException.java @@ -0,0 +1,28 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.domains.registry; + +public class InvalidDomainFormatException extends Exception { + private static final long serialVersionUID = 8101615074524004172L; + + public InvalidDomainFormatException(String msg) { + super(msg); + } +} diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/SimpleDomainRegistry.java b/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/SimpleDomainRegistry.java new file mode 100644 index 000000000..2d4330a2d --- /dev/null +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/SimpleDomainRegistry.java @@ -0,0 +1,167 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.domains.registry; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterators; +import com.google.common.collect.Maps; +import com.sk89q.worldguard.domains.CustomDomain; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static com.google.common.base.Preconditions.checkNotNull; + +public class SimpleDomainRegistry implements DomainRegistry { + private static final Logger log = Logger.getLogger(SimpleDomainRegistry.class.getCanonicalName()); + + private final Object lock = new Object(); + private final ConcurrentMap> domains = Maps.newConcurrentMap(); + private boolean initialized = false; + + public boolean isInitialized() { + return initialized; + } + + public void setInitialized(boolean initialized) { + this.initialized = initialized; + } + + @Override + public void register(String name, DomainFactory domain) throws DomainConflictException { + synchronized (lock) { + if (initialized) { + throw new IllegalStateException("New domains cannot be registered at this time"); + } + + forceRegister(name, domain); + } + } + + @Override + public void registerAll(Map> domains) { + synchronized (lock) { + for (Map.Entry> entry : domains.entrySet()) { + try { + register(entry.getKey(), entry.getValue()); + } catch (DomainConflictException e) { + log.log(Level.WARNING, e.getMessage()); + } + } + } + } + + private > T forceRegister(String name, T domain) throws DomainConflictException { + checkNotNull(domain, "domain"); + checkNotNull(name, "name"); + + if (!CustomDomain.isValidName(name)) { + throw new IllegalArgumentException("Invalid Domain name used."); + } + + synchronized (lock) { + if (domains.containsKey(name)) { + throw new DomainConflictException("A domain already exists by the name " + name); + } + + domains.put(name, domain); + } + + return domain; + } + + @Nullable + @Override + public DomainFactory get(String name) { + checkNotNull(name, "name"); + return domains.get(name.toLowerCase()); + } + + @Nullable + @Override + public CustomDomain createDomain(String name) { + DomainFactory factory = get(name); + if (factory == null) return null; + return factory.create(name); + } + + @Override + public Map> getAll() { + return ImmutableMap.copyOf(domains); + } + + private CustomDomain getOrCreate(String name, Object value, boolean createUnknown) { + CustomDomain customDomain = createDomain(name); + + if (customDomain != null) { + customDomain.unmarshal(value); + return customDomain; + } + + synchronized (lock) { + customDomain = createDomain(name); // Load again because the previous load was not synchronized + if (customDomain != null) { + customDomain.unmarshal(value); + return customDomain; + } + if (createUnknown) { + DomainFactory unknownFactory = forceRegister(name, UnknownDomain.FACTORY); + if (unknownFactory != null) { + customDomain = unknownFactory.create(name); + if (customDomain != null) customDomain.unmarshal(value); + return customDomain; + } + } + } + return null; + } + + public List unmarshal(Map rawValues, boolean createUnknown) { + checkNotNull(rawValues, "rawValues"); + + List domainList = new ArrayList<>(); + + for (Map.Entry entry : rawValues.entrySet()) { + try { + CustomDomain domain = getOrCreate(entry.getKey(), entry.getValue(), createUnknown); + domainList.add(domain); + } catch (Throwable e) { + log.log(Level.WARNING, "Failed to unmarshal domain for " + entry.getKey(), e); + } + } + return domainList; + } + + @Override + public int size() { + return domains.size(); + } + + @Override + public Iterator> iterator() { + return Iterators.unmodifiableIterator(domains.values().iterator()); + } +} diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/UnknownDomain.java b/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/UnknownDomain.java new file mode 100644 index 000000000..2ff9d1ace --- /dev/null +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/UnknownDomain.java @@ -0,0 +1,88 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.domains.registry; + +import com.sk89q.worldguard.domains.CustomDomain; + +import java.util.UUID; + +public class UnknownDomain extends CustomDomain { + public static DomainFactory FACTORY = UnknownDomain::new; + + private boolean isDirty = false; + private Object o; + + public UnknownDomain(String name) { + super(name); + } + + @Override + public void parseInput(CustomDomainContext context) throws InvalidDomainFormatException { + throw new InvalidDomainFormatException("The plugin that registered this domain is not currently installed"); + } + + @Override + public void unmarshal(Object o) { + this.o = o; + } + + @Override + public Object marshal() { + return o; + } + + @Override + public boolean contains(UUID uniqueId) { + return false; + } + + @Override + public boolean contains(String playerName) { + return false; + } + + @Override + public int size() { + return 0; + } + + @Override + public void clear() { + isDirty = true; + o = null; + } + + @Override + public void setDirty(boolean dirty) { + isDirty = dirty; + } + + @Override + public boolean isDirty() { + return isDirty; + } + + @Override + public String toString() { + return "UnknownDomain{" + + "o=" + o + + '}'; + } +} diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/internal/permission/RegionPermissionModel.java b/worldguard-core/src/main/java/com/sk89q/worldguard/internal/permission/RegionPermissionModel.java index 287ed972d..c3da47fba 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/internal/permission/RegionPermissionModel.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/internal/permission/RegionPermissionModel.java @@ -176,13 +176,14 @@ public boolean mayRemoveMembers(ProtectedRegion region) { public boolean mayRemoveOwners(ProtectedRegion region) { return hasPatternPermission("removeowner", region); } - + /** * Checks to see if the given sender has permission to modify the given region * using the region permission pattern. * * @param perm the name of the node * @param region the region + * @return whether the actor has the permission */ private boolean hasPatternPermission(String perm, ProtectedRegion region) { if (!(getSender() instanceof Player)) { diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/FlagContext.java b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/FlagContext.java index a394a18fb..3283de3a5 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/FlagContext.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/FlagContext.java @@ -21,106 +21,28 @@ import com.google.common.collect.Maps; import com.sk89q.worldedit.extension.platform.Actor; -import com.sk89q.worldguard.LocalPlayer; import com.sk89q.worldguard.WorldGuard; - -import java.util.Map; +import com.sk89q.worldguard.commands.CommandInputContext; import javax.annotation.Nullable; +import java.util.Map; -public final class FlagContext { - - private final Actor sender; - private final String input; - - private Map context; +public final class FlagContext extends CommandInputContext { private FlagContext(Actor sender, String input, Map values) { - this.sender = sender; - this.input = input; - this.context = values; + super(sender, input, values); } - public static FlagContextBuilder create() { + public static FlagContext.FlagContextBuilder create() { return new FlagContextBuilder(); } - public void put(String name, Object value) { - context.put(name, value); - } - - public Actor getSender() { - return sender; - } - - public String getUserInput() { - return input; - } - - /** - * Gets the CommandSender as a player. - * - * @return Player - * @throws InvalidFlagFormat if the sender is not a player - */ - public LocalPlayer getPlayerSender() throws InvalidFlagFormat { - if (sender.isPlayer() && sender instanceof LocalPlayer) { - return (LocalPlayer) sender; - } else { - throw new InvalidFlagFormat("Not a player"); - } - } - - public Integer getUserInputAsInt() throws InvalidFlagFormat { - try { - return Integer.parseInt(input); - } catch (NumberFormatException e) { - throw new InvalidFlagFormat("Not a number: " + input); - } - } - - public Double getUserInputAsDouble() throws InvalidFlagFormat { - try { - return Double.parseDouble(input); - } catch (NumberFormatException e) { - throw new InvalidFlagFormat("Not a number: " + input); - } - } - - /** - * Get an object from the context by key name. - * May return null if the object does not exist in the context. - * - * @param name key name of the object - * @return the object matching the key, or null - */ - @Nullable - public Object get(String name) { - return get(name, null); - } - - /** - * Get an object from the context by key name. - * Will only return null if - * a) you provide null as the default - * b) the key has explicity been set to null - * - * @param name key name of the object - * @return the object matching the key - */ - @Nullable - public Object get(String name, Object defaultValue) { - Object obj; - return (((obj = context.get(name)) != null) || context.containsKey(name) - ? obj : defaultValue); - } - /** * Create a copy of this FlagContext, with optional substitutions for values * - * If any supplied variable is null, it will be ignored. + *

If any supplied variable is null, it will be ignored. * If a map is supplied, it will override this FlagContext's values of the same key, - * but unprovided keys will not be overriden and will be returned as shallow copies. + * but unprovided keys will not be overriden and will be returned as shallow copies.

* * @param commandSender CommandSender for the new FlagContext to run under * @param s String of the user input for the new FlagContext @@ -136,6 +58,11 @@ public FlagContext copyWith(@Nullable Actor commandSender, @Nullable String s, @ return new FlagContext(commandSender == null ? this.sender : commandSender, s == null ? this.input : s, map); } + @Override + protected InvalidFlagFormat createException(String str) { + return new InvalidFlagFormat(str); + } + public static class FlagContextBuilder { private Actor sender; private String input; diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/managers/storage/file/YamlRegionFile.java b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/managers/storage/file/YamlRegionFile.java index 9aebc5ac7..531b499f6 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/managers/storage/file/YamlRegionFile.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/managers/storage/file/YamlRegionFile.java @@ -27,6 +27,8 @@ import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; +import com.sk89q.worldguard.WorldGuard; +import com.sk89q.worldguard.domains.CustomDomain; import com.sk89q.worldguard.domains.DefaultDomain; import com.sk89q.worldguard.protection.flags.FlagUtil; import com.sk89q.worldguard.protection.flags.registry.FlagRegistry; @@ -284,6 +286,12 @@ private DefaultDomain parseDomain(YAMLNode node) { } } + YAMLNode apiDomains = node.getNode("custom"); + if (apiDomains != null) { + List parsedDomains = WorldGuard.getInstance().getDomainRegistry().unmarshal(apiDomains.getMap(), true); + domain.setCustomDomains(parsedDomains); + } + return domain; } @@ -304,6 +312,14 @@ private Map getDomainData(DefaultDomain domain) { setDomainData(domainData, "unique-ids", domain.getUniqueIds()); setDomainData(domainData, "groups", domain.getGroups()); + if (!domain.getCustomDomains().isEmpty()) { + Map values = new HashMap<>(); + for (CustomDomain customDomain : domain.getCustomDomains()) { + values.put(customDomain.getName(), customDomain.marshal()); + } + domainData.put("custom", values); + } + return domainData; } diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/util/DomainInputResolver.java b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/util/DomainInputResolver.java index 4c8a00ff8..3a5fad75e 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/util/DomainInputResolver.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/util/DomainInputResolver.java @@ -21,6 +21,12 @@ import com.google.common.base.Function; import com.google.common.base.Joiner; +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldguard.WorldGuard; +import com.sk89q.worldguard.domains.CustomDomain; +import com.sk89q.worldguard.domains.registry.CustomDomainContext; +import com.sk89q.worldguard.domains.registry.InvalidDomainFormatException; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; import com.sk89q.worldguard.util.profile.Profile; import com.sk89q.worldguard.util.profile.resolver.ProfileService; import com.sk89q.worldguard.util.profile.util.UUIDs; @@ -43,6 +49,7 @@ public class DomainInputResolver implements Callable { private static final Pattern GROUP_PATTERN = Pattern.compile("(?i)^[G]:(.+)$"); + private static final Pattern CUSTOM_PATTERN = Pattern.compile("(?i)^([A-Za-z0-9\\-]{1,40}):(.*)$"); /** * The policy for locating users. @@ -56,6 +63,8 @@ public enum UserLocatorPolicy { private final ProfileService profileService; private final String[] input; private UserLocatorPolicy locatorPolicy = UserLocatorPolicy.UUID_ONLY; + private ProtectedRegion region; + private Actor actor; /** * Create a new instance. @@ -89,20 +98,54 @@ public void setLocatorPolicy(UserLocatorPolicy locatorPolicy) { this.locatorPolicy = locatorPolicy; } + /** + * Set the region for the Resolver + * @param region the region + */ + public void setRegion(ProtectedRegion region) { + this.region = region; + } + + /** + * Get the current region from the Resolver + * @return the region + */ + public @Nullable ProtectedRegion getRegion() { + return region; + } + + /** + * Set the actor of the Resolver + * @param actor the actor + */ + public void setActor(Actor actor) { + this.actor = actor; + } + @Override - public DefaultDomain call() throws UnresolvedNamesException { + public DefaultDomain call() throws UnresolvedNamesException, InvalidDomainFormatException { DefaultDomain domain = new DefaultDomain(); List namesToQuery = new ArrayList<>(); for (String s : input) { - Matcher m = GROUP_PATTERN.matcher(s); - if (m.matches()) { - domain.addGroup(m.group(1)); + Matcher groupMatcher = GROUP_PATTERN.matcher(s); + Matcher customMatcher = CUSTOM_PATTERN.matcher(s); + if (groupMatcher.matches()) { + domain.addGroup(groupMatcher.group(1)); + } else if (customMatcher.matches()) { + String domainName = customMatcher.group(1); + CustomDomain customDomain = WorldGuard.getInstance().getDomainRegistry().createDomain(domainName); + if (customDomain == null) { + throw new InvalidDomainFormatException("No domain named '" + domainName + "' found."); + } + customDomain.parseInput(CustomDomainContext.create() + .setSender(actor).setInput(customMatcher.group(2)).setObject("region", region).build()); + domain.addCustomDomain(customDomain); } else { UUID uuid = parseUUID(s); if (uuid != null) { // Try to add any UUIDs given - domain.addPlayer(UUID.fromString(UUIDs.addDashes(s.replaceAll("^uuid:", "")))); + domain.addPlayer(uuid); } else { switch (locatorPolicy) { case NAME_ONLY: diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/util/WorldGuardExceptionConverter.java b/worldguard-core/src/main/java/com/sk89q/worldguard/util/WorldGuardExceptionConverter.java index 89492df2d..f508951cd 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/util/WorldGuardExceptionConverter.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/util/WorldGuardExceptionConverter.java @@ -26,6 +26,7 @@ import com.sk89q.worldedit.util.auth.AuthorizationException; import com.sk89q.worldedit.util.formatting.component.InvalidComponentException; import com.sk89q.worldguard.WorldGuard; +import com.sk89q.worldguard.domains.registry.InvalidDomainFormat; import com.sk89q.worldguard.protection.managers.storage.StorageException; import com.sk89q.worldguard.protection.util.UnresolvedNamesException; @@ -91,6 +92,11 @@ public void convert(UnresolvedNamesException e) throws CommandException { throw newCommandException(e.getMessage(), e); } + @ExceptionMatch + public void convert(InvalidDomainFormat e) throws CommandException { + throw newCommandException(e.getMessage(), e); + } + @ExceptionMatch public void convert(AuthorizationException e) throws CommandException { throw newCommandException("You don't have permission to do that.", e); diff --git a/worldguard-core/src/test/java/com/sk89q/worldguard/domains/CustomUUIDDomain.java b/worldguard-core/src/test/java/com/sk89q/worldguard/domains/CustomUUIDDomain.java new file mode 100644 index 000000000..a70b874d3 --- /dev/null +++ b/worldguard-core/src/test/java/com/sk89q/worldguard/domains/CustomUUIDDomain.java @@ -0,0 +1,64 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.domains; + +import com.sk89q.worldguard.domains.registry.CustomDomainContext; +import com.sk89q.worldguard.domains.registry.InvalidDomainFormatException; + +import java.util.Objects; +import java.util.UUID; + +public class CustomUUIDDomain extends CustomDomain { + private UUID test; + + public CustomUUIDDomain(String name, UUID test) { + super(name); + this.test = test; + } + + @Override + public void parseInput(CustomDomainContext context) throws InvalidDomainFormatException { + throw new InvalidDomainFormatException("not supported"); + } + + @Override + public void unmarshal(Object o) { + } + + @Override + public Object marshal() { + return null; + } + + @Override + public boolean contains(UUID uniqueId) { + return Objects.equals(test, uniqueId); + } + + @Override + public boolean contains(String playerName) { + return false; + } + + @Override + public void clear() { + test = null; + } +} diff --git a/worldguard-core/src/test/java/com/sk89q/worldguard/domains/DefaultDomainTest.java b/worldguard-core/src/test/java/com/sk89q/worldguard/domains/DefaultDomainTest.java index 86586657f..fc112219d 100644 --- a/worldguard-core/src/test/java/com/sk89q/worldguard/domains/DefaultDomainTest.java +++ b/worldguard-core/src/test/java/com/sk89q/worldguard/domains/DefaultDomainTest.java @@ -112,5 +112,11 @@ public void testContains() throws Exception { assertFalse(domain.contains(player1)); assertTrue(domain.contains(player2)); assertTrue(domain.contains(player3)); + + domain = new DefaultDomain(); + domain.addCustomDomain(new CustomUUIDDomain("test", player2.getUniqueId())); + assertTrue(domain.contains(player2)); + assertFalse(domain.contains(player2.getName())); + assertFalse(domain.contains(player3)); } } \ No newline at end of file From bb3fdcc8800b15e819eb52e1fe054ef48af10eeb Mon Sep 17 00:00:00 2001 From: Joo200 Date: Sun, 22 Jan 2023 12:06:05 +0100 Subject: [PATCH 4/6] Renamed InvalidFlagFormat to InvalidFlagFormatException --- .../commands/CommandInputContext.java | 1 - .../commands/region/RegionCommands.java | 6 ++-- .../commands/region/RegionCommandsBase.java | 10 ++----- .../protection/flags/BooleanFlag.java | 4 +-- .../protection/flags/CommandStringFlag.java | 2 +- .../protection/flags/DoubleFlag.java | 2 +- .../protection/flags/EntityTypeFlag.java | 4 +-- .../worldguard/protection/flags/EnumFlag.java | 4 +-- .../worldguard/protection/flags/Flag.java | 4 +-- .../protection/flags/FlagContext.java | 6 ++-- .../protection/flags/GameModeTypeFlag.java | 4 +-- .../protection/flags/IntegerFlag.java | 2 +- .../protection/flags/InvalidFlagFormat.java | 4 +++ .../flags/InvalidFlagFormatException.java | 28 +++++++++++++++++++ .../protection/flags/LocationFlag.java | 6 ++-- .../worldguard/protection/flags/MapFlag.java | 4 +-- .../protection/flags/RegistryFlag.java | 4 +-- .../worldguard/protection/flags/SetFlag.java | 2 +- .../protection/flags/StateFlag.java | 4 +-- .../protection/flags/StringFlag.java | 2 +- .../protection/flags/TimestampFlag.java | 6 ++-- .../worldguard/protection/flags/UUIDFlag.java | 4 +-- .../protection/flags/VectorFlag.java | 4 +-- .../protection/flags/WeatherTypeFlag.java | 4 +-- .../flags/registry/UnknownFlag.java | 6 ++-- .../util/WorldGuardExceptionConverter.java | 4 +-- 26 files changed, 79 insertions(+), 52 deletions(-) create mode 100644 worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/InvalidFlagFormatException.java diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/commands/CommandInputContext.java b/worldguard-core/src/main/java/com/sk89q/worldguard/commands/CommandInputContext.java index 7c6de508b..82fd27d7f 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/commands/CommandInputContext.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/commands/CommandInputContext.java @@ -21,7 +21,6 @@ import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldguard.LocalPlayer; -import com.sk89q.worldguard.protection.flags.InvalidFlagFormat; import javax.annotation.Nullable; import java.util.Map; diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/commands/region/RegionCommands.java b/worldguard-core/src/main/java/com/sk89q/worldguard/commands/region/RegionCommands.java index 104184955..da85a0016 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/commands/region/RegionCommands.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/commands/region/RegionCommands.java @@ -59,7 +59,7 @@ import com.sk89q.worldguard.protection.flags.Flag; import com.sk89q.worldguard.protection.flags.FlagContext; import com.sk89q.worldguard.protection.flags.Flags; -import com.sk89q.worldguard.protection.flags.InvalidFlagFormat; +import com.sk89q.worldguard.protection.flags.InvalidFlagFormatException; import com.sk89q.worldguard.protection.flags.RegionGroup; import com.sk89q.worldguard.protection.flags.RegionGroupFlag; import com.sk89q.worldguard.protection.flags.registry.FlagRegistry; @@ -587,7 +587,7 @@ public void flag(CommandContext args, Actor sender) throws CommandException { // the [value] part throws an error. try { groupValue = groupFlag.parseInput(FlagContext.create().setSender(sender).setInput(group).setObject("region", existing).build()); - } catch (InvalidFlagFormat e) { + } catch (InvalidFlagFormatException e) { throw new CommandException(e.getMessage()); } @@ -598,7 +598,7 @@ public void flag(CommandContext args, Actor sender) throws CommandException { // Set the flag if [value] was given even if [-g group] was given as well try { value = setFlag(existing, foundFlag, sender, value).toString(); - } catch (InvalidFlagFormat e) { + } catch (InvalidFlagFormatException e) { throw new CommandException(e.getMessage()); } diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/commands/region/RegionCommandsBase.java b/worldguard-core/src/main/java/com/sk89q/worldguard/commands/region/RegionCommandsBase.java index 163048965..40c386afa 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/commands/region/RegionCommandsBase.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/commands/region/RegionCommandsBase.java @@ -41,15 +41,11 @@ import com.sk89q.worldedit.world.World; import com.sk89q.worldguard.LocalPlayer; import com.sk89q.worldguard.WorldGuard; -import com.sk89q.worldguard.domains.CustomDomain; -import com.sk89q.worldguard.domains.DefaultDomain; -import com.sk89q.worldguard.domains.registry.CustomDomainContext; -import com.sk89q.worldguard.domains.registry.InvalidDomainFormat; import com.sk89q.worldguard.internal.permission.RegionPermissionModel; import com.sk89q.worldguard.protection.ApplicableRegionSet; import com.sk89q.worldguard.protection.flags.Flag; import com.sk89q.worldguard.protection.flags.FlagContext; -import com.sk89q.worldguard.protection.flags.InvalidFlagFormat; +import com.sk89q.worldguard.protection.flags.InvalidFlagFormatException; import com.sk89q.worldguard.protection.managers.RegionManager; import com.sk89q.worldguard.protection.regions.GlobalProtectedRegion; import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion; @@ -419,9 +415,9 @@ protected static void setPlayerSelection(Actor actor, ProtectedRegion region, Wo * @param flag the flag * @param sender the sender * @param value the value - * @throws InvalidFlagFormat thrown if the value is invalid + * @throws InvalidFlagFormatException thrown if the value is invalid */ - protected static V setFlag(ProtectedRegion region, Flag flag, Actor sender, String value) throws InvalidFlagFormat { + protected static V setFlag(ProtectedRegion region, Flag flag, Actor sender, String value) throws InvalidFlagFormatException { V val = flag.parseInput(FlagContext.create().setSender(sender).setInput(value).setObject("region", region).build()); region.setFlag(flag, val); return val; diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/BooleanFlag.java b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/BooleanFlag.java index 3d5145c96..dcb26e760 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/BooleanFlag.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/BooleanFlag.java @@ -33,7 +33,7 @@ public BooleanFlag(String name) { } @Override - public Boolean parseInput(FlagContext context) throws InvalidFlagFormat { + public Boolean parseInput(FlagContext context) throws InvalidFlagFormatException { String input = context.getUserInput(); if (input.equalsIgnoreCase("true") || input.equalsIgnoreCase("yes") @@ -45,7 +45,7 @@ public Boolean parseInput(FlagContext context) throws InvalidFlagFormat { || input.equalsIgnoreCase("0")) { return false; } else { - throw new InvalidFlagFormat("Not a yes/no value: " + input); + throw new InvalidFlagFormatException("Not a yes/no value: " + input); } } diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/CommandStringFlag.java b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/CommandStringFlag.java index dd3afc601..d5f0cf278 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/CommandStringFlag.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/CommandStringFlag.java @@ -33,7 +33,7 @@ public CommandStringFlag(String name) { } @Override - public String parseInput(FlagContext context) throws InvalidFlagFormat { + public String parseInput(FlagContext context) throws InvalidFlagFormatException { String input = context.getUserInput(); input = input.trim(); if (!input.startsWith("/")) { diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/DoubleFlag.java b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/DoubleFlag.java index 81b046971..2f7389caa 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/DoubleFlag.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/DoubleFlag.java @@ -33,7 +33,7 @@ public DoubleFlag(String name) { } @Override - public Double parseInput(FlagContext context) throws InvalidFlagFormat { + public Double parseInput(FlagContext context) throws InvalidFlagFormatException { return context.getUserInputAsDouble(); } diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/EntityTypeFlag.java b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/EntityTypeFlag.java index 67be8c8cc..dfd671c34 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/EntityTypeFlag.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/EntityTypeFlag.java @@ -40,12 +40,12 @@ protected EntityTypeFlag(String name) { } @Override - public EntityType parseInput(FlagContext context) throws InvalidFlagFormat { + public EntityType parseInput(FlagContext context) throws InvalidFlagFormatException { String input = context.getUserInput(); input = input.trim(); EntityType entityType = unmarshal(input); if (entityType == null) { - throw new InvalidFlagFormat("Unknown entity type: " + input); + throw new InvalidFlagFormatException("Unknown entity type: " + input); } return entityType; } diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/EnumFlag.java b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/EnumFlag.java index 7c7a4c54a..d553fd6b4 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/EnumFlag.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/EnumFlag.java @@ -74,12 +74,12 @@ public T detectValue(String input) { } @Override - public T parseInput(FlagContext context) throws InvalidFlagFormat { + public T parseInput(FlagContext context) throws InvalidFlagFormatException { String input = context.getUserInput(); try { return findValue(input); } catch (IllegalArgumentException e) { - throw new InvalidFlagFormat("Unknown value '" + input + "' in " + throw new InvalidFlagFormatException("Unknown value '" + input + "' in " + enumClass.getName()); } } diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/Flag.java b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/Flag.java index 1cd145b9b..bd044335a 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/Flag.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/Flag.java @@ -181,9 +181,9 @@ public final RegionGroupFlag getRegionGroupFlag() { * * @param context the {@link FlagContext} * @return The coerced type - * @throws InvalidFlagFormat Raised if the input is invalid + * @throws InvalidFlagFormatException Raised if the input is invalid */ - public abstract T parseInput(FlagContext context) throws InvalidFlagFormat; + public abstract T parseInput(FlagContext context) throws InvalidFlagFormatException; /** * Convert a raw type that was loaded (from a YAML file, for example) diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/FlagContext.java b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/FlagContext.java index 3283de3a5..43f5fd812 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/FlagContext.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/FlagContext.java @@ -27,7 +27,7 @@ import javax.annotation.Nullable; import java.util.Map; -public final class FlagContext extends CommandInputContext { +public final class FlagContext extends CommandInputContext { private FlagContext(Actor sender, String input, Map values) { super(sender, input, values); @@ -59,8 +59,8 @@ public FlagContext copyWith(@Nullable Actor commandSender, @Nullable String s, @ } @Override - protected InvalidFlagFormat createException(String str) { - return new InvalidFlagFormat(str); + protected InvalidFlagFormatException createException(String str) { + return new InvalidFlagFormatException(str); } public static class FlagContextBuilder { diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/GameModeTypeFlag.java b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/GameModeTypeFlag.java index 3b8db1e0c..a0ce3a076 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/GameModeTypeFlag.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/GameModeTypeFlag.java @@ -40,12 +40,12 @@ protected GameModeTypeFlag(String name) { } @Override - public GameMode parseInput(FlagContext context) throws InvalidFlagFormat { + public GameMode parseInput(FlagContext context) throws InvalidFlagFormatException { String input = context.getUserInput(); input = input.trim(); GameMode gamemode = unmarshal(input); if (gamemode == null) { - throw new InvalidFlagFormat("Unknown game mode: " + input); + throw new InvalidFlagFormatException("Unknown game mode: " + input); } return gamemode; } diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/IntegerFlag.java b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/IntegerFlag.java index a9bafe36a..78c4cabc4 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/IntegerFlag.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/IntegerFlag.java @@ -33,7 +33,7 @@ public IntegerFlag(String name) { } @Override - public Integer parseInput(FlagContext context) throws InvalidFlagFormat { + public Integer parseInput(FlagContext context) throws InvalidFlagFormatException { return context.getUserInputAsInt(); } diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/InvalidFlagFormat.java b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/InvalidFlagFormat.java index d5d2d46ae..87d1d6ee1 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/InvalidFlagFormat.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/InvalidFlagFormat.java @@ -19,6 +19,10 @@ package com.sk89q.worldguard.protection.flags; +/** + * @deprecated replaced by {@link InvalidFlagFormatException}. Will be removed in WorldGuard 8 + */ +@Deprecated(forRemoval = true) public class InvalidFlagFormat extends Exception { private static final long serialVersionUID = 8101615074524004172L; diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/InvalidFlagFormatException.java b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/InvalidFlagFormatException.java new file mode 100644 index 000000000..807b7452a --- /dev/null +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/InvalidFlagFormatException.java @@ -0,0 +1,28 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.flags; + +public class InvalidFlagFormatException extends InvalidFlagFormat { + private static final long serialVersionUID = 8101615074524004172L; + + public InvalidFlagFormatException(String msg) { + super(msg); + } +} diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/LocationFlag.java b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/LocationFlag.java index 4f6b6c398..03a3358ae 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/LocationFlag.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/LocationFlag.java @@ -41,7 +41,7 @@ public LocationFlag(String name) { } @Override - public Location parseInput(FlagContext context) throws InvalidFlagFormat { + public Location parseInput(FlagContext context) throws InvalidFlagFormatException { String input = context.getUserInput(); Player player = context.getPlayerSender(); @@ -76,7 +76,7 @@ public Location parseInput(FlagContext context) throws InvalidFlagFormat { player.printDebug("WARNING: Flag location is outside of region."); } else { // no permission - throw new InvalidFlagFormat("You can't set that flag outside of the region boundaries."); + throw new InvalidFlagFormatException("You can't set that flag outside of the region boundaries."); } } // clamp height to world limits @@ -86,7 +86,7 @@ public Location parseInput(FlagContext context) throws InvalidFlagFormat { } return loc; } - throw new InvalidFlagFormat("Expected 'here' or x,y,z."); + throw new InvalidFlagFormatException("Expected 'here' or x,y,z."); } @Override diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/MapFlag.java b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/MapFlag.java index 4472b95ef..5147d8a09 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/MapFlag.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/MapFlag.java @@ -70,7 +70,7 @@ public Flag getValueFlag() { } @Override - public Map parseInput(final FlagContext context) throws InvalidFlagFormat { + public Map parseInput(final FlagContext context) throws InvalidFlagFormatException { final String input = context.getUserInput(); if (input.isEmpty()) { @@ -83,7 +83,7 @@ public Map parseInput(final FlagContext context) throws InvalidFlagFormat final char split = str.indexOf('=') == -1 ? ':' : '='; final String[] keyVal = str.split(String.valueOf(split)); if (keyVal.length != 2) { - throw new InvalidFlagFormat("Input must be in a 'key:value,key1=value1' format. Either ':' or '=' can be used."); + throw new InvalidFlagFormatException("Input must be in a 'key:value,key1=value1' format. Either ':' or '=' can be used."); } final FlagContext key = context.copyWith(null, keyVal[0], null); diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/RegistryFlag.java b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/RegistryFlag.java index cb7bf057d..81af639f8 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/RegistryFlag.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/RegistryFlag.java @@ -44,10 +44,10 @@ public RegistryFlag(String name, @Nullable RegionGroup defaultGroup, Registry } @Override - public T parseInput(FlagContext context) throws InvalidFlagFormat { + public T parseInput(FlagContext context) throws InvalidFlagFormatException { final String key = context.getUserInput().trim().toLowerCase(Locale.ROOT); return Optional.ofNullable(registry.get(key)) - .orElseThrow(() -> new InvalidFlagFormat("Unknown " + registry.getName() + ": " + key)); + .orElseThrow(() -> new InvalidFlagFormatException("Unknown " + registry.getName() + ": " + key)); } public Registry getRegistry() { diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/SetFlag.java b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/SetFlag.java index e850e3955..31438b691 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/SetFlag.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/SetFlag.java @@ -58,7 +58,7 @@ public Flag getType() { } @Override - public Set parseInput(FlagContext context) throws InvalidFlagFormat { + public Set parseInput(FlagContext context) throws InvalidFlagFormatException { String input = context.getUserInput(); if (input.isEmpty()) { return Sets.newHashSet(); diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/StateFlag.java b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/StateFlag.java index 8e21589d4..72180664a 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/StateFlag.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/StateFlag.java @@ -78,7 +78,7 @@ public boolean preventsAllowOnGlobal() { } @Override - public State parseInput(FlagContext context) throws InvalidFlagFormat { + public State parseInput(FlagContext context) throws InvalidFlagFormatException { String input = context.getUserInput(); if (input.equalsIgnoreCase("allow")) { @@ -88,7 +88,7 @@ public State parseInput(FlagContext context) throws InvalidFlagFormat { } else if (input.equalsIgnoreCase("none")) { return null; } else { - throw new InvalidFlagFormat("Expected none/allow/deny but got '" + input + "'"); + throw new InvalidFlagFormatException("Expected none/allow/deny but got '" + input + "'"); } } diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/StringFlag.java b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/StringFlag.java index 080e2571a..2a48380d5 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/StringFlag.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/StringFlag.java @@ -57,7 +57,7 @@ public String getDefault() { } @Override - public String parseInput(FlagContext context) throws InvalidFlagFormat { + public String parseInput(FlagContext context) throws InvalidFlagFormatException { String lines = context.getUserInput().replaceAll("(? Date: Sun, 11 Jun 2023 00:07:43 +0200 Subject: [PATCH 5/6] Synchronize YamlRegionFile#saveAll This prevents multiple invocations of the method from intersecting, which could potentially cause the .tmp file to be overwritten before being renamed, thus corrupting the region storage --- .../protection/managers/storage/file/YamlRegionFile.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/managers/storage/file/YamlRegionFile.java b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/managers/storage/file/YamlRegionFile.java index cd1d26e7c..4c38a32be 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/managers/storage/file/YamlRegionFile.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/managers/storage/file/YamlRegionFile.java @@ -192,7 +192,7 @@ public Set loadAll(FlagRegistry flagRegistry) throws StorageExc } @Override - public void saveAll(Set regions) throws StorageException { + public synchronized void saveAll(Set regions) throws StorageException { checkNotNull(regions); File tempFile = new File(file.getParentFile(), file.getName() + ".tmp"); From 5763f53415cf084a66c76b64e18c356352359c70 Mon Sep 17 00:00:00 2001 From: TomyLobo Date: Mon, 12 Jun 2023 03:16:14 +0200 Subject: [PATCH 6/6] Synchronize on the class instead of the instance I didn't realize it before, but it looks like there is at least one instance of YamlRegionFile per world, so synchronizing on the instance wouldn't necessarily help. --- .../managers/storage/file/YamlRegionFile.java | 106 +++++++++--------- 1 file changed, 54 insertions(+), 52 deletions(-) diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/managers/storage/file/YamlRegionFile.java b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/managers/storage/file/YamlRegionFile.java index 4c38a32be..90cddd1ce 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/managers/storage/file/YamlRegionFile.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/managers/storage/file/YamlRegionFile.java @@ -192,66 +192,68 @@ public Set loadAll(FlagRegistry flagRegistry) throws StorageExc } @Override - public synchronized void saveAll(Set regions) throws StorageException { + public void saveAll(Set regions) throws StorageException { checkNotNull(regions); - File tempFile = new File(file.getParentFile(), file.getName() + ".tmp"); - YAMLProcessor config = createYamlProcessor(tempFile); - - config.clear(); - - YAMLNode regionsNode = config.addNode("regions"); - Map map = regionsNode.getMap(); - - for (ProtectedRegion region : regions) { - Map nodeMap = new HashMap<>(); - map.put(region.getId(), nodeMap); - YAMLNode node = new YAMLNode(nodeMap, false); - - if (region instanceof ProtectedCuboidRegion) { - ProtectedCuboidRegion cuboid = (ProtectedCuboidRegion) region; - node.setProperty("type", "cuboid"); - node.setProperty("min", cuboid.getMinimumPoint()); - node.setProperty("max", cuboid.getMaximumPoint()); - } else if (region instanceof ProtectedPolygonalRegion) { - ProtectedPolygonalRegion poly = (ProtectedPolygonalRegion) region; - node.setProperty("type", "poly2d"); - node.setProperty("min-y", poly.getMinimumPoint().getBlockY()); - node.setProperty("max-y", poly.getMaximumPoint().getBlockY()); - - List> points = new ArrayList<>(); - for (BlockVector2 point : poly.getPoints()) { - Map data = new HashMap<>(); - data.put("x", point.getBlockX()); - data.put("z", point.getBlockZ()); - points.add(data); + synchronized (YamlRegionFile.class) { + File tempFile = new File(file.getParentFile(), file.getName() + ".tmp"); + YAMLProcessor config = createYamlProcessor(tempFile); + + config.clear(); + + YAMLNode regionsNode = config.addNode("regions"); + Map map = regionsNode.getMap(); + + for (ProtectedRegion region : regions) { + Map nodeMap = new HashMap<>(); + map.put(region.getId(), nodeMap); + YAMLNode node = new YAMLNode(nodeMap, false); + + if (region instanceof ProtectedCuboidRegion) { + ProtectedCuboidRegion cuboid = (ProtectedCuboidRegion) region; + node.setProperty("type", "cuboid"); + node.setProperty("min", cuboid.getMinimumPoint()); + node.setProperty("max", cuboid.getMaximumPoint()); + } else if (region instanceof ProtectedPolygonalRegion) { + ProtectedPolygonalRegion poly = (ProtectedPolygonalRegion) region; + node.setProperty("type", "poly2d"); + node.setProperty("min-y", poly.getMinimumPoint().getBlockY()); + node.setProperty("max-y", poly.getMaximumPoint().getBlockY()); + + List> points = new ArrayList<>(); + for (BlockVector2 point : poly.getPoints()) { + Map data = new HashMap<>(); + data.put("x", point.getBlockX()); + data.put("z", point.getBlockZ()); + points.add(data); + } + + node.setProperty("points", points); + } else if (region instanceof GlobalProtectedRegion) { + node.setProperty("type", "global"); + } else { + node.setProperty("type", region.getClass().getCanonicalName()); } - node.setProperty("points", points); - } else if (region instanceof GlobalProtectedRegion) { - node.setProperty("type", "global"); - } else { - node.setProperty("type", region.getClass().getCanonicalName()); - } - - node.setProperty("priority", region.getPriority()); - node.setProperty("flags", getFlagData(region)); - node.setProperty("owners", getDomainData(region.getOwners())); - node.setProperty("members", getDomainData(region.getMembers())); + node.setProperty("priority", region.getPriority()); + node.setProperty("flags", getFlagData(region)); + node.setProperty("owners", getDomainData(region.getOwners())); + node.setProperty("members", getDomainData(region.getMembers())); - ProtectedRegion parent = region.getParent(); - if (parent != null) { - node.setProperty("parent", parent.getId()); + ProtectedRegion parent = region.getParent(); + if (parent != null) { + node.setProperty("parent", parent.getId()); + } } - } - config.setHeader(FILE_HEADER); - config.save(); + config.setHeader(FILE_HEADER); + config.save(); - //noinspection ResultOfMethodCallIgnored - file.delete(); - if (!tempFile.renameTo(file)) { - throw new StorageException("Failed to rename temporary regions file to " + file.getAbsolutePath()); + //noinspection ResultOfMethodCallIgnored + file.delete(); + if (!tempFile.renameTo(file)) { + throw new StorageException("Failed to rename temporary regions file to " + file.getAbsolutePath()); + } } }