diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
new file mode 100644
index 00000000000..a3ad5118388
--- /dev/null
+++ b/.git-blame-ignore-revs
@@ -0,0 +1 @@
+# .git-blame-ignore-revs
diff --git a/.github/workflows/cleanup-docs.yml b/.github/workflows/cleanup-docs.yml
index 750bab83213..68d12e84dc1 100644
--- a/.github/workflows/cleanup-docs.yml
+++ b/.github/workflows/cleanup-docs.yml
@@ -15,7 +15,7 @@ jobs:
echo "DOCS_OUTPUT_DIR=${GITHUB_WORKSPACE}/skript-docs/docs/nightly/${BRANCH_NAME}" >> $GITHUB_OUTPUT
echo "DOCS_REPO_DIR=${GITHUB_WORKSPACE}/skript-docs" >> $GITHUB_OUTPUT
- name: Checkout Skript
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
ref: ${{ github.event.repository.default_branch }}
submodules: recursive
diff --git a/.github/workflows/docs/setup-docs/action.yml b/.github/workflows/docs/setup-docs/action.yml
index cd6c6a05216..3f9fe050f47 100644
--- a/.github/workflows/docs/setup-docs/action.yml
+++ b/.github/workflows/docs/setup-docs/action.yml
@@ -26,7 +26,7 @@ runs:
ssh-key: ${{ inputs.docs_deploy_key }}
- uses: actions/setup-java@v3
with:
- java-version: '17'
+ java-version: '21'
distribution: 'adopt'
cache: gradle
- shell: bash
diff --git a/.github/workflows/java-17-builds.yml b/.github/workflows/java-17-builds.yml
index fa432f7f17d..2ca6d6bde61 100644
--- a/.github/workflows/java-17-builds.yml
+++ b/.github/workflows/java-17-builds.yml
@@ -1,4 +1,4 @@
-name: Java 17 CI (MC 1.17+)
+name: Java 17 CI (MC 1.17-1.20.4)
on:
push:
@@ -15,10 +15,12 @@ jobs:
- uses: actions/checkout@v4
with:
submodules: recursive
- - name: Set up JDK 17
- uses: actions/setup-java@v3
+ - name: validate gradle wrapper
+ uses: gradle/wrapper-validation-action@v2
+ - name: Set up JDK 21
+ uses: actions/setup-java@v4
with:
- java-version: '17'
+ java-version: '21'
distribution: 'adopt'
cache: gradle
- name: Grant execute permission for gradlew
@@ -26,7 +28,7 @@ jobs:
- name: Build Skript and run test scripts
run: ./gradlew clean skriptTestJava17
- name: Upload Nightly Build
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
if: success()
with:
name: skript-nightly
diff --git a/.github/workflows/java-21-builds.yml b/.github/workflows/java-21-builds.yml
new file mode 100644
index 00000000000..6c82f22ce78
--- /dev/null
+++ b/.github/workflows/java-21-builds.yml
@@ -0,0 +1,35 @@
+name: Java 21 CI (MC 1.20.6+)
+
+on:
+ push:
+ branches:
+ - master
+ - 'dev/**'
+ pull_request:
+
+jobs:
+ build:
+ if: "! contains(toJSON(github.event.commits.*.message), '[ci skip]')"
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: recursive
+ - name: validate gradle wrapper
+ uses: gradle/wrapper-validation-action@v2
+ - name: Set up JDK 21
+ uses: actions/setup-java@v4
+ with:
+ java-version: '21'
+ distribution: 'adopt'
+ cache: gradle
+ - name: Grant execute permission for gradlew
+ run: chmod +x gradlew
+ - name: Build Skript and run test scripts
+ run: ./gradlew clean skriptTestJava21
+ - name: Upload Nightly Build
+ uses: actions/upload-artifact@v4
+ if: success()
+ with:
+ name: skript-nightly
+ path: build/libs/*
diff --git a/.github/workflows/java-8-builds.yml b/.github/workflows/java-8-builds.yml
index 4a36e74a44b..0120b950f94 100644
--- a/.github/workflows/java-8-builds.yml
+++ b/.github/workflows/java-8-builds.yml
@@ -15,10 +15,12 @@ jobs:
- uses: actions/checkout@v4
with:
submodules: recursive
- - name: Set up JDK 17
- uses: actions/setup-java@v3
+ - name: validate gradle wrapper
+ uses: gradle/wrapper-validation-action@v2
+ - name: Set up JDK 21
+ uses: actions/setup-java@v4
with:
- java-version: '17'
+ java-version: '21'
distribution: 'adopt'
cache: gradle
- name: Grant execute permission for gradlew
@@ -26,7 +28,7 @@ jobs:
- name: Build Skript and run test scripts
run: ./gradlew clean skriptTestJava8
- name: Upload Nightly Build
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
if: success()
with:
name: skript-nightly
diff --git a/.github/workflows/junit-17-builds.yml b/.github/workflows/junit-17-builds.yml
index 69c5b73f27a..238bd9b132e 100644
--- a/.github/workflows/junit-17-builds.yml
+++ b/.github/workflows/junit-17-builds.yml
@@ -1,4 +1,4 @@
-name: JUnit (MC 1.17+)
+name: JUnit (MC 1.17-1.20.4)
on:
push:
@@ -15,10 +15,12 @@ jobs:
- uses: actions/checkout@v4
with:
submodules: recursive
- - name: Set up JDK 17
- uses: actions/setup-java@v3
+ - name: validate gradle wrapper
+ uses: gradle/wrapper-validation-action@v2
+ - name: Set up JDK 21
+ uses: actions/setup-java@v4
with:
- java-version: '17'
+ java-version: '21'
distribution: 'adopt'
cache: gradle
- name: Grant execute permission for gradlew
diff --git a/.github/workflows/junit-21-builds.disabled b/.github/workflows/junit-21-builds.disabled
new file mode 100644
index 00000000000..07bc5bebbeb
--- /dev/null
+++ b/.github/workflows/junit-21-builds.disabled
@@ -0,0 +1,31 @@
+# Disabled as EasyMock 5.2.0 is required for Java 21 support
+# However, we are currently using 5.0.1 (see https://github.com/SkriptLang/Skript/pull/6204#discussion_r1405302009)
+name: JUnit (MC 1.20.6+)
+
+on:
+ push:
+ branches:
+ - master
+ - 'dev/**'
+ pull_request:
+
+jobs:
+ build:
+ if: "! contains(toJSON(github.event.commits.*.message), '[ci skip]')"
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: recursive
+ - name: validate gradle wrapper
+ uses: gradle/wrapper-validation-action@v2
+ - name: Set up JDK 21
+ uses: actions/setup-java@v4
+ with:
+ java-version: '21'
+ distribution: 'adopt'
+ cache: gradle
+ - name: Grant execute permission for gradlew
+ run: chmod +x gradlew
+ - name: Build Skript and run JUnit
+ run: ./gradlew clean JUnitJava21
diff --git a/.github/workflows/junit-8-builds.yml b/.github/workflows/junit-8-builds.yml
index 3aa98f554a7..ec2fef19869 100644
--- a/.github/workflows/junit-8-builds.yml
+++ b/.github/workflows/junit-8-builds.yml
@@ -15,10 +15,12 @@ jobs:
- uses: actions/checkout@v4
with:
submodules: recursive
- - name: Set up JDK 17
- uses: actions/setup-java@v3
+ - name: validate gradle wrapper
+ uses: gradle/wrapper-validation-action@v2
+ - name: Set up JDK 21
+ uses: actions/setup-java@v4
with:
- java-version: '17'
+ java-version: '21'
distribution: 'adopt'
cache: gradle
- name: Grant execute permission for gradlew
diff --git a/.github/workflows/release-docs.yml b/.github/workflows/release-docs.yml
index fe5c9bb27aa..ee368ef5eb7 100644
--- a/.github/workflows/release-docs.yml
+++ b/.github/workflows/release-docs.yml
@@ -70,6 +70,7 @@ jobs:
with:
docs_output_dir: ${{ steps.configuration.outputs.DOCS_OUTPUT_DIR }}
docs_repo_dir: ${{ steps.configuration.outputs.DOCS_REPO_DIR }}
+ docs_output_dir: ${{ steps.configuration.outputs.DOCS_OUTPUT_DIR }}
skript_repo_dir: ${{ steps.configuration.outputs.SKRIPT_REPO_DIR }}
is_release: true
generate_javadocs: true
diff --git a/.github/workflows/repo.yml b/.github/workflows/repo.yml
index 3b6640cc765..a04327ed64c 100644
--- a/.github/workflows/repo.yml
+++ b/.github/workflows/repo.yml
@@ -11,10 +11,12 @@ jobs:
- uses: actions/checkout@v4
with:
submodules: recursive
- - name: Set up JDK 17
- uses: actions/setup-java@v3
+ - name: validate gradle wrapper
+ uses: gradle/wrapper-validation-action@v2
+ - name: Set up JDK 21
+ uses: actions/setup-java@v4
with:
- java-version: '17'
+ java-version: '21'
distribution: 'adopt'
cache: gradle
- name: Publish Skript
diff --git a/README.md b/README.md
index 36c82e5c00f..2dba8104819 100644
--- a/README.md
+++ b/README.md
@@ -77,13 +77,15 @@ Skript has some tests written in Skript. Running them requires a Minecraft
server, but our build script will create one for you. Running the tests is easy:
```
-./gradlew (quickTest|skriptTest|skriptTestJava8|skriptTestJava17)
+./gradlew (quickTest|skriptTest|skriptTestJava8|skriptTestJava17|skriptTestJava21)
```
quickTest
runs the test suite on newest supported server version.
-skriptTestJava17
(1.17+) runs the tests on the latest supported Java version.
-skriptTestJava8
(1.13-1.16) runs the tests on the oldest supported Java version.
-skriptTest
runs both skriptTestJava8 and skriptTestJava17
+skriptTestJava21
(1.20.6+) runs the tests on Java 21 supported versions.
+skriptTestJava17
(1.17-1.20.4) runs the tests on Java 17 supported versions.
+skriptTestJava8
(1.13-1.16) runs the tests on Java 8 supported versions.
+skriptTest
runs the tests on all versions.
+That is, it runs skriptTestJava8, skriptTestJava17, and skriptTestJava21.
By running the tests, you agree to Mojang's End User License Agreement.
diff --git a/build.gradle b/build.gradle
index c6d7b430a94..059026285ef 100644
--- a/build.gradle
+++ b/build.gradle
@@ -5,7 +5,6 @@ import java.time.LocalTime
plugins {
id 'com.github.johnrengelman.shadow' version '8.1.1'
- id 'com.github.hierynomus.license' version '0.16.1'
id 'maven-publish'
id 'java'
}
@@ -20,6 +19,7 @@ allprojects {
maven { url 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' }
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
maven { url 'https://repo.papermc.io/repository/maven-public/' }
+ maven { url = 'https://s01.oss.sonatype.org/content/repositories/snapshots/' } // needed for paper adventure snapshot
maven { url 'https://ci.emc.gs/nexus/content/groups/aikar/' }
}
}
@@ -27,20 +27,20 @@ allprojects {
dependencies {
shadow group: 'io.papermc', name: 'paperlib', version: '1.0.8'
shadow group: 'org.bstats', name: 'bstats-bukkit', version: '3.0.2'
- shadow group: 'net.kyori', name: 'adventure-text-serializer-bungeecord', version: '4.3.1'
+ shadow group: 'net.kyori', name: 'adventure-text-serializer-bungeecord', version: '4.3.2'
- implementation group: 'io.papermc.paper', name: 'paper-api', version: '1.20.2-R0.1-SNAPSHOT'
+ implementation group: 'io.papermc.paper', name: 'paper-api', version: '1.20.6-R0.1-SNAPSHOT'
implementation group: 'org.eclipse.jdt', name: 'org.eclipse.jdt.annotation', version: '2.2.700'
implementation group: 'com.google.code.findbugs', name: 'findbugs', version: '3.0.1'
implementation group: 'com.sk89q.worldguard', name: 'worldguard-legacy', version: '7.0.0-SNAPSHOT'
- implementation group: 'net.milkbowl.vault', name: 'Vault', version: '1.7.1', {
+ implementation group: 'net.milkbowl.vault', name: 'Vault', version: '1.7.3', {
exclude group: 'org.bstats', module: 'bstats-bukkit'
}
implementation fileTree(dir: 'lib', include: '*.jar')
testShadow group: 'junit', name: 'junit', version: '4.13.2'
- testShadow group: 'org.easymock', name: 'easymock', version: '5.2.0'
+ testShadow group: 'org.easymock', name: 'easymock', version: '5.0.1'
}
task checkAliases {
@@ -54,19 +54,19 @@ task checkAliases {
}
task testJar(type: ShadowJar) {
- dependsOn(compileTestJava, licenseTest)
+ dependsOn(compileTestJava)
archiveFileName = 'Skript-JUnit.jar'
from sourceSets.test.output, sourceSets.main.output, project.configurations.testShadow
}
task jar(overwrite: true, type: ShadowJar) {
dependsOn checkAliases
- archiveFileName = jarName ? 'Skript.jar' : jarName
+ archiveFileName = jarName ? 'Skript-' + project.version + '.jar' : jarName
from sourceSets.main.output
}
task build(overwrite: true, type: ShadowJar) {
- archiveFileName = jarName ? 'Skript.jar' : jarName
+ archiveFileName = jarName ? 'Skript-' + project.version + '.jar' : jarName
from sourceSets.main.output
}
@@ -140,15 +140,6 @@ publishing {
}
}
-license {
- header file('licenseheader.txt')
- exclude('**/Metrics.java') // Not under GPLv3
- exclude('**/BurgerHelper.java') // Not exclusively GPLv3
- exclude('**/*.sk') // Sample scripts and maybe aliases
- exclude('**/*.lang') // Language files do not have headers (still under GPLv3)
- exclude('**/*.json') // JSON files do not have headers
-}
-
// Task to check that test scripts are named correctly
tasks.register('testNaming') {
doLast {
@@ -177,7 +168,10 @@ enum Modifiers {
}
// Create a test task with given name, environments dir/file, dev mode and java version.
-void createTestTask(String name, String desc, String environments, int javaVersion, Modifiers... modifiers) {
+// -1 on the timeout means it'll be disabled.
+void createTestTask(String name, String desc, String environments, int javaVersion, long timeout, Modifiers... modifiers) {
+ if (timeout == 0)
+ timeout = 300000 // 5 minutes
boolean junit = modifiers.contains(Modifiers.JUNIT)
boolean releaseDocs = modifiers.contains(Modifiers.GEN_RELEASE_DOCS)
boolean docs = modifiers.contains(Modifiers.GEN_NIGHTLY_DOCS) || releaseDocs
@@ -191,7 +185,6 @@ void createTestTask(String name, String desc, String environments, int javaVersi
}
tasks.register(name, JavaExec) {
description = desc
- dependsOn licenseTest
if (junit) {
dependsOn testJar
} else if (releaseDocs) {
@@ -214,14 +207,15 @@ void createTestTask(String name, String desc, String environments, int javaVersi
main = 'ch.njol.skript.test.platform.PlatformMain'
args = [
'build/test_runners',
- junit ? 'src/test/skript/tests/junit' : 'src/test/skript/tests',
+ junit ? 'src/test/skript/junit' : 'src/test/skript/tests',
'src/test/resources/runner_data',
environments,
modifiers.contains(Modifiers.DEV_MODE),
docs,
junit,
modifiers.contains(Modifiers.DEBUG),
- project.findProperty('verbosity') ?: "null"
+ project.findProperty('verbosity') ?: "null",
+ timeout
]
// Do first is used when throwing exceptions.
@@ -237,9 +231,16 @@ void createTestTask(String name, String desc, String environments, int javaVersi
}
}
-def latestEnv = 'java17/paper-1.20.2.json'
-def latestJava = 17
-def oldestJava = 8
+def java21 = 21
+def java17 = 17
+def java8 = 8
+
+def latestEnv = 'java21/paper-1.20.6.json'
+def latestJava = java21
+def oldestJava = java8
+
+def latestJUnitEnv = 'java17/paper-1.20.4.json'
+def latestJUnitJava = java17
java {
toolchain.languageVersion.set(JavaLanguageVersion.of(latestJava))
@@ -256,24 +257,28 @@ compileTestJava.options.encoding = 'UTF-8'
String environments = 'src/test/skript/environments/';
String env = project.property('testEnv') == null ? latestEnv : project.property('testEnv') + '.json'
int envJava = project.property('testEnvJavaVersion') == null ? latestJava : Integer.parseInt(project.property('testEnvJavaVersion') as String)
-createTestTask('quickTest', 'Runs tests on one environment being the latest supported Java and Minecraft.', environments + latestEnv, latestJava)
-createTestTask('skriptTestJava17', 'Runs tests on all Java 17 environments.', environments + 'java17', latestJava)
-createTestTask('skriptTestJava8', 'Runs tests on all Java 8 environments.', environments + 'java8', oldestJava)
-createTestTask('skriptTestDev', 'Runs testing server and uses \'system.in\' for command input, stop server to finish.', environments + env, envJava, Modifiers.DEV_MODE, Modifiers.DEBUG)
-createTestTask('skriptProfile', 'Starts the testing server with JProfiler support.', environments + latestEnv, latestJava, Modifiers.PROFILE)
-createTestTask('genNightlyDocs', 'Generates the Skript documentation website html files.', environments + env, envJava, Modifiers.GEN_NIGHTLY_DOCS)
-createTestTask('genReleaseDocs', 'Generates the Skript documentation website html files for a release.', environments + env, envJava, Modifiers.GEN_RELEASE_DOCS)
+createTestTask('quickTest', 'Runs tests on one environment being the latest supported Java and Minecraft.', environments + latestEnv, latestJava, 0)
+createTestTask('skriptTestJava21', 'Runs tests on all Java 21 environments.', environments + 'java21', java21, 0)
+createTestTask('skriptTestJava17', 'Runs tests on all Java 17 environments.', environments + 'java17', java17, 0)
+createTestTask('skriptTestJava8', 'Runs tests on all Java 8 environments.', environments + 'java8', java8, 0)
+createTestTask('skriptTestDev', 'Runs testing server and uses \'system.in\' for command input, stop server to finish.', environments + env, envJava, 0, Modifiers.DEV_MODE, Modifiers.DEBUG)
+createTestTask('skriptProfile', 'Starts the testing server with JProfiler support.', environments + latestEnv, latestJava, -1, Modifiers.PROFILE)
+createTestTask('genNightlyDocs', 'Generates the Skript documentation website html files.', environments + env, envJava, 0, Modifiers.GEN_NIGHTLY_DOCS)
+createTestTask('genReleaseDocs', 'Generates the Skript documentation website html files for a release.', environments + env, envJava, 0, Modifiers.GEN_RELEASE_DOCS)
tasks.register('skriptTest') {
description = 'Runs tests on all environments.'
- dependsOn skriptTestJava8, skriptTestJava17
+ dependsOn skriptTestJava8, skriptTestJava17, skriptTestJava21
}
-createTestTask('JUnitQuick', 'Runs JUnit tests on one environment being the latest supported Java and Minecraft.', environments + latestEnv, latestJava, Modifiers.JUNIT)
-createTestTask('JUnitJava17', 'Runs JUnit tests on all Java 17 environments.', environments + 'java17', latestJava, Modifiers.JUNIT)
-createTestTask('JUnitJava8', 'Runs JUnit tests on all Java 8 environments.', environments + 'java8', oldestJava, Modifiers.JUNIT)
+createTestTask('JUnitQuick', 'Runs JUnit tests on one environment being the latest supported Java and Minecraft.', environments + latestJUnitEnv, latestJUnitJava, 0, Modifiers.JUNIT)
+// Disabled as EasyMock 5.2.0 is required for Java 21 support
+// However, we are currently using 5.0.1 (see https://github.com/SkriptLang/Skript/pull/6204#discussion_r1405302009)
+//createTestTask('JUnitJava21', 'Runs JUnit tests on all Java 21 environments.', environments + 'java21', java21, 0, Modifiers.JUNIT)
+createTestTask('JUnitJava17', 'Runs JUnit tests on all Java 17 environments.', environments + 'java17', java17, 0, Modifiers.JUNIT)
+createTestTask('JUnitJava8', 'Runs JUnit tests on all Java 8 environments.', environments + 'java8', java8, 0, Modifiers.JUNIT)
tasks.register('JUnit') {
description = 'Runs JUnit tests on all environments.'
- dependsOn JUnitJava8, JUnitJava17
+ dependsOn JUnitJava8, JUnitJava17//, JUnitJava21
}
// Build flavor configurations
@@ -282,10 +287,8 @@ task githubResources(type: ProcessResources) {
include '**'
version = project.property('version')
def channel = 'stable'
- if (version.contains('alpha'))
- channel = 'alpha'
- else if (version.contains('beta'))
- channel = 'beta'
+ if (version.contains('-'))
+ channel = 'prerelease'
filter ReplaceTokens, tokens: [
'version' : version,
'today' : '' + LocalTime.now(),
@@ -302,7 +305,7 @@ task githubResources(type: ProcessResources) {
task githubRelease(type: ShadowJar) {
from sourceSets.main.output
dependsOn githubResources
- archiveFileName = 'Skript-github.jar'
+ archiveFileName = 'Skript-' + version +'.jar'
manifest {
attributes(
'Name': 'ch/njol/skript',
@@ -317,10 +320,8 @@ task spigotResources(type: ProcessResources) {
include '**'
version = project.property('version')
def channel = 'stable'
- if (version.contains('alpha'))
- channel = 'alpha'
- else if (version.contains('beta'))
- channel = 'beta'
+ if (version.contains('-'))
+ channel = 'prerelease'
filter ReplaceTokens, tokens: [
'version' : version,
'today' : '' + LocalTime.now(),
@@ -356,7 +357,7 @@ task nightlyResources(type: ProcessResources) {
'version' : version,
'today' : '' + LocalTime.now(),
'release-flavor' : 'skriptlang-nightly', // SkriptLang build, automatically done by CI
- 'release-channel' : 'alpha', // No update checking, but these are VERY unstable
+ 'release-channel' : 'prerelease', // No update checking, but these are VERY unstable
'release-updater' : 'ch.njol.skript.update.NoUpdateChecker', // No auto updates for now
'release-source' : '',
'release-download': 'null'
@@ -367,7 +368,7 @@ task nightlyResources(type: ProcessResources) {
task nightlyRelease(type: ShadowJar) {
from sourceSets.main.output
- dependsOn nightlyResources, licenseMain
+ dependsOn nightlyResources
archiveFileName = 'Skript-nightly.jar'
manifest {
attributes(
diff --git a/code-conventions.md b/code-conventions.md
index 37ac182cd62..25c6d5c4417 100644
--- a/code-conventions.md
+++ b/code-conventions.md
@@ -66,6 +66,9 @@ With the exception of contacting our own resources (e.g. to check for updates) c
Code contributed must be licensed under GPLv3, by **you**.
We expect that any code you contribute is either owned by you or you have explicit permission to provide and license it to us.
+Licenses do not need to be printed in individual files (or packages) unless the licence applying to the code in
+that file (or package) deviates from the licence scope of its containing package.
+
Third party code (under a compatible licence) _may_ be accepted in the following cases:
- It is part of a public, freely-available library or resource.
- It is somehow necessary to your contribution, and you have been given permission to include it.
@@ -75,34 +78,61 @@ If we receive complaints regarding the licensing of a contribution we will forwa
If you have questions or complaints regarding the licensing or reproduction of a contribution you may contact us (the organisation) or the contributor of that code directly.
-If, in the future, we need to relicense contributed code, we will contact all contributors involved.
+If, in the future, we need to re-license contributed code, we will contact all contributors involved.
If we need to remove or alter contributed code due to a licensing issue we will attempt to notify its contributor.
## Code Style
### Formatting
+* Imports should be grouped together by type (e.g. all `java.lang...` imports together)
+ * Following the style of existing imports in a class is encouraged, but not required
+ * Wildcard `*` imports are permitted (as long as they do not interfere with existing imports), e.g. `java.lang.*`.
* Tabs, no spaces (unless in code imported from other projects)
-** No tabs/spaces in empty lines
+ - No tabs/spaces in empty lines
* No trailing whitespace
* At most 120 characters per line
- - In Javadoc/multiline comments, at most 80 characters per line
-* When statements consume multiple lines, all lines but first have two tabs of additional indentation
+ - In Javadoc/multiline comments, at most 80 characters per line
+* When statements consume multiple lines, all lines but the first have two tabs of additional indentation
+ - The exception to this is breaking up conditional statements (e.g. `if (x || y)`) where the
+ condition starts may be aligned
* Each class begins with an empty line
* No squeezing of multiple lines of code on a single line
* Separate method declarations with empty lines
- - Empty line after last method in a class is *not* required
- - Otherwise, empty line before and after method is a good rule of thumb
+ - Empty line after last method in a class is *not* required
+ - Otherwise, empty line before and after method is a good rule of thumb
* If fields have Javadoc, separate them with empty lines
* Use empty lines liberally inside methods to improve readability
* Use curly brackets to start and end most blocks
- - Only when a conditional block (if or else) contains a single statement, they may be omitted
- - When omitting brackets, still indent as if the code had brackets
- - Avoid omitting brackets if it produces hard-to-read code
+ - When a block contains a single statement, they may be omitted
+ - Brackets may not be omitted in a chain of other blocks that require brackets, e.g `if ... else {}`
+ - When omitting brackets, still indent as if the code had brackets
+ - Avoid omitting brackets if it produces hard-to-read or ambiguous code
+* Ternaries should be avoided where it makes the code complex or difficult to read
* Annotations for methods and classes are placed in lines before their declarations, one per line
-* When there are multiple annotations, place them in order:
- - @Override -> @Nullable -> @SuppressWarnings
- - For other annotations, doesn't matter; let your IDE decide
+ - Annotations for a structure go on the line before that structure
+ ```java
+ @Override
+ @SuppressWarnings("xyz")
+ public void myMethod() {
+ // Override goes above method because method is overriding
+ }
+ ```
+
+ - Annotations for the _value_ of a thing go before that value's type declaration
+ ```java
+ @Override
+ public @Nullable Object myMethod() {
+ // Nullable goes before Object because Object is Nullable
+ }
+ ```
+* When there are multiple annotations, it looks nicer to place them in length order (longest last)
+but this is not strictly required:
+ ```java
+ @Override
+ @Deprecated
+ @SuppressWarnings("xyz")
+ ```
* When splitting Strings into multiple lines the last part of the string must be (space character included) " " +
```java
String string = "example string " +
@@ -111,6 +141,7 @@ If we need to remove or alter contributed code due to a licensing issue we will
* When extending one of following classes: SimpleExpression, SimplePropertyExpression, Effect, Condition...
- Put overridden methods in order
+ - Put static registration before all methods
- SimpleExpression: init -> get/getAll -> acceptChange -> change -> setTime -> getTime -> isSingle -> getReturnType -> toString
- SimplePropertyExpression: -> init -> convert -> acceptChange -> change -> setTime -> getTime -> getReturnType -> getPropertyName
- Effect: init -> execute -> toString
@@ -127,9 +158,11 @@ If we need to remove or alter contributed code due to a licensing issue we will
- Static constant fields should be named in `UPPER_SNAKE_CASE`
* Localised messages should be named in `lower_snake_case`
- And that is the only place where snake_case is acceptable
-* Use prefixes only where their use has been already estabilished (such as `ExprSomeRandomThing`)
+* Use prefixes only where their use has been already established (such as `ExprSomeRandomThing`)
- Otherwise, use postfixes where necessary
- Common occurrences include: Struct (Structure), Sec (Section), EffSec (EffectSection), Eff (Effect), Cond (Condition), Expr (Expression)
+* Ensure variable/field names are descriptive. Avoid using shorthand names like `e`, or `c`
+ - e.g. Event should be `event`, not `e`. `e` is ambiguous and could mean a number of things
### Comments
* Prefer to comment *why* you're doing things instead of how you're doing them
@@ -161,33 +194,79 @@ Your comments should look something like these:
## Language Features
### Compatibility
-* Contributions should maintain Java 8 source/binary compatibility, even though compiling Skript requires Java 17
- - Users must not need JRE newer than version 8
-* Versions up to and including Java 17 should work too
+[//]: # (To be updated after feature/2.9 for Java 17)
+* Contributions should maintain Java 11 source/binary compatibility, even though compiling Skript requires Java 21
+ - Users must not need JRE newer than version 11
+* Versions up to and including Java 21 should work too
- Please avoid using unsafe reflection
* It is recommended to make fields final, if they are effectively final
-* Local variables and method parameters should not be declared final
+* Local variables and method parameters should not be declared final unless used in anonymous classes, lambdas
+or try-with-resources sections where their immutability is necessary
* Methods should be declared final only where necessary
* Use `@Override` whenever applicable
-
-### Nullness
+ - They may be omitted to prevent compilation errors when something overrides only
+ on a version-dependent basis (e.g. in Library XYZ version 2 we override `getX()` but in version 3 it's
+ gone, and we call it ourselves)
+
+### null-ness
+* We use **JetBrains** Annotations for specifying null-ness and method contracts.
+ * If editing a file using a different annotation set (e.g. Javax, Eclipse Sisu, Bukkit)
+ these should be replaced with their JetBrains equivalent.
+ * The semantics for JetBrains Annotations are strict _and should be observed!_
+ * Many IDEs have built-in compiler-level support for these, and can even be set to produce strict
+ errors when an annotation is misused; do not misuse them.
+ * **`@NotNull`**
+ * > An element annotated with NotNull claims null value is forbidden to return (for methods),
+ pass to (parameters) and hold (local variables and fields).
+ * Something is `@NotNull` iff it is never null from its inception (new X) to its garbage collection,
+ i.e. there is no point in time at which the value could ever be null.
+ * **`@Nullable`**
+ * > An element annotated with Nullable claims null value is perfectly valid to return (for methods),
+ > pass to (parameters) or hold in (local variables and fields).
+ >
+ > By convention, this annotation applied only when the value should always be checked
+ > against null because the developer could do nothing to prevent null from happening.
+ * Something is `@Nullable` iff there is _absolutely no way of determining_ (other than checking its
+ value `!= null`) whether it is null.
+ * In other words, if there is another way of knowing (e.g. you set it yourself, an `isPresent` method, etc.)
+ then it should not be marked nullable.
+ * **`@Contract`**
+ * The contract annotation should be used to express other behaviour (e.g. null depending on parameters).
* All fields, method parameters and their return values are non-null by default
- - Exceptions: Github API JSON mappings, Metrics
-* When something is nullable, mark it as so
-* Only ignore nullness errors when a variable is effectively non-null - if in doubt: check
- - Most common example is syntax elements, which are not initialised using a constructor
+ - Exceptions: GitHub API JSON mappings, Metrics
+* When ignoring warnings, use the no-inspection comment rather than a blanket suppression annotation
* Use assertions liberally: if you're sure something is not null, assert so to the compiler
- Makes finding bugs easier for developers
-* Assertions must **not** have side-effects - they may be skipped in real environments
+* Assertions must **not** have side-effects in non-test packages - they may be skipped in real environments
* Avoid checking non-null values for nullability
- Unless working around buggy addons, it is rarely necessary
- - This is why ignoring nullness errors is particularly dangerous
+ - This is why ignoring null-ness errors is particularly dangerous
+* Annotations on array types **must** be placed properly:
+ * Annotations on the array itself go before the array brackets
+ ```java
+ @Nullable Object @NotNull []
+ // a not-null array of nullable objects
+ ```
+ * Annotations on values inside the array go before the value declaration
+ ```java
+ @NotNull Object @Nullable []
+ // a nullable array of not-null objects
+ ```
+ * If this is not adhered to, an IDE may provide incorrect feedback.
### Assertions
Skript must run with assertations enabled; use them in your development environment. \
The JVM flag -ea
is used to enable them.
+## Code Complexity
+
+Dense, highly-complex code should be avoided to preserve readability and to help with future maintenance,
+especially within a single method body.
+
+There are many available metrics for measuring code complexity (for different purposes); [we have our own](https://stable.skriptlang.org/Radical_Complexity.pdf).
+There are no strict limits for code complexity, but you may be encouraged (or required) to reformat or break up methods
+into smaller, more manageable chunks. If in doubt, keep things simple.
## Minecraft Features
diff --git a/gradle.properties b/gradle.properties
index 3c6d77ca224..f78ae1766eb 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,9 +1,11 @@
-# Ensure encoding is consistent across systems
-org.gradle.jvmargs=-Dfile.encoding=UTF-8
+# Done to increase the memory available to gradle.
+# Ensure encoding is consistent across systems.
+org.gradle.jvmargs=-Xmx1G -Dfile.encoding=UTF-8
+org.gradle.parallel=true
groupid=ch.njol
name=skript
-version=2.7.1
+version=2.8.7
jarName=Skript.jar
-testEnv=java17/paper-1.20.2
-testEnvJavaVersion=17
+testEnv=java21/paper-1.20.6
+testEnvJavaVersion=21
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index ccebba7710d..7f93135c49b 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index fc10b601f7c..b82aa23a4f0 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
networkTimeout=10000
+validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
index 79a61d421cc..0adc8e1a532 100755
--- a/gradlew
+++ b/gradlew
@@ -83,10 +83,8 @@ done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
-APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
-
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -133,10 +131,13 @@ location of your Java installation."
fi
else
JAVACMD=java
- which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+ if ! command -v java >/dev/null 2>&1
+ then
+ die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
+ fi
fi
# Increase the maximum file descriptors if we can.
@@ -197,6 +198,10 @@ if "$cygwin" || "$msys" ; then
done
fi
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
diff --git a/licenseheader.txt b/licenseheader.txt
deleted file mode 100644
index 15be760be16..00000000000
--- a/licenseheader.txt
+++ /dev/null
@@ -1,16 +0,0 @@
- This file is part of Skript.
-
- Skript is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- Skript is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with Skript. If not, see .
-
-Copyright Peter Güttinger, SkriptLang team and contributors
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
index e5432e8214f..78b185aca4c 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,6 +1,6 @@
plugins {
- id 'org.gradle.toolchains.foojay-resolver-convention' version '0.7.0'
+ id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0'
}
rootProject.name = 'Skript'
diff --git a/skript-aliases b/skript-aliases
index 1ee77d8573a..9ea857f6b7d 160000
--- a/skript-aliases
+++ b/skript-aliases
@@ -1 +1 @@
-Subproject commit 1ee77d8573aa37456f1b49fe12aec7bb410d1dd7
+Subproject commit 9ea857f6b7dd1e4fc4a35a88149b9e463b537b06
diff --git a/src/main/java/ch/njol/skript/Skript.java b/src/main/java/ch/njol/skript/Skript.java
index 9740ee27efa..7aeeb0d38f0 100644
--- a/src/main/java/ch/njol/skript/Skript.java
+++ b/src/main/java/ch/njol/skript/Skript.java
@@ -26,6 +26,7 @@
import ch.njol.skript.classes.data.DefaultComparators;
import ch.njol.skript.classes.data.DefaultConverters;
import ch.njol.skript.classes.data.DefaultFunctions;
+import ch.njol.skript.classes.data.DefaultOperations;
import ch.njol.skript.classes.data.JavaClasses;
import ch.njol.skript.classes.data.SkriptClasses;
import ch.njol.skript.command.Commands;
@@ -82,6 +83,7 @@
import ch.njol.util.Kleenean;
import ch.njol.util.NullableChecker;
import ch.njol.util.StringUtils;
+import ch.njol.util.coll.CollectionUtils;
import ch.njol.util.coll.iterator.CheckedIterator;
import ch.njol.util.coll.iterator.EnumerationIterable;
import com.google.common.collect.Lists;
@@ -106,6 +108,7 @@
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.java.JavaPlugin;
import org.eclipse.jdt.annotation.Nullable;
+import org.junit.After;
import org.junit.runner.JUnitCore;
import org.junit.runner.Result;
import org.skriptlang.skript.lang.comparator.Comparator;
@@ -147,7 +150,6 @@
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
@@ -534,6 +536,7 @@ public void onEnable() {
new DefaultComparators();
new DefaultConverters();
new DefaultFunctions();
+ new DefaultOperations();
ChatMessages.registerListeners();
@@ -591,6 +594,8 @@ public void run() {
tainted = true;
try {
getAddonInstance().loadClasses("ch.njol.skript.test.runner");
+ if (TestMode.JUNIT)
+ getAddonInstance().loadClasses("org.skriptlang.skript.test.junit.registration");
} catch (IOException e) {
Skript.exception("Failed to load testing environment.");
Bukkit.getServer().shutdown();
@@ -684,11 +689,13 @@ protected void afterErrors() {
TestTracker.testFailed("exception was thrown during execution");
}
if (TestMode.JUNIT) {
- SkriptLogger.setVerbosity(Verbosity.DEBUG);
info("Running all JUnit tests...");
long milliseconds = 0, tests = 0, fails = 0, ignored = 0, size = 0;
try {
List> classes = Lists.newArrayList(Utils.getClasses(Skript.getInstance(), "org.skriptlang.skript.test", "tests"));
+ // Don't attempt to run inner/anonymous classes as tests
+ classes.removeIf(Class::isAnonymousClass);
+ classes.removeIf(Class::isLocalClass);
// Test that requires package access. This is only present when compiling with src/test.
classes.add(Class.forName("ch.njol.skript.variables.FlatFileStorageTest"));
size = classes.size();
@@ -702,6 +709,23 @@ protected void afterErrors() {
Result junit = JUnitCore.runClasses(clazz);
TestTracker.testStarted("JUnit: '" + test + "'");
+ /**
+ * Usage of @After is pointless if the JUnit class requires delay. As the @After will happen instantly.
+ * The JUnit must override the 'cleanup' method to avoid Skript automatically cleaning up the test data.
+ */
+ boolean overrides = false;
+ for (Method method : clazz.getDeclaredMethods()) {
+ if (!method.isAnnotationPresent(After.class))
+ continue;
+ if (SkriptJUnitTest.getShutdownDelay() > 1)
+ warning("Using @After in JUnit classes, happens instantaneously, and JUnit class '" + test + "' requires a delay. Do your test cleanup in the script junit file or 'cleanup' method.");
+ if (method.getName().equals("cleanup"))
+ overrides = true;
+ }
+ if (SkriptJUnitTest.getShutdownDelay() > 1 && !overrides)
+ error("The JUnit class '" + test + "' does not override the method 'cleanup' thus the test data will instantly be cleaned up. " +
+ "This JUnit test requires longer shutdown time: " + SkriptJUnitTest.getShutdownDelay());
+
// Collect all data from the current JUnit test.
shutdownDelay = Math.max(shutdownDelay, SkriptJUnitTest.getShutdownDelay());
tests += junit.getRunCount();
@@ -712,7 +736,7 @@ protected void afterErrors() {
// If JUnit failures are present, add them to the TestTracker.
junit.getFailures().forEach(failure -> {
String message = failure.getMessage() == null ? "" : " " + failure.getMessage();
- TestTracker.testFailed("'" + test + "': " + message);
+ TestTracker.JUnitTestFailed(test, message);
Skript.exception(failure.getException(), "JUnit test '" + failure.getTestHeader() + " failed.");
});
SkriptJUnitTest.clearJUnitTest();
@@ -734,7 +758,7 @@ protected void afterErrors() {
// Delay server shutdown to stop the server from crashing because the current tick takes a long time due to all the tests
Bukkit.getScheduler().runTaskLater(Skript.this, () -> {
if (TestMode.JUNIT && !EffObjectives.isJUnitComplete())
- TestTracker.testFailed(EffObjectives.getFailedObjectivesString());
+ EffObjectives.fail();
info("Collecting results to " + TestMode.RESULTS_FILE);
String results = new Gson().toJson(TestTracker.collectResults());
@@ -1133,7 +1157,17 @@ public void onPluginDisable(PluginDisableEvent event) {
try {
// Spigot removed the mapping for this method in 1.18, so its back to obfuscated method
// 1.19 mapping is u and 1.18 is v
- String isRunningMethod = Skript.isRunningMinecraft(1, 19) ? "u" : Skript.isRunningMinecraft(1, 18) ? "v" :"isRunning";
+ String isRunningMethod = "isRunning";
+
+ if (Skript.isRunningMinecraft(1, 20, 5)) {
+ isRunningMethod = "x";
+ } else if (Skript.isRunningMinecraft(1, 20)) {
+ isRunningMethod = "v";
+ } else if (Skript.isRunningMinecraft(1, 19)) {
+ isRunningMethod = "u";
+ } else if (Skript.isRunningMinecraft(1, 18)) {
+ isRunningMethod = "v";
+ }
IS_RUNNING = MC_SERVER.getClass().getMethod(isRunningMethod);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
@@ -1261,7 +1295,7 @@ public static boolean isAcceptRegistrations() {
}
public static void checkAcceptRegistrations() {
- if (!isAcceptRegistrations())
+ if (!isAcceptRegistrations() && !Skript.testing())
throw new SkriptAPIException("Registration can only be done during plugin initialization");
}
@@ -1436,6 +1470,7 @@ public boolean check(final @Nullable ExpressionInfo, ?> i) {
// ================ EVENTS ================
+ private static final List> events = new ArrayList<>(50);
private static final List> structures = new ArrayList<>(10);
/**
@@ -1468,10 +1503,10 @@ public static SkriptEventInfo registerEvent(String na
String[] transformedPatterns = new String[patterns.length];
for (int i = 0; i < patterns.length; i++)
- transformedPatterns[i] = "[on] " + SkriptEvent.fixPattern(patterns[i]) + SkriptEventInfo.EVENT_PRIORITY_SYNTAX;
+ transformedPatterns[i] = SkriptEvent.fixPattern(patterns[i]);
SkriptEventInfo r = new SkriptEventInfo<>(name, transformedPatterns, c, originClassPath, events);
- structures.add(r);
+ Skript.events.add(r);
return r;
}
@@ -1489,14 +1524,8 @@ public static void registerStructure(Class c, EntryVali
structures.add(structureInfo);
}
- /**
- * Modifications made to the returned Collection will not be reflected in the events available for parsing.
- */
public static Collection> getEvents() {
- // Only used in documentation generation, so generating a new list each time is fine
- return (Collection>) (Collection>) structures.stream()
- .filter(info -> info instanceof SkriptEventInfo)
- .collect(Collectors.toList());
+ return events;
}
public static List> getStructures() {
diff --git a/src/main/java/ch/njol/skript/SkriptCommand.java b/src/main/java/ch/njol/skript/SkriptCommand.java
index 62261d0e426..0bc25391267 100644
--- a/src/main/java/ch/njol/skript/SkriptCommand.java
+++ b/src/main/java/ch/njol/skript/SkriptCommand.java
@@ -62,19 +62,19 @@ public class SkriptCommand implements CommandExecutor {
// TODO /skript scripts show/list - lists all enabled and/or disabled scripts in the scripts folder and/or subfolders (maybe add a pattern [using * and **])
// TODO document this command on the website
private static final CommandHelp SKRIPT_COMMAND_HELP = new CommandHelp("/skript", SkriptColor.LIGHT_CYAN, CONFIG_NODE + ".help")
- .add(new CommandHelp("reload", SkriptColor.DARK_RED)
+ .add(new CommandHelp("reload", SkriptColor.DARK_CYAN)
.add("all")
.add("config")
.add("aliases")
.add("scripts")
.add("