Merge branch 'MV5' into pr/2975
# Conflicts: # src/main/java/com/onarandombox/MultiverseCore/world/SimpleMVWorldManager.java
This commit is contained in:
commit
55aac81258
|
@ -1,2 +0,0 @@
|
|||
prerelease:
|
||||
- '*'
|
|
@ -0,0 +1,34 @@
|
|||
name: 'Call: GitHub Release'
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
release_mode:
|
||||
description: 'Release mode'
|
||||
required: true
|
||||
type: string
|
||||
version_bump:
|
||||
description: 'Version bump'
|
||||
required: false
|
||||
type: string
|
||||
promote_from:
|
||||
description: 'Promote from'
|
||||
required: false
|
||||
type: string
|
||||
outputs:
|
||||
release_created:
|
||||
description: 'Release created'
|
||||
value: ${{ jobs.github_release.outputs.release_created }}
|
||||
tag_name:
|
||||
description: 'Tag name'
|
||||
value: ${{ jobs.github_release.outputs.tag_name }}
|
||||
|
||||
jobs:
|
||||
github_release:
|
||||
uses: ./.github/workflows/generic.github_release.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
plugin_name: multiverse-core
|
||||
release_mode: ${{ inputs.release_mode }}
|
||||
version_bump: ${{ inputs.version_bump }}
|
||||
promote_from: ${{ inputs.promote_from }}
|
|
@ -0,0 +1,83 @@
|
|||
name: 'Call: Platform Uploads'
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
target_tag:
|
||||
description: 'Version to upload'
|
||||
required: true
|
||||
type: string
|
||||
upload_modrinth:
|
||||
description: 'Upload to modrinth.com'
|
||||
required: true
|
||||
type: string
|
||||
upload_dbo:
|
||||
description: 'Upload to dev.bukkit.org'
|
||||
required: true
|
||||
type: string
|
||||
upload_hangar:
|
||||
description: 'Upload to hangar.papermc.io'
|
||||
required: true
|
||||
type: string
|
||||
secrets:
|
||||
MODRINTH_TOKEN:
|
||||
required: true
|
||||
DBO_UPLOAD_API_TOKEN:
|
||||
required: true
|
||||
HANGAR_UPLOAD_TOKEN:
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
platform_uploads:
|
||||
uses: ./.github/workflows/generic.platform_uploads.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
plugin_name: multiverse-core
|
||||
modrinth_project_id: 3wmN97b8
|
||||
modrinth_dependencies: >
|
||||
[
|
||||
{"project_id": "qvdtDX3s", "dependency_type": "optional"},
|
||||
{"project_id": "8VMk6P0I", "dependency_type": "optional"},
|
||||
{"project_id": "vtawPsTo", "dependency_type": "optional"},
|
||||
{"project_id": "WuErDeI1", "dependency_type": "optional"}
|
||||
]
|
||||
|
||||
dbo_project_id: 30765
|
||||
dbo_project_relations: >
|
||||
[
|
||||
{"slug": "multiverse-inventories", "type": "optionalDependency"},
|
||||
{"slug": "multiverse-portals", "type": "optionalDependency"},
|
||||
{"slug": "multiverse-netherportals", "type": "optionalDependency"},
|
||||
{"slug": "multiverse-signportals", "type": "optionalDependency"},
|
||||
{"slug": "vault", "type": "optionalDependency"}
|
||||
]
|
||||
|
||||
hangar_slug: Multiverse-Core
|
||||
hangar_plugin_dependencies: >
|
||||
{ "PAPER": [
|
||||
{
|
||||
"name": "Multiverse-Inventories",
|
||||
"required": false,
|
||||
"platforms": ["PAPER"]
|
||||
},
|
||||
{
|
||||
"name": "Multiverse-Portals",
|
||||
"required": false,
|
||||
"platforms": ["PAPER"]
|
||||
},
|
||||
{
|
||||
"name": "Multiverse-NetherPortals",
|
||||
"required": false,
|
||||
"platforms": ["PAPER"]
|
||||
},
|
||||
{
|
||||
"name": "Multiverse-SignPortals",
|
||||
"required": false,
|
||||
"platforms": ["PAPER"]
|
||||
}
|
||||
]}
|
||||
|
||||
target_tag: ${{ inputs.target_tag }}
|
||||
upload_modrinth: ${{ inputs.upload_modrinth }}
|
||||
upload_dbo: ${{ inputs.upload_dbo }}
|
||||
upload_hangar: ${{ inputs.upload_hangar }}
|
|
@ -0,0 +1,31 @@
|
|||
name: 'Dispatch: Platform Uploads'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
target_tag:
|
||||
description: 'Version to upload'
|
||||
required: true
|
||||
type: string
|
||||
upload_modrinth:
|
||||
description: 'Upload to modrinth.com'
|
||||
required: true
|
||||
type: boolean
|
||||
upload_dbo:
|
||||
description: 'Upload to dev.bukkit.org'
|
||||
required: true
|
||||
type: boolean
|
||||
upload_hangar:
|
||||
description: 'Upload to hangar.papermc.io'
|
||||
required: true
|
||||
type: boolean
|
||||
|
||||
jobs:
|
||||
dispatch_platform_uploads:
|
||||
uses: ./.github/workflows/call.platform_uploads.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
target_tag: ${{ github.event.inputs.target_tag }}
|
||||
upload_modrinth: ${{ github.event.inputs.upload_modrinth }}
|
||||
upload_dbo: ${{ github.event.inputs.upload_dbo }}
|
||||
upload_hangar: ${{ github.event.inputs.upload_hangar }}
|
|
@ -0,0 +1,38 @@
|
|||
name: 'Dispatch: Promote Release'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
target_tag:
|
||||
description: 'Version to promote'
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
check_version:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Verify input version is prerelease
|
||||
run: |
|
||||
if [[ "${{ github.event.inputs.target_tag }}" != *"pre"* ]]; then
|
||||
echo "Version must be a prerelease"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
github_release:
|
||||
needs: check_version
|
||||
uses: ./.github/workflows/call.github_release.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
release_mode: promote
|
||||
promote_from: ${{ github.event.inputs.target_tag }}
|
||||
|
||||
platform_uploads:
|
||||
needs: github_release
|
||||
if: needs.github_release.outputs.release_created == 'true'
|
||||
uses: ./.github/workflows/call.platform_uploads.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
target_tag: ${{ needs.github_release.outputs.tag_name }}
|
||||
upload_modrinth: 'true'
|
||||
upload_dbo: 'true'
|
||||
upload_hangar: 'true'
|
|
@ -0,0 +1,91 @@
|
|||
name: 'Generic: GitHub Release'
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
# Plugin specific
|
||||
plugin_name:
|
||||
description: 'Plugin name'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
# Common params
|
||||
release_mode:
|
||||
description: 'Release mode'
|
||||
required: true
|
||||
type: string
|
||||
version_bump:
|
||||
description: 'Version bump'
|
||||
required: false
|
||||
type: string
|
||||
promote_from:
|
||||
description: 'Promote from'
|
||||
required: false
|
||||
type: string
|
||||
outputs:
|
||||
release_created:
|
||||
description: 'Release created'
|
||||
value: ${{ jobs.github_release.outputs.release_created }}
|
||||
tag_name:
|
||||
description: 'Tag name'
|
||||
value: ${{ jobs.github_release.outputs.tag_name }}
|
||||
|
||||
jobs:
|
||||
github_release:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
release_created: ${{ steps.release.outputs.release_created }}
|
||||
tag_name: ${{ steps.release.outputs.tag_name }}
|
||||
steps:
|
||||
- name: Echo inputs
|
||||
run: |
|
||||
echo "release_mode: ${{ inputs.release_mode }}"
|
||||
echo "version_bump: ${{ inputs.version_bump }}"
|
||||
echo "promote_from: ${{ inputs.promote_from }}"
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ inputs.promote_from }}
|
||||
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
cache: gradle
|
||||
|
||||
- name: Validate Gradle wrapper
|
||||
uses: gradle/wrapper-validation-action@v1
|
||||
|
||||
- name: Build and test
|
||||
uses: gradle/gradle-build-action@v2
|
||||
with:
|
||||
arguments: clean build -x assemble -x shadowJar -x checkStyleMain -x checkStyleTest
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Create release
|
||||
id: release
|
||||
uses: benwoo1110/semantic-release-action@v1
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
release_mode: ${{ inputs.release_mode }}
|
||||
version_bump: ${{ inputs.version_bump }}
|
||||
promote_from: ${{ inputs.promote_from }}
|
||||
|
||||
- name: Publish package
|
||||
if: steps.release.outputs.release_created == 'true'
|
||||
uses: gradle/gradle-build-action@v2
|
||||
with:
|
||||
arguments: publish -x checkStyleMain -x checkStyleTest -x test
|
||||
env:
|
||||
GITHUB_VERSION: ${{ steps.release.outputs.publish_version }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Upload release artifact
|
||||
if: steps.release.outputs.release_created == 'true'
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: build/libs/${{ inputs.plugin_name }}-${{ steps.release.outputs.publish_version }}.jar
|
||||
asset_name: ${{ inputs.plugin_name }}-${{ steps.release.outputs.tag_name }}.jar
|
||||
tag: ${{ steps.release.outputs.tag_name }}
|
|
@ -0,0 +1,140 @@
|
|||
name: 'Generic: Platform Uploads'
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
# Plugin specific params
|
||||
plugin_name:
|
||||
description: 'Plugin name'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
modrinth_project_id:
|
||||
description: 'modrinth.com project ID'
|
||||
required: true
|
||||
type: string
|
||||
modrinth_dependencies:
|
||||
description: 'modrinth.com project dependencies'
|
||||
required: false
|
||||
type: string
|
||||
default: '[]'
|
||||
|
||||
dbo_project_id:
|
||||
description: 'dev.bukkit.org project ID'
|
||||
required: true
|
||||
type: string
|
||||
dbo_project_relations:
|
||||
description: 'dev.bukkit.org project relations'
|
||||
required: false
|
||||
type: string
|
||||
default: '[]'
|
||||
|
||||
hangar_slug:
|
||||
description: 'hangar.papermc.io project slug'
|
||||
required: true
|
||||
type: string
|
||||
hangar_plugin_dependencies:
|
||||
description: 'hangar.papermc.io project dependencies'
|
||||
required: false
|
||||
type: string
|
||||
default: '{}'
|
||||
|
||||
# Common params
|
||||
target_tag:
|
||||
description: 'Version to upload'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
upload_modrinth:
|
||||
description: 'Upload to modrinth.com'
|
||||
required: true
|
||||
type: string
|
||||
upload_dbo:
|
||||
description: 'Upload to dev.bukkit.org'
|
||||
required: true
|
||||
type: string
|
||||
upload_hangar:
|
||||
description: 'Upload to hangar.papermc.io'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
secrets:
|
||||
MODRINTH_TOKEN:
|
||||
required: true
|
||||
DBO_UPLOAD_API_TOKEN:
|
||||
required: true
|
||||
HANGAR_UPLOAD_TOKEN:
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
platform_uploads:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Get release info
|
||||
id: release-info
|
||||
uses: cardinalby/git-get-release-action@1.2.4
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag: ${{ inputs.target_tag }}
|
||||
|
||||
- name: Download release artifact
|
||||
id: release-artifact
|
||||
uses: dsaltares/fetch-gh-release-asset@1.1.1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
version: tags/${{ steps.release-info.outputs.tag_name }}
|
||||
file: ${{ inputs.plugin_name }}-${{ steps.release-info.outputs.tag_name }}.jar
|
||||
|
||||
- name: Parse release type
|
||||
id: parse-release-type
|
||||
run: |
|
||||
if [[ "${{ steps.release-info.outputs.prerelease }}" == "true" ]]; then
|
||||
echo Setting release_type to Beta
|
||||
echo "release_type=Beta" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo Setting release_type to Release
|
||||
echo "release_type=Release" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Upload to Modrinth
|
||||
if: ${{ !cancelled() && inputs.upload_modrinth == 'true' }}
|
||||
uses: benwoo1110/modrinth-upload-action@v1
|
||||
with:
|
||||
api_token: ${{ secrets.MODRINTH_TOKEN }}
|
||||
project_id: ${{ inputs.modrinth_project_id }}
|
||||
version_number: ${{ steps.release-info.outputs.tag_name }}
|
||||
files: '["${{ github.workspace }}/${{ inputs.plugin_name }}-${{ steps.release-info.outputs.tag_name }}.jar"]'
|
||||
name: ${{ steps.release-info.outputs.tag_name }}
|
||||
changelog: ${{ steps.release-artifact.outputs.body }}
|
||||
game_versions: 1.20.1, 1.20, 1.19.4, 1.19.3, 1.19.2, 1.19.1, 1.19, 1.18.2, 1.18.1, 1.18, 1.17.1, 1.17, 1.16.5, 1.16.4, 1.16.3, 1.16.2, 1.16.1, 1.16, 1.15.2, 1.15.1, 1.15, 1.14.4, 1.14.3, 1.14.2, 1.14.1, 1.14, 1.13.2, 1.13.1, 1.13
|
||||
version_type: ${{ steps.parse-release-type.outputs.release_type }}
|
||||
loaders: bukkit, spigot, paper
|
||||
dependencies: ${{ inputs.modrinth_dependencies }}
|
||||
|
||||
- name: Upload to DBO
|
||||
if: ${{ !cancelled() && inputs.upload_dbo == 'true' }}
|
||||
uses: benwoo1110/dbo-upload-action@v1
|
||||
with:
|
||||
api_token: ${{ secrets.DBO_UPLOAD_API_TOKEN }}
|
||||
project_id: ${{ inputs.dbo_project_id }}
|
||||
changelog: ${{ steps.release-artifact.outputs.body }}
|
||||
changelog_type: markdown
|
||||
display_name: ${{ steps.release-info.outputs.tag_name }}
|
||||
game_versions: 1.20.1, 1.20, 1.19.4, 1.19.3, 1.19.2, 1.19.1, 1.19, 1.18.2, 1.18.1, 1.18, 1.17, 1.16, 1.15, 1.14, 1.13
|
||||
release_type: ${{ steps.parse-release-type.outputs.release_type }}
|
||||
project_relations: ${{ inputs.dbo_project_relations }}
|
||||
file_path: ${{ github.workspace }}/${{ inputs.plugin_name }}-${{ steps.release-info.outputs.tag_name }}.jar
|
||||
|
||||
- name: Upload to Hangar
|
||||
if: ${{ !cancelled() && inputs.upload_hangar == 'true' }}
|
||||
uses: benwoo1110/hangar-upload-action@v1
|
||||
with:
|
||||
api_token: ${{ secrets.HANGAR_UPLOAD_TOKEN }}
|
||||
slug: ${{ inputs.hangar_slug }}
|
||||
version: ${{ steps.release-info.outputs.tag_name }}
|
||||
channel: ${{ steps.parse-release-type.outputs.release_type }}
|
||||
files: '[{"path": "${{ github.workspace }}/${{ inputs.plugin_name }}-${{ steps.release-info.outputs.tag_name }}.jar", "platforms": ["PAPER"]}]'
|
||||
description: ${{ steps.release-artifact.outputs.body }}
|
||||
platform_dependencies: '{"PAPER": ["1.13-1.20.1"]}'
|
||||
plugin_dependencies: ${{ inputs.hangar_plugin_dependencies }}
|
|
@ -1,8 +1,12 @@
|
|||
name: Run unit tests against all PRs
|
||||
name: 'Generic: Test'
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize]
|
||||
workflow_call:
|
||||
inputs:
|
||||
plugin_name:
|
||||
description: 'Plugin name'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
test:
|
||||
|
@ -32,5 +36,5 @@ jobs:
|
|||
- name: Artifact output
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: multiverse-core-pr${{ github.event.pull_request.number }}
|
||||
path: build/libs/multiverse-core-pr${{ github.event.pull_request.number }}.jar
|
||||
name: ${{ inputs.plugin_name }}-pr${{ github.event.pull_request.number }}
|
||||
path: build/libs/${{ inputs.plugin_name }}-pr${{ github.event.pull_request.number }}.jar
|
|
@ -0,0 +1,24 @@
|
|||
name: 'Main: Prerelease'
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
github_release_on_push:
|
||||
uses: ./.github/workflows/call.github_release.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
release_mode: prerelease
|
||||
version_bump: prlabel
|
||||
|
||||
platform_uploads_on_push:
|
||||
needs: github_release_on_push
|
||||
if: needs.github_release_on_push.outputs.release_created == 'true'
|
||||
uses: ./.github/workflows/call.platform_uploads.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
target_tag: ${{ needs.github_release_on_push.outputs.tag_name }}
|
||||
upload_modrinth: 'true'
|
||||
upload_dbo: 'false'
|
||||
upload_hangar: 'false'
|
|
@ -1,4 +1,4 @@
|
|||
name: Require PR Labels
|
||||
name: 'PR: Require Label'
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
@ -15,4 +15,4 @@ jobs:
|
|||
with:
|
||||
mode: exactly
|
||||
count: 1
|
||||
labels: "release:major, release:minor, release:patch, no version bump"
|
||||
labels: "release:major, release:minor, release:patch, no release"
|
|
@ -0,0 +1,11 @@
|
|||
name: 'PR: Test'
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
uses: ./.github/workflows/generic.test.yml
|
||||
with:
|
||||
plugin_name: multiverse-core
|
|
@ -1,17 +0,0 @@
|
|||
name: "Pull Request Labeler"
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened]
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
prerelease_labeler:
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/labeler@v4
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
|
@ -1,90 +0,0 @@
|
|||
name: Manually promote last prerelease to release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version to promote'
|
||||
required: true
|
||||
version-bump:
|
||||
description: 'Version bump to apply - should usually match the version bump used for the prerelease since last release'
|
||||
required: true
|
||||
type: choice
|
||||
options:
|
||||
- 'patch'
|
||||
- 'minor'
|
||||
- 'major'
|
||||
|
||||
jobs:
|
||||
manually_promote_release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Verify input version is prerelease
|
||||
run: |
|
||||
if [[ "${{ github.event.inputs.version }}" != *"pre"* ]]; then
|
||||
echo "Version must be a prerelease"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Get release info
|
||||
id: get-release
|
||||
uses: cardinalby/git-get-release-action@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
with:
|
||||
tag: ${{ github.event.inputs.version }}
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ steps.get-release.outputs.tag_name }}
|
||||
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'adopt'
|
||||
cache: gradle
|
||||
|
||||
- name: Validate Gradle wrapper
|
||||
uses: gradle/wrapper-validation-action@v1
|
||||
|
||||
- name: Remove prerelease tag
|
||||
run: |
|
||||
echo "Removing prerelease tag from version"
|
||||
echo "VERSION=$(echo ${{ steps.get-release.outputs.tag_name }} | sed -E 's/-pre.*//')" >> $GITHUB_ENV
|
||||
|
||||
- name: Build
|
||||
uses: gradle/gradle-build-action@v2
|
||||
with:
|
||||
arguments: clean build -x test -x checkstyleMain -x checkstyleTest
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_VERSION: ${{ env.VERSION }}
|
||||
|
||||
- name: Create release
|
||||
id: release
|
||||
uses: Multiverse/release-on-push-action@skip_prs
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
bump_version_scheme: ${{ github.event.inputs.version-bump }}
|
||||
tag_prefix: ''
|
||||
release_name: "<RELEASE_VERSION>"
|
||||
use_github_release_notes: true
|
||||
ref: ${{ steps.get-release.outputs.target_commitish }}
|
||||
skip_prs: true
|
||||
|
||||
- name: Publish package
|
||||
uses: gradle/gradle-build-action@v2
|
||||
with:
|
||||
arguments: publish
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_VERSION: ${{ env.VERSION }}
|
||||
|
||||
- name: Upload release artifact
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: build/libs/multiverse-core-${{ env.VERSION }}.jar
|
||||
asset_name: multiverse-core-${{ steps.release.outputs.tag_name }}.jar
|
||||
tag: ${{ steps.release.outputs.tag_name }}
|
|
@ -1,64 +0,0 @@
|
|||
name: Create Release Version & Publish Package
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
release_on_push:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'adopt'
|
||||
cache: gradle
|
||||
|
||||
- name: Validate Gradle wrapper
|
||||
uses: gradle/wrapper-validation-action@v1
|
||||
|
||||
- name: Test & Build
|
||||
uses: gradle/gradle-build-action@v2
|
||||
with:
|
||||
arguments: clean build -x assemble -x shadowJar
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Create release
|
||||
id: release
|
||||
uses: Multiverse/release-on-push-action@support_prerelease
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
bump_version_scheme: norelease
|
||||
tag_prefix: ''
|
||||
release_name: "<RELEASE_VERSION>"
|
||||
use_github_release_notes: true
|
||||
|
||||
- name: Modify version scheme
|
||||
run: |
|
||||
if [[ "${{ steps.release.outputs.tag_name }}" == *"pre"* ]]; then
|
||||
echo "Replacing prerelease version scheme with SNAPSHOT"
|
||||
echo "VERSION=$(echo ${{ steps.release.outputs.tag_name }} | sed -E 's/-pre.*/-SNAPSHOT/')" >> $GITHUB_ENV
|
||||
else
|
||||
echo "Using release version scheme"
|
||||
echo "VERSION=${{ steps.release.outputs.tag_name }}" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Publish package
|
||||
uses: gradle/gradle-build-action@v2
|
||||
with:
|
||||
arguments: publish
|
||||
env:
|
||||
GITHUB_VERSION: ${{ env.VERSION }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Upload release artifact
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: build/libs/multiverse-core-${{ env.VERSION }}.jar
|
||||
asset_name: multiverse-core-${{ steps.release.outputs.tag_name }}.jar
|
||||
tag: ${{ steps.release.outputs.tag_name }}
|
36
README.md
36
README.md
|
@ -1,26 +1,33 @@
|
|||
![Multiverse 2](config/multiverse2-long.png)
|
||||
<p align="center">
|
||||
<img src="config/multiverse2-long.png" alt="Multiverse Logo">
|
||||
</p>
|
||||
|
||||
[![Modrinth](https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy/available/modrinth_vector.svg)](https://modrinth.com/plugin/multiverse-core)
|
||||
[![hangar](https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy/available/hangar_vector.svg)](https://hangar.papermc.io/Multiverse/Multiverse-Core)
|
||||
[![bukkit](https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy/supported/bukkit_vector.svg)](https://dev.bukkit.org/projects/multiverse-core)
|
||||
[![Spigot](https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy/supported/spigot_vector.svg)](https://www.spigotmc.org/resources/multiverse-core.390/)
|
||||
|
||||
[![Maven CI/CD](https://github.com/Multiverse/Multiverse-Core/actions/workflows/build.yml/badge.svg)](https://github.com/Multiverse/Multiverse-Core/actions/workflows/build.yml)
|
||||
[![Release](https://img.shields.io/nexus/r/com.onarandombox.multiversecore/Multiverse-Core?label=release&server=https%3A%2F%2Frepo.onarandombox.com%2F)](https://dev.bukkit.org/projects/multiverse-core)
|
||||
[![Dev builds](https://img.shields.io/nexus/s/com.onarandombox.multiversecore/Multiverse-Core?label=dev%20builds&server=http%3A%2F%2Frepo.onarandombox.com%2F)](https://ci.onarandombox.com/job/Multiverse-Core/)
|
||||
[![Discord](https://img.shields.io/discord/325459248047980545?label=discord&logo=discord)](https://discord.gg/NZtfKky)
|
||||
[![Support me on Patreon](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fshieldsio-patreon.vercel.app%2Fapi%3Fusername%3Ddumptruckman%26type%3Dpatrons&style=flat)](https://patreon.com/dumptruckman)
|
||||
[![Support us on Patreon](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fshieldsio-patreon.vercel.app%2Fapi%3Fusername%3Ddumptruckman%26type%3Dpatrons&style=flat)](https://patreon.com/dumptruckman)
|
||||
[![License](https://img.shields.io/github/license/Multiverse/Multiverse-Core)](LICENSE.md)
|
||||
|
||||
About
|
||||
========
|
||||
|
||||
# About
|
||||
|
||||
[Multiverse](https://dev.bukkit.org/projects/multiverse-core) was created at the dawn of Bukkit multiworld support. It has since then grown into a **complete world management solution!** Multiverse provides the easiest to use world management solution for your Minecraft server, big or small, and with great addons like [Portals](https://dev.bukkit.org/projects/multiverse-portals) and [NetherPortals](https://dev.bukkit.org/projects/multiverse-netherportals/), what's not to love!
|
||||
<br><br>
|
||||
|
||||
Now it's time to create your very own Multiverse server, do check out our [Wiki](https://github.com/Multiverse/Multiverse-Core/wiki) and [Usage Guide](https://github.com/Multiverse/Multiverse-Core/wiki/Basics) to get started. Feel free to hop onto our [Discord](https://discord.gg/NZtfKky) if you have any question or just want to have a chat with us!
|
||||
|
||||
### Amazing sub-modules available:
|
||||
## Amazing sub-modules available:
|
||||
|
||||
* [Multiverse-NetherPortals](https://github.com/Multiverse/Multiverse-NetherPortals) -> Have separate nether and end worlds for each of your overworlds!
|
||||
* [Multiverse-Portals](https://github.com/Multiverse/Multiverse-Portals) -> Make custom portals to go to any destination!
|
||||
* [Multiverse-Inventories](https://github.com/Multiverse/Multiverse-Inventories) -> Have separated players stats and inventories per world or per group of worlds.
|
||||
* [Multiverse-SignPortals](https://github.com/Multiverse/Multiverse-SignPortals) -> Signs as teleprompters!
|
||||
* [Multiverse-SignPortals](https://github.com/Multiverse/Multiverse-SignPortals) -> Signs as teleporters!
|
||||
|
||||
Building
|
||||
========
|
||||
## Building
|
||||
Simply build the source with Gradle:
|
||||
```
|
||||
./gradlew build
|
||||
|
@ -28,17 +35,16 @@ Simply build the source with Gradle:
|
|||
More details are available on the [build instructions wiki page](https://github.com/Multiverse/Multiverse-Core/wiki/Building).
|
||||
|
||||
|
||||
Contributing
|
||||
=======
|
||||
## Contributing
|
||||
|
||||
**Want to help improve Multiverse?** There are several ways you can support and contribute to the project.
|
||||
* Take a look at our "Bug: Unconfirmed" issues, where you can find issues that need extra testing and investigation.
|
||||
* Want others to love Multiverse too? You can join the [Multiverse Discord community](https://discord.gg/NZtfKky) and help others with issues and setup!
|
||||
* A Multiverse guru? You can update our [Wiki](https://github.com/Multiverse/Multiverse-Core/wiki) with your latest tip, tricks and guides! The wiki open for all to edit and improve.
|
||||
* Love coding? You could look at ["State: Open to PR"](https://github.com/Multiverse/Multiverse-Core/labels/State%3A%20Open%20to%20PR) and ["Resolution: Accepted"](https://github.com/Multiverse/Multiverse-Core/labels/Resolution%3A%20Accepted) issues. We're always happy to receive bug fixes and feature additions as [pull requests](https://www.freecodecamp.org/news/how-to-make-your-first-pull-request-on-github-3/).
|
||||
* If you'd like to make a financial contribution to the project, do consider joining our [patreon](https://www.patreon.com/dumptruckman) or make a one-time donation [here](https://paypal.me/dumptruckman)!
|
||||
* If you'd like to make a financial contribution to the project, do consider joining our [Patreon](https://www.patreon.com/dumptruckman) or make a one-time donation [here](https://paypal.me/dumptruckman)!
|
||||
|
||||
Additionally, we would like to give a big thanks to everyone that has supported Multiverse over the years, as well as those in the years to come. Thank you!
|
||||
|
||||
License
|
||||
=======
|
||||
## License
|
||||
Multiverse-Core is licensed under BSD-3-Clause License. Please see [LICENSE.md](LICENSE.md) for more info.
|
||||
|
|
83
build.gradle
83
build.gradle
|
@ -62,50 +62,58 @@ repositories {
|
|||
}
|
||||
|
||||
configurations {
|
||||
// Add configuration for server API
|
||||
compileOnly.extendsFrom serverApi
|
||||
runtimeClasspath.extendsFrom serverApi
|
||||
|
||||
// Add configuration for external plugins
|
||||
implementation.extendsFrom externalPlugin
|
||||
|
||||
// Add configuration for dependencies that will be included in fat jar
|
||||
compileClasspath.extendsFrom shadowed
|
||||
testCompileClasspath.extendsFrom shadowed
|
||||
testRuntimeClasspath.extendsFrom shadowed
|
||||
oldTestCompileClasspath.extendsFrom shadowed
|
||||
oldTestRuntimeClasspath.extendsFrom shadowed
|
||||
|
||||
// Add configuration for old tests
|
||||
oldTestImplementation.extendsFrom implementation
|
||||
oldTestRuntime.extendsFrom runtime
|
||||
relocatedApi
|
||||
compileClasspath.extendsFrom relocatedApi
|
||||
runtimeClasspath.extendsFrom relocatedApi
|
||||
testCompileClasspath.extendsFrom relocatedApi
|
||||
testRuntimeClasspath.extendsFrom relocatedApi
|
||||
oldTestCompileClasspath.extendsFrom relocatedApi
|
||||
oldTestRuntimeClasspath.extendsFrom relocatedApi
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly 'org.spigotmc:spigot-api:1.16.5-R0.1-SNAPSHOT'
|
||||
serverApi 'org.spigotmc:spigot-api:1.16.5-R0.1-SNAPSHOT'
|
||||
|
||||
// Economy
|
||||
implementation('com.github.MilkBowl:VaultAPI:1.7.1') {
|
||||
externalPlugin('com.github.MilkBowl:VaultAPI:1.7.1') {
|
||||
exclude group: 'org.bukkit', module: 'bukkit'
|
||||
}
|
||||
|
||||
// PlaceholderAPI
|
||||
implementation 'me.clip:placeholderapi:2.11.3'
|
||||
externalPlugin 'me.clip:placeholderapi:2.11.3'
|
||||
|
||||
// Command Framework
|
||||
relocatedApi 'co.aikar:acf-paper:0.5.1-SNAPSHOT'
|
||||
shadowed 'co.aikar:acf-paper:0.5.1-SNAPSHOT'
|
||||
|
||||
// Config
|
||||
relocatedApi('me.main__.util:SerializationConfig:1.7') {
|
||||
shadowed('me.main__.util:SerializationConfig:1.7') {
|
||||
exclude group: 'org.bukkit', module: 'bukkit'
|
||||
}
|
||||
relocatedApi('io.github.townyadvanced.commentedconfiguration:CommentedConfiguration:1.0.1') {
|
||||
shadowed('io.github.townyadvanced.commentedconfiguration:CommentedConfiguration:1.0.1') {
|
||||
exclude group: 'org.spigotmc', module: 'spigot-api'
|
||||
}
|
||||
|
||||
// Utils
|
||||
relocatedApi 'io.vavr:vavr:0.10.4'
|
||||
relocatedApi 'org.glassfish.hk2:hk2-locator:3.0.3'
|
||||
relocatedApi('com.dumptruckman.minecraft:Logging:1.1.1') {
|
||||
shadowed 'io.vavr:vavr:0.10.4'
|
||||
shadowed 'org.glassfish.hk2:hk2-locator:3.0.3'
|
||||
shadowed('com.dumptruckman.minecraft:Logging:1.1.1') {
|
||||
exclude group: 'junit', module: 'junit'
|
||||
}
|
||||
relocatedApi 'de.themoep.idconverter:mappings:1.2-SNAPSHOT'
|
||||
relocatedApi 'org.bstats:bstats-bukkit:2.2.1'
|
||||
relocatedApi 'net.minidev:json-smart:2.4.8'
|
||||
relocatedApi 'org.jetbrains:annotations:22.0.0'
|
||||
relocatedApi 'io.papermc:paperlib:1.0.8'
|
||||
shadowed 'de.themoep.idconverter:mappings:1.2-SNAPSHOT'
|
||||
shadowed 'org.bstats:bstats-bukkit:2.2.1'
|
||||
shadowed 'net.minidev:json-smart:2.4.8'
|
||||
shadowed 'org.jetbrains:annotations:22.0.0'
|
||||
shadowed 'io.papermc:paperlib:1.0.8'
|
||||
|
||||
// Tests
|
||||
testImplementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.10'
|
||||
|
@ -170,6 +178,34 @@ publishing {
|
|||
publications {
|
||||
maven(MavenPublication) {
|
||||
from components.java
|
||||
|
||||
pom.withXml {
|
||||
Node pomNode = asNode()
|
||||
|
||||
// Remove Kotlin dependency
|
||||
pomNode.dependencies.'*'.findAll() {
|
||||
it.groupId.text() == 'org.jetbrains.kotlin'
|
||||
}.each() {
|
||||
it.parent().remove(it)
|
||||
}
|
||||
|
||||
// Switch runtime deps to provided
|
||||
pomNode.dependencies.'*'.findAll() {
|
||||
it.scope.text() == 'runtime'
|
||||
}.each() {
|
||||
it.scope*.value = 'provided'
|
||||
}
|
||||
|
||||
// Add spigot api to pom
|
||||
project.configurations.serverApi.allDependencies.each { dependency ->
|
||||
pomNode.dependencies[0].appendNode("dependency").with {
|
||||
it.appendNode("groupId", dependency.group)
|
||||
it.appendNode("artifactId", dependency.name)
|
||||
it.appendNode("version", dependency.version)
|
||||
it.appendNode("scope", "provided")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
|
@ -243,7 +279,7 @@ javadoc {
|
|||
project.configurations.api.canBeResolved = true
|
||||
|
||||
shadowJar {
|
||||
relocate 'co.aikar.commands', 'com.onarandombox.acf'
|
||||
relocate 'co.aikar', 'com.onarandombox.acf'
|
||||
relocate 'com.dumptruckman.minecraft.util.Logging', 'com.onarandombox.MultiverseCore.utils.CoreLogging'
|
||||
relocate 'com.dumptruckman.minecraft.util.DebugLog', 'com.onarandombox.MultiverseCore.utils.DebugFileLogger'
|
||||
relocate 'de.themoep.idconverter', 'com.onarandombox.idconverter'
|
||||
|
@ -263,7 +299,7 @@ shadowJar {
|
|||
relocate 'org.jetbrains', 'com.onarandombox.jetbrains'
|
||||
relocate 'io.papermc.lib', 'com.onarandombox.paperlib'
|
||||
|
||||
configurations = [project.configurations.api, project.configurations.relocatedApi]
|
||||
configurations = [project.configurations.shadowed]
|
||||
|
||||
archiveFileName = "$baseName-$version.$extension"
|
||||
|
||||
|
@ -272,6 +308,7 @@ shadowJar {
|
|||
it.moduleGroup == 'org.jetbrains.kotlin'
|
||||
})
|
||||
}
|
||||
//classifier = ''
|
||||
}
|
||||
|
||||
build.dependsOn shadowJar
|
||||
|
|
|
@ -2,4 +2,10 @@
|
|||
* This file was generated by the Gradle 'init' task.
|
||||
*/
|
||||
|
||||
pluginManagement {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = 'multiverse-core'
|
||||
|
|
|
@ -25,6 +25,7 @@ import com.onarandombox.MultiverseCore.placeholders.MultiverseCorePlaceholders;
|
|||
import com.onarandombox.MultiverseCore.utils.TestingMode;
|
||||
import com.onarandombox.MultiverseCore.utils.metrics.MetricsConfigurator;
|
||||
import com.onarandombox.MultiverseCore.world.WorldProperties;
|
||||
import com.onarandombox.MultiverseCore.worldnew.WorldManager;
|
||||
import io.vavr.control.Try;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Provider;
|
||||
|
@ -54,10 +55,12 @@ public class MultiverseCore extends JavaPlugin implements MVCore {
|
|||
|
||||
private ServiceLocator serviceLocator;
|
||||
@Inject
|
||||
private MVCoreConfig config;
|
||||
private Provider<MVCoreConfig> configProvider;
|
||||
@Inject
|
||||
private Provider<MVWorldManager> worldManagerProvider;
|
||||
@Inject
|
||||
private Provider<WorldManager> newWorldManagerProvider;
|
||||
@Inject
|
||||
private Provider<AnchorManager> anchorManagerProvider;
|
||||
@Inject
|
||||
private Provider<MVCommandManager> commandManagerProvider;
|
||||
|
@ -101,7 +104,8 @@ public class MultiverseCore extends JavaPlugin implements MVCore {
|
|||
initializeDependencyInjection();
|
||||
|
||||
// Load our configs first as we need them for everything else.
|
||||
if (config == null || !config.isLoaded()) {
|
||||
var config = configProvider.get();
|
||||
if (!config.isLoaded()) {
|
||||
Logging.severe("Your configs were not loaded.");
|
||||
Logging.severe("Please check your configs and restart the server.");
|
||||
this.getServer().getPluginManager().disablePlugin(this);
|
||||
|
@ -124,6 +128,9 @@ public class MultiverseCore extends JavaPlugin implements MVCore {
|
|||
config.setFirstSpawnLocation(firstSpawnWorld.getName());
|
||||
}
|
||||
|
||||
var newWorldManager = newWorldManagerProvider.get();
|
||||
newWorldManager.loadAllWorlds(); // TODO: Possibly move this to constructor of WorldManager
|
||||
|
||||
//Setup economy here so vault is loaded
|
||||
this.loadEconomist();
|
||||
|
||||
|
@ -169,7 +176,7 @@ public class MultiverseCore extends JavaPlugin implements MVCore {
|
|||
}
|
||||
|
||||
private boolean shouldShowConfig() {
|
||||
return !config.getSilentStart();
|
||||
return !configProvider.get().getSilentStart();
|
||||
}
|
||||
|
||||
private void loadEconomist() {
|
||||
|
@ -273,14 +280,14 @@ public class MultiverseCore extends JavaPlugin implements MVCore {
|
|||
private void logEnableMessage() {
|
||||
Logging.config("Version %s (API v%s) Enabled - By %s", this.getDescription().getVersion(), PROTOCOL, getAuthors());
|
||||
|
||||
if (config.isShowingDonateMessage()) {
|
||||
if (configProvider.get().isShowingDonateMessage()) {
|
||||
Logging.config("Help dumptruckman keep this project alive. Become a patron! https://www.patreon.com/dumptruckman");
|
||||
Logging.config("One time donations are also appreciated: https://www.paypal.me/dumptruckman");
|
||||
}
|
||||
}
|
||||
|
||||
private void loadPlaceholderAPIIntegration() {
|
||||
if (config.isRegisterPapiHook()
|
||||
if (configProvider.get().isRegisterPapiHook()
|
||||
&& getServer().getPluginManager().getPlugin("PlaceholderAPI") != null) {
|
||||
Try.run(() -> serviceLocator.createAndInitialize(MultiverseCorePlaceholders.class))
|
||||
.onFailure(e -> {
|
||||
|
@ -367,7 +374,7 @@ public class MultiverseCore extends JavaPlugin implements MVCore {
|
|||
*/
|
||||
@Override
|
||||
public boolean saveAllConfigs() {
|
||||
return config.save()
|
||||
return configProvider.get().save()
|
||||
&& worldManagerProvider.get().saveWorldsConfig()
|
||||
&& anchorManagerProvider.get().saveAnchors();
|
||||
}
|
||||
|
|
|
@ -36,13 +36,13 @@ public class CloneCommand extends MultiverseCommand {
|
|||
@Description("{@@mv-core.clone.description}")
|
||||
public void onCloneCommand(CommandIssuer issuer,
|
||||
|
||||
@Conditions("validWorldName:scope=both")
|
||||
@Conditions("worldname:scope=both")
|
||||
@Syntax("<world>")
|
||||
@Description("{@@mv-core.clone.world.description}")
|
||||
String worldName,
|
||||
|
||||
@Single
|
||||
@Conditions("validWorldName:scope=new")
|
||||
@Conditions("worldname:scope=new")
|
||||
@Syntax("<new world name>")
|
||||
@Description("{@@mv-core.clone.newWorld.description}")
|
||||
String newWorldName
|
||||
|
|
|
@ -67,22 +67,8 @@ public class CreateCommand extends MultiverseCommand {
|
|||
.map(genplugin -> genplugin.getDescription().getName())
|
||||
.collect(Collectors.toList()))
|
||||
.build())
|
||||
.add(CommandValueFlag.builder("--world-type", WorldType.class)
|
||||
.add(CommandValueFlag.enumBuilder("--world-type", WorldType.class)
|
||||
.addAlias("-t")
|
||||
.context((value) -> {
|
||||
try {
|
||||
return WorldType.valueOf(value.toUpperCase());
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new InvalidCommandArgument("Invalid world type: " + value);
|
||||
}
|
||||
})
|
||||
.completion(() -> {
|
||||
List<String> types = new ArrayList<>();
|
||||
for (WorldType type : WorldType.values()) {
|
||||
types.add(type.name().toLowerCase());
|
||||
}
|
||||
return types;
|
||||
})
|
||||
.build())
|
||||
.add(CommandFlag.builder("--adjust-spawn")
|
||||
.addAlias("-n")
|
||||
|
@ -100,7 +86,7 @@ public class CreateCommand extends MultiverseCommand {
|
|||
@Description("{@@mv-core.create.description}")
|
||||
public void onCreateCommand(BukkitCommandIssuer issuer,
|
||||
|
||||
@Conditions("validWorldName:scope=new")
|
||||
@Conditions("worldname:scope=new")
|
||||
@Syntax("<name>")
|
||||
@Description("{@@mv-core.create.name.description}")
|
||||
String worldName,
|
||||
|
|
|
@ -39,7 +39,7 @@ public class DeleteCommand extends MultiverseCommand {
|
|||
public void onDeleteCommand(BukkitCommandIssuer issuer,
|
||||
|
||||
@Single
|
||||
@Conditions("validWorldName:scope=both")
|
||||
@Conditions("worldname:scope=both")
|
||||
@Syntax("<world>")
|
||||
@Description("The world you want to delete.")
|
||||
String worldName
|
||||
|
|
|
@ -69,7 +69,7 @@ public class ImportCommand extends MultiverseCommand {
|
|||
@Description("{@@mv-core.import.description")
|
||||
public void onImportCommand(BukkitCommandIssuer issuer,
|
||||
|
||||
@Conditions("validWorldName:scope=new")
|
||||
@Conditions("worldname:scope=new")
|
||||
@Syntax("<name>")
|
||||
@Description("{@@mv-core.import.name.description}")
|
||||
String worldName,
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
package com.onarandombox.MultiverseCore.commands;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import co.aikar.commands.BukkitCommandIssuer;
|
||||
import co.aikar.commands.InvalidCommandArgument;
|
||||
import co.aikar.commands.annotation.CommandAlias;
|
||||
import co.aikar.commands.annotation.CommandCompletion;
|
||||
import co.aikar.commands.annotation.CommandPermission;
|
||||
import co.aikar.commands.annotation.Description;
|
||||
import co.aikar.commands.annotation.Subcommand;
|
||||
import co.aikar.commands.annotation.Syntax;
|
||||
import com.onarandombox.MultiverseCore.MultiverseCore;
|
||||
import com.onarandombox.MultiverseCore.api.MVWorld;
|
||||
import com.onarandombox.MultiverseCore.api.MVWorldManager;
|
||||
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
|
||||
import com.onarandombox.MultiverseCore.commandtools.MultiverseCommand;
|
||||
import com.onarandombox.MultiverseCore.commandtools.flags.CommandFlagGroup;
|
||||
import com.onarandombox.MultiverseCore.commandtools.flags.CommandValueFlag;
|
||||
import com.onarandombox.MultiverseCore.commandtools.flags.ParsedCommandFlags;
|
||||
import com.onarandombox.MultiverseCore.display.ContentDisplay;
|
||||
import com.onarandombox.MultiverseCore.display.filters.ContentFilter;
|
||||
import com.onarandombox.MultiverseCore.display.filters.DefaultContentFilter;
|
||||
import com.onarandombox.MultiverseCore.display.filters.RegexContentFilter;
|
||||
import com.onarandombox.MultiverseCore.display.handlers.PagedSendHandler;
|
||||
import com.onarandombox.MultiverseCore.display.parsers.ListContentProvider;
|
||||
import com.onarandombox.MultiverseCore.utils.UnsafeCallWrapper;
|
||||
import com.onarandombox.MultiverseCore.world.entrycheck.WorldEntryCheckerProvider;
|
||||
import jakarta.inject.Inject;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jvnet.hk2.annotations.Service;
|
||||
|
||||
@Service
|
||||
@CommandAlias("mv")
|
||||
public class ListCommand extends MultiverseCommand {
|
||||
|
||||
private final MVWorldManager worldManager;
|
||||
private final WorldEntryCheckerProvider worldEntryCheckerProvider;
|
||||
|
||||
@Inject
|
||||
public ListCommand(
|
||||
@NotNull MVCommandManager commandManager,
|
||||
@NotNull MVWorldManager worldManager,
|
||||
@NotNull WorldEntryCheckerProvider worldEntryCheckerProvider
|
||||
) {
|
||||
super(commandManager);
|
||||
this.worldManager = worldManager;
|
||||
this.worldEntryCheckerProvider = worldEntryCheckerProvider;
|
||||
|
||||
registerFlagGroup(CommandFlagGroup.builder("mvlist")
|
||||
.add(CommandValueFlag.builder("--filter", ContentFilter.class)
|
||||
.addAlias("-f")
|
||||
.context((value) -> {
|
||||
try {
|
||||
return RegexContentFilter.fromString(value);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new InvalidCommandArgument("Invalid filter: " + value);
|
||||
}
|
||||
})
|
||||
.build())
|
||||
.add(CommandValueFlag.builder("--page", Integer.class)
|
||||
.addAlias("-p")
|
||||
.context((value) -> {
|
||||
try {
|
||||
return Integer.parseInt(value);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new InvalidCommandArgument("Invalid page number: " + value);
|
||||
}
|
||||
})
|
||||
.build())
|
||||
.build());
|
||||
}
|
||||
|
||||
@Subcommand("list")
|
||||
@CommandPermission("multiverse.core.list.worlds")
|
||||
@CommandCompletion("@flags:groupName=mvlist")
|
||||
@Syntax("--filter [filter] --page [page]")
|
||||
@Description("Displays a listing of all worlds that you can enter.")
|
||||
public void onListCommand(BukkitCommandIssuer issuer,
|
||||
|
||||
@Syntax("--filter [filter] --page [page]")
|
||||
@Description("Filters the list of worlds by the given regex and displays the given page.")
|
||||
String[] flags
|
||||
) {
|
||||
ParsedCommandFlags parsedFlags = parseFlags(flags);
|
||||
ContentDisplay.create()
|
||||
.addContent(ListContentProvider.forContent(getListContents(issuer)))
|
||||
.withSendHandler(PagedSendHandler.create()
|
||||
.withHeader("%s====[ Multiverse World List ]====", ChatColor.GOLD)
|
||||
.withTargetPage(parsedFlags.flagValue("--page", 1, Integer.class))
|
||||
.withFilter(parsedFlags.flagValue("--filter", DefaultContentFilter.get(), ContentFilter.class))
|
||||
.withLinesPerPage(4)) //TODO Change back after testing
|
||||
.send(issuer);
|
||||
}
|
||||
|
||||
private List<String> getListContents(BukkitCommandIssuer issuer) {
|
||||
Player player = issuer.isPlayer() ? issuer.getPlayer() : null;
|
||||
List<String> worldList = new ArrayList<>();
|
||||
|
||||
worldManager.getMVWorlds().stream()
|
||||
.filter(world -> player == null || worldEntryCheckerProvider.forSender(player).canAccessWorld(world).isSuccess())
|
||||
.filter(world -> canSeeWorld(player, world))
|
||||
.map(world -> hiddenText(world) + world.getColoredWorldString() + " - " + parseColouredEnvironment(world.getEnvironment()))
|
||||
.sorted()
|
||||
.forEach(worldList::add);
|
||||
|
||||
worldManager.getUnloadedWorlds().stream()
|
||||
.filter(world -> issuer.hasPermission("multiverse.access." + world)) // TODO: Refactor stray permission check
|
||||
.map(world -> ChatColor.GRAY + world + " - UNLOADED")
|
||||
.sorted()
|
||||
.forEach(worldList::add);
|
||||
|
||||
return worldList;
|
||||
}
|
||||
|
||||
private boolean canSeeWorld(Player player, MVWorld world) {
|
||||
return !world.isHidden()
|
||||
|| player == null
|
||||
|| player.hasPermission("multiverse.core.modify"); // TODO: Refactor stray permission check
|
||||
}
|
||||
|
||||
private String hiddenText(MVWorld world) {
|
||||
return (world.isHidden()) ? String.format("%s[H] ", ChatColor.GRAY) : "";
|
||||
}
|
||||
|
||||
private String parseColouredEnvironment(World.Environment env) {
|
||||
ChatColor color = ChatColor.GOLD;
|
||||
switch (env) {
|
||||
case NETHER:
|
||||
color = ChatColor.RED;
|
||||
break;
|
||||
case NORMAL:
|
||||
color = ChatColor.GREEN;
|
||||
break;
|
||||
case THE_END:
|
||||
color = ChatColor.AQUA;
|
||||
break;
|
||||
}
|
||||
return color + env.toString();
|
||||
}
|
||||
}
|
|
@ -37,7 +37,7 @@ public class LoadCommand extends MultiverseCommand {
|
|||
public void onLoadCommand(BukkitCommandIssuer issuer,
|
||||
|
||||
@Single
|
||||
@Conditions("validWorldName:scope=unloaded")
|
||||
@Conditions("worldname:scope=unloaded")
|
||||
@Syntax("<world>")
|
||||
@Description("{@@mv-core.load.world.description}")
|
||||
String worldName
|
||||
|
|
|
@ -56,7 +56,7 @@ public class RegenCommand extends MultiverseCommand {
|
|||
@Description("{@@mv-core.regen.description}")
|
||||
public void onRegenCommand(BukkitCommandIssuer issuer,
|
||||
|
||||
@Conditions("validWorldName:scope=both")
|
||||
@Conditions("worldname:scope=both")
|
||||
@Syntax("<world>")
|
||||
@Description("{@@mv-core.regen.world.description}")
|
||||
String worldName,
|
||||
|
|
|
@ -11,6 +11,7 @@ import co.aikar.commands.annotation.Description;
|
|||
import co.aikar.commands.annotation.Flags;
|
||||
import co.aikar.commands.annotation.Subcommand;
|
||||
import co.aikar.commands.annotation.Syntax;
|
||||
import com.dumptruckman.minecraft.util.Logging;
|
||||
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
|
||||
import com.onarandombox.MultiverseCore.commandtools.MultiverseCommand;
|
||||
import com.onarandombox.MultiverseCore.destination.DestinationsProvider;
|
||||
|
@ -49,17 +50,17 @@ public class TeleportCommand extends MultiverseCommand {
|
|||
) {
|
||||
// TODO Add warning if teleporting too many players at once.
|
||||
|
||||
String playerName = players.length == 1
|
||||
? issuer.getPlayer() == players[0] ? "you" : players[0].getName()
|
||||
: players.length + " players";
|
||||
|
||||
issuer.sendInfo(MVCorei18n.TELEPORT_SUCCESS,
|
||||
"{player}", playerName, "{destination}", destination.toString());
|
||||
|
||||
CompletableFuture.allOf(Arrays.stream(players)
|
||||
.map(player -> this.destinationsProvider.playerTeleportAsync(issuer, player, destination))
|
||||
.toArray(CompletableFuture[]::new))
|
||||
.thenRun(() -> {
|
||||
String playerName = players.length == 1
|
||||
? issuer.getPlayer() == players[0] ? "you" : players[0].getName()
|
||||
: players.length + " players";
|
||||
|
||||
issuer.sendInfo(MVCorei18n.TELEPORT_SUCCESS,
|
||||
"{player}", playerName, "{destination}", destination.toString());
|
||||
});
|
||||
.thenRun(() -> Logging.finer("Async teleport completed."));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -8,7 +8,6 @@ import co.aikar.commands.ConditionContext;
|
|||
import co.aikar.commands.ConditionFailedException;
|
||||
import com.onarandombox.MultiverseCore.api.MVWorldManager;
|
||||
import com.onarandombox.MultiverseCore.world.WorldNameChecker;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class MVCommandConditions {
|
||||
|
@ -22,19 +21,19 @@ public class MVCommandConditions {
|
|||
private MVCommandConditions(@NotNull MVCommandManager commandManager, @NotNull MVWorldManager worldManager) {
|
||||
this.worldManager = worldManager;
|
||||
this.commandManager = commandManager;
|
||||
registerConditions();
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
private void registerConditions() {
|
||||
CommandConditions<BukkitCommandIssuer, BukkitCommandExecutionContext, BukkitConditionContext> conditions
|
||||
= commandManager.getCommandConditions();
|
||||
|
||||
conditions.addCondition(String.class, "validWorldName", this::checkValidWorldName);
|
||||
conditions.addCondition(String.class, "worldname", this::checkWorldname);
|
||||
}
|
||||
|
||||
private void checkValidWorldName(ConditionContext<BukkitCommandIssuer> context,
|
||||
BukkitCommandExecutionContext executionContext,
|
||||
String worldName
|
||||
private void checkWorldname(ConditionContext<BukkitCommandIssuer> context,
|
||||
BukkitCommandExecutionContext executionContext,
|
||||
String worldName
|
||||
) {
|
||||
String scope = context.getConfigValue("scope", "loaded");
|
||||
|
||||
|
|
|
@ -26,11 +26,12 @@ public class CommandFlag {
|
|||
/**
|
||||
* Creates a new flag.
|
||||
*
|
||||
* @param builder The builder.
|
||||
* @param key The key for the new flag.
|
||||
* @param aliases The aliases that also refer to this flag.
|
||||
*/
|
||||
protected CommandFlag(@NotNull Builder<?> builder) {
|
||||
key = builder.key;
|
||||
aliases = builder.aliases;
|
||||
protected CommandFlag(@NotNull String key, @NotNull List<String> aliases) {
|
||||
this.key = key;
|
||||
this.aliases = aliases;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -57,8 +58,8 @@ public class CommandFlag {
|
|||
* @param <S> The type of the builder.
|
||||
*/
|
||||
public static class Builder<S extends Builder<?>> {
|
||||
private final String key;
|
||||
private final List<String> aliases;
|
||||
protected final String key;
|
||||
protected final List<String> aliases;
|
||||
|
||||
/**
|
||||
* Create a new builder.
|
||||
|
@ -87,7 +88,7 @@ public class CommandFlag {
|
|||
* @return The flag.
|
||||
*/
|
||||
public @NotNull CommandFlag build(){
|
||||
return new CommandFlag(this);
|
||||
return new CommandFlag(key, aliases);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
package com.onarandombox.MultiverseCore.commandtools.flags;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import co.aikar.commands.InvalidCommandArgument;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Represents a flag with a value.
|
||||
*
|
||||
|
@ -16,14 +20,25 @@ public class CommandValueFlag<T> extends CommandFlag {
|
|||
/**
|
||||
* A builder for a flag.
|
||||
*
|
||||
* @param key The key for the new flag.
|
||||
* @param type The type of the value.
|
||||
* @param key The key for the new flag.
|
||||
* @param type The type of the value.
|
||||
* @return The builder.
|
||||
*/
|
||||
public static @NotNull <T> Builder<T, ?> builder(@NotNull String key, @NotNull Class<T> type) {
|
||||
return new Builder<>(key, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* A builder for a flag with enum value.
|
||||
*
|
||||
* @param key The key for the new flag.
|
||||
* @param type The type of the value, must be enum.
|
||||
* @return The builder.
|
||||
*/
|
||||
public static @NotNull <T extends Enum<T>> EnumBuilder<T, ?> enumBuilder(@NotNull String key, @NotNull Class<T> type) {
|
||||
return new EnumBuilder<>(key, type);
|
||||
}
|
||||
|
||||
private final Class<T> type;
|
||||
private final boolean optional;
|
||||
private final T defaultValue;
|
||||
|
@ -33,15 +48,29 @@ public class CommandValueFlag<T> extends CommandFlag {
|
|||
/**
|
||||
* Creates a new flag.
|
||||
*
|
||||
* @param builder The builder.
|
||||
* @param key The key for the new flag.
|
||||
* @param aliases The aliases that also refer to this flag.
|
||||
* @param type The type of the value.
|
||||
* @param optional Allow for flag without value.
|
||||
* @param defaultValue The default value if optional is true and user does not specify a value.
|
||||
* @param context Function to parse string into value type.
|
||||
* @param completion Function to get completion for this flag.
|
||||
*/
|
||||
protected CommandValueFlag(@NotNull Builder<T, ?> builder) {
|
||||
super(builder);
|
||||
type = builder.type;
|
||||
optional = builder.optional;
|
||||
defaultValue = builder.defaultValue;
|
||||
context = builder.context;
|
||||
completion = builder.completion;
|
||||
protected CommandValueFlag(
|
||||
@NotNull String key,
|
||||
@NotNull List<String> aliases,
|
||||
@NotNull Class<T> type,
|
||||
boolean optional,
|
||||
@Nullable T defaultValue,
|
||||
@Nullable Function<String, T> context,
|
||||
@Nullable Supplier<Collection<String>> completion
|
||||
) {
|
||||
super(key, aliases);
|
||||
this.type = type;
|
||||
this.optional = optional;
|
||||
this.defaultValue = defaultValue;
|
||||
this.context = context;
|
||||
this.completion = completion;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -96,11 +125,11 @@ public class CommandValueFlag<T> extends CommandFlag {
|
|||
* @param <S> The type of the builder.
|
||||
*/
|
||||
public static class Builder<T, S extends Builder<T, S>> extends CommandFlag.Builder<S> {
|
||||
private final Class<T> type;
|
||||
private boolean optional = false;
|
||||
private T defaultValue = null;
|
||||
private Function<String, T> context = null;
|
||||
private Supplier<Collection<String>> completion = null;
|
||||
protected final Class<T> type;
|
||||
protected boolean optional = false;
|
||||
protected T defaultValue = null;
|
||||
protected Function<String, T> context = null;
|
||||
protected Supplier<Collection<String>> completion = null;
|
||||
|
||||
/**
|
||||
* Create a new builder.
|
||||
|
@ -166,7 +195,77 @@ public class CommandValueFlag<T> extends CommandFlag {
|
|||
if (context == null && !String.class.equals(type)) {
|
||||
throw new IllegalStateException("Context is required for none-string value flags");
|
||||
}
|
||||
return new CommandValueFlag<>(this);
|
||||
return new CommandValueFlag<>(key, aliases, type, optional, defaultValue, context, completion);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Specific builder for a flag with enum value.
|
||||
*
|
||||
* @param <T> The type of the value.
|
||||
* @param <S> The type of the builder.
|
||||
*/
|
||||
public static class EnumBuilder<T extends Enum<T>, S extends EnumBuilder<T, S>> extends CommandFlag.Builder<S> {
|
||||
protected final Class<T> type;
|
||||
protected boolean optional = false;
|
||||
protected T defaultValue = null;
|
||||
protected Function<String, T> context = null;
|
||||
protected Supplier<Collection<String>> completion = null;
|
||||
|
||||
public EnumBuilder(@NotNull String key, @NotNull Class<T> type) {
|
||||
super(key);
|
||||
this.type = type;
|
||||
setEnumContext();
|
||||
setEnumCompletion();
|
||||
}
|
||||
|
||||
private void setEnumContext() {
|
||||
this.context = (String value) -> {
|
||||
try {
|
||||
return Enum.valueOf(type, value.toUpperCase());
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new InvalidCommandArgument("Invalid value for argument " + key + ": " + value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void setEnumCompletion() {
|
||||
List<String> types = Arrays.stream(type.getEnumConstants())
|
||||
.map(type -> type.name().toLowerCase())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
this.completion = () -> types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the flag as optional for users to specify a value.
|
||||
*
|
||||
* @return The builder.
|
||||
*/
|
||||
public @NotNull S optional() {
|
||||
this.optional = true;
|
||||
return (S) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default value. Used if optional is true and user does not specify a value.
|
||||
*
|
||||
* @param defaultValue The default value.
|
||||
* @return The builder.
|
||||
*/
|
||||
public @NotNull S defaultValue(@NotNull T defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
return (S) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the flag.
|
||||
*
|
||||
* @return The flag.
|
||||
*/
|
||||
@Override
|
||||
public @NotNull CommandFlag build() {
|
||||
return new CommandValueFlag<>(key, aliases, type, optional, defaultValue, context, completion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ abstract class FileConfigHandle<C extends FileConfiguration> extends GenericConf
|
|||
protected final @NotNull Path configPath;
|
||||
protected final @NotNull File configFile;
|
||||
|
||||
|
||||
protected FileConfigHandle(@NotNull Path configPath, @Nullable Logger logger, @Nullable NodeGroup nodes, @Nullable ConfigMigrator migrator) {
|
||||
super(logger, nodes, migrator);
|
||||
this.configPath = configPath;
|
||||
|
@ -107,9 +106,6 @@ abstract class FileConfigHandle<C extends FileConfiguration> extends GenericConf
|
|||
public static abstract class Builder<C extends FileConfiguration, B extends Builder<C, B>> extends GenericConfigHandle.Builder<C, B> {
|
||||
|
||||
protected @NotNull Path configPath;
|
||||
protected @Nullable Logger logger;
|
||||
protected @Nullable NodeGroup nodes;
|
||||
protected @Nullable ConfigMigrator migrator;
|
||||
|
||||
protected Builder(@NotNull Path configPath) {
|
||||
this.configPath = configPath;
|
||||
|
|
|
@ -3,7 +3,9 @@ package com.onarandombox.MultiverseCore.configuration.handle;
|
|||
import com.onarandombox.MultiverseCore.configuration.migration.ConfigMigrator;
|
||||
import com.onarandombox.MultiverseCore.configuration.node.ConfigNodeNotFoundException;
|
||||
import com.onarandombox.MultiverseCore.configuration.node.NodeGroup;
|
||||
import com.onarandombox.MultiverseCore.configuration.node.NodeSerializer;
|
||||
import com.onarandombox.MultiverseCore.configuration.node.ValueNode;
|
||||
import io.vavr.control.Option;
|
||||
import io.vavr.control.Try;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
@ -58,7 +60,7 @@ public abstract class GenericConfigHandle<C extends ConfigurationSection> {
|
|||
|
||||
nodes.forEach(node -> {
|
||||
if (node instanceof ValueNode valueNode) {
|
||||
set(valueNode, config.getObject(valueNode.getPath(), valueNode.getType(), valueNode.getDefaultValue()));
|
||||
set(valueNode, get(valueNode));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -81,7 +83,10 @@ public abstract class GenericConfigHandle<C extends ConfigurationSection> {
|
|||
* @return The value of the node.
|
||||
*/
|
||||
public <T> T get(@NotNull ValueNode<T> node) {
|
||||
return config.getObject(node.getPath(), node.getType(), node.getDefaultValue());
|
||||
if (node.getSerializer() == null) {
|
||||
return config.getObject(node.getPath(), node.getType(), node.getDefaultValue());
|
||||
}
|
||||
return node.getSerializer().deserialize(config.get(node.getPath(), node.getDefaultValue()), node.getType());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -108,7 +113,12 @@ public abstract class GenericConfigHandle<C extends ConfigurationSection> {
|
|||
public <T> Try<Void> set(@NotNull ValueNode<T> node, T value) {
|
||||
return node.validate(value).map(ignore -> {
|
||||
T oldValue = get(node);
|
||||
config.set(node.getPath(), value);
|
||||
if (node.getSerializer() != null) {
|
||||
var serialized = node.getSerializer().serialize(value, node.getType());
|
||||
config.set(node.getPath(), serialized);
|
||||
} else {
|
||||
config.set(node.getPath(), value);
|
||||
}
|
||||
node.onSetValue(oldValue, get(node));
|
||||
return null;
|
||||
});
|
||||
|
|
|
@ -32,6 +32,7 @@ public class ConfigNode<T> extends ConfigHeaderNode implements ValueNode<T> {
|
|||
protected final @Nullable String name;
|
||||
protected final @NotNull Class<T> type;
|
||||
protected final @Nullable T defaultValue;
|
||||
protected final @Nullable NodeSerializer<T> serializer;
|
||||
protected final @Nullable Function<T, Try<Void>> validator;
|
||||
protected final @Nullable BiConsumer<T, T> onSetValue;
|
||||
|
||||
|
@ -41,6 +42,7 @@ public class ConfigNode<T> extends ConfigHeaderNode implements ValueNode<T> {
|
|||
@Nullable String name,
|
||||
@NotNull Class<T> type,
|
||||
@Nullable T defaultValue,
|
||||
@Nullable NodeSerializer<T> serializer,
|
||||
@Nullable Function<T, Try<Void>> validator,
|
||||
@Nullable BiConsumer<T, T> onSetValue
|
||||
) {
|
||||
|
@ -48,6 +50,7 @@ public class ConfigNode<T> extends ConfigHeaderNode implements ValueNode<T> {
|
|||
this.name = name;
|
||||
this.type = type;
|
||||
this.defaultValue = defaultValue;
|
||||
this.serializer = serializer;
|
||||
this.validator = validator;
|
||||
this.onSetValue = onSetValue;
|
||||
}
|
||||
|
@ -76,6 +79,10 @@ public class ConfigNode<T> extends ConfigHeaderNode implements ValueNode<T> {
|
|||
return defaultValue;
|
||||
}
|
||||
|
||||
public @Nullable NodeSerializer<T> getSerializer() {
|
||||
return serializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
@ -104,10 +111,12 @@ public class ConfigNode<T> extends ConfigHeaderNode implements ValueNode<T> {
|
|||
* @param <B> The type of the builder.
|
||||
*/
|
||||
public static class Builder<T, B extends ConfigNode.Builder<T, B>> extends ConfigHeaderNode.Builder<B> {
|
||||
private static final NodeSerializer<?> ENUM_NODE_SERIALIZER = new EnumNodeSerializer<>();
|
||||
|
||||
protected @Nullable String name;
|
||||
protected @NotNull final Class<T> type;
|
||||
protected @Nullable T defaultValue;
|
||||
protected @Nullable NodeSerializer<T> serializer;
|
||||
protected @Nullable Function<T, Try<Void>> validator;
|
||||
protected @Nullable BiConsumer<T, T> onSetValue;
|
||||
|
||||
|
@ -121,6 +130,9 @@ public class ConfigNode<T> extends ConfigHeaderNode implements ValueNode<T> {
|
|||
super(path);
|
||||
this.name = path;
|
||||
this.type = type;
|
||||
if (type.isEnum()) {
|
||||
this.serializer = (NodeSerializer<T>) ENUM_NODE_SERIALIZER;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -145,6 +157,11 @@ public class ConfigNode<T> extends ConfigHeaderNode implements ValueNode<T> {
|
|||
return (B) this;
|
||||
}
|
||||
|
||||
public @NotNull B serializer(@NotNull NodeSerializer<T> serializer) {
|
||||
this.serializer = serializer;
|
||||
return (B) this;
|
||||
}
|
||||
|
||||
public @NotNull B validator(@NotNull Function<T, Try<Void>> validator) {
|
||||
this.validator = validator;
|
||||
return (B) this;
|
||||
|
@ -166,7 +183,7 @@ public class ConfigNode<T> extends ConfigHeaderNode implements ValueNode<T> {
|
|||
*/
|
||||
@Override
|
||||
public @NotNull ConfigNode<T> build() {
|
||||
return new ConfigNode<>(path, comments.toArray(new String[0]), name, type, defaultValue, validator, onSetValue);
|
||||
return new ConfigNode<>(path, comments.toArray(new String[0]), name, type, defaultValue, serializer, validator, onSetValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package com.onarandombox.MultiverseCore.configuration.node;
|
||||
|
||||
import com.dumptruckman.minecraft.util.Logging;
|
||||
|
||||
public class EnumNodeSerializer<T extends Enum<T>> implements NodeSerializer<T> {
|
||||
|
||||
@Override
|
||||
public T deserialize(Object object, Class<T> type) {
|
||||
return Enum.valueOf(type, object.toString().toUpperCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object serialize(T object, Class<T> type) {
|
||||
return object.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package com.onarandombox.MultiverseCore.configuration.node;
|
||||
|
||||
public interface NodeSerializer<T> {
|
||||
T deserialize(Object object, Class<T> type);
|
||||
Object serialize(T object, Class<T> type);
|
||||
}
|
|
@ -28,6 +28,13 @@ public interface ValueNode<T> extends Node {
|
|||
*/
|
||||
@Nullable T getDefaultValue();
|
||||
|
||||
/**
|
||||
* Gets the serializer for this node.
|
||||
*
|
||||
* @return The serializer for this node.
|
||||
*/
|
||||
@Nullable NodeSerializer<T> getSerializer();
|
||||
|
||||
/**
|
||||
* Validates the value of this node.
|
||||
*
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.onarandombox.MultiverseCore.economy;
|
||||
|
||||
import com.onarandombox.MultiverseCore.api.MVWorld;
|
||||
import jakarta.inject.Inject;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
|
@ -91,6 +92,35 @@ public class MVEconomist {
|
|||
return "Sorry, you don't have enough " + (isItemCurrency(currency) ? "items" : "funds") + ". " + message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pays for a given amount of currency either from the player's economy account or inventory if the currency.
|
||||
*
|
||||
* @param player the player to deposit currency into.
|
||||
* @param world the world to take entry fee from.
|
||||
*/
|
||||
public void payEntryFee(Player player, MVWorld world) {
|
||||
payEntryFee(player, world.getPrice(), world.getCurrency());
|
||||
}
|
||||
|
||||
/**
|
||||
* Pays for a given amount of currency either from the player's economy account or inventory if the currency
|
||||
*
|
||||
* @param player the player to take currency from.
|
||||
* @param price the amount to take.
|
||||
* @param currency the type of currency.
|
||||
*/
|
||||
public void payEntryFee(Player player, double price, Material currency) {
|
||||
if (price == 0D) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (price < 0) {
|
||||
this.deposit(player, -price, currency);
|
||||
} else {
|
||||
this.withdraw(player, price, currency);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deposits a given amount of currency either into the player's economy account or inventory if the currency
|
||||
* is not null.
|
||||
|
|
|
@ -16,12 +16,17 @@ import com.onarandombox.MultiverseCore.MultiverseCore;
|
|||
import com.onarandombox.MultiverseCore.api.MVWorld;
|
||||
import com.onarandombox.MultiverseCore.api.MVWorldManager;
|
||||
import com.onarandombox.MultiverseCore.api.SafeTTeleporter;
|
||||
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
|
||||
import com.onarandombox.MultiverseCore.config.MVCoreConfig;
|
||||
import com.onarandombox.MultiverseCore.economy.MVEconomist;
|
||||
import com.onarandombox.MultiverseCore.event.MVRespawnEvent;
|
||||
import com.onarandombox.MultiverseCore.inject.InjectableListener;
|
||||
import com.onarandombox.MultiverseCore.teleportation.TeleportQueue;
|
||||
import com.onarandombox.MultiverseCore.utils.MVPermissions;
|
||||
import com.onarandombox.MultiverseCore.utils.PermissionTools;
|
||||
import com.onarandombox.MultiverseCore.utils.result.ResultChain;
|
||||
import com.onarandombox.MultiverseCore.world.entrycheck.EntryFeeResult;
|
||||
import com.onarandombox.MultiverseCore.world.entrycheck.WorldEntryCheckerProvider;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Provider;
|
||||
import org.bukkit.GameMode;
|
||||
|
@ -54,6 +59,9 @@ public class MVPlayerListener implements InjectableListener {
|
|||
private final SafeTTeleporter safeTTeleporter;
|
||||
private final Server server;
|
||||
private final TeleportQueue teleportQueue;
|
||||
private final MVEconomist economist;
|
||||
private final WorldEntryCheckerProvider worldEntryCheckerProvider;
|
||||
private final Provider<MVCommandManager> commandManagerProvider;
|
||||
|
||||
private final Map<String, String> playerWorld = new ConcurrentHashMap<String, String>();
|
||||
|
||||
|
@ -66,8 +74,10 @@ public class MVPlayerListener implements InjectableListener {
|
|||
Provider<MVPermissions> mvPermsProvider,
|
||||
SafeTTeleporter safeTTeleporter,
|
||||
Server server,
|
||||
TeleportQueue teleportQueue
|
||||
) {
|
||||
TeleportQueue teleportQueue,
|
||||
MVEconomist economist,
|
||||
WorldEntryCheckerProvider worldEntryCheckerProvider,
|
||||
Provider<MVCommandManager> commandManagerProvider) {
|
||||
this.plugin = plugin;
|
||||
this.config = config;
|
||||
this.worldManagerProvider = worldManagerProvider;
|
||||
|
@ -76,12 +86,19 @@ public class MVPlayerListener implements InjectableListener {
|
|||
this.safeTTeleporter = safeTTeleporter;
|
||||
this.server = server;
|
||||
this.teleportQueue = teleportQueue;
|
||||
this.economist = economist;
|
||||
this.worldEntryCheckerProvider = worldEntryCheckerProvider;
|
||||
this.commandManagerProvider = commandManagerProvider;
|
||||
}
|
||||
|
||||
private MVWorldManager getWorldManager() {
|
||||
return worldManagerProvider.get();
|
||||
}
|
||||
|
||||
private MVCommandManager getCommandManager() {
|
||||
return commandManagerProvider.get();
|
||||
}
|
||||
|
||||
private MVPermissions getMVPerms() {
|
||||
return mvPermsProvider.get();
|
||||
}
|
||||
|
@ -189,7 +206,7 @@ public class MVPlayerListener implements InjectableListener {
|
|||
return;
|
||||
}
|
||||
Player teleportee = event.getPlayer();
|
||||
CommandSender teleporter = null;
|
||||
CommandSender teleporter;
|
||||
Optional<String> teleporterName = teleportQueue.popFromQueue(teleportee.getName());
|
||||
if (teleporterName.isPresent()) {
|
||||
if (teleporterName.equals("CONSOLE")) {
|
||||
|
@ -198,6 +215,8 @@ public class MVPlayerListener implements InjectableListener {
|
|||
} else {
|
||||
teleporter = this.server.getPlayerExact(teleporterName.get());
|
||||
}
|
||||
} else {
|
||||
teleporter = teleportee;
|
||||
}
|
||||
Logging.finer("Inferred sender '" + teleporter + "' from name '"
|
||||
+ teleporterName + "', fetched from name '" + teleportee.getName() + "'");
|
||||
|
@ -215,50 +234,20 @@ public class MVPlayerListener implements InjectableListener {
|
|||
this.stateSuccess(teleportee.getName(), toWorld.getAlias());
|
||||
return;
|
||||
}
|
||||
// TODO: Refactor these lines.
|
||||
// Charge the teleporter
|
||||
event.setCancelled(!pt.playerHasMoneyToEnter(fromWorld, toWorld, teleporter, teleportee, true));
|
||||
if (event.isCancelled() && teleporter != null) {
|
||||
Logging.fine("Player '" + teleportee.getName()
|
||||
+ "' was DENIED ACCESS to '" + toWorld.getAlias()
|
||||
+ "' because '" + teleporter.getName()
|
||||
+ "' don't have the FUNDS required to enter it.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if player is allowed to enter the world if we're enforcing permissions
|
||||
if (config.getEnforceAccess()) {
|
||||
event.setCancelled(!pt.playerCanGoFromTo(fromWorld, toWorld, teleporter, teleportee));
|
||||
if (event.isCancelled() && teleporter != null) {
|
||||
Logging.fine("Player '" + teleportee.getName()
|
||||
+ "' was DENIED ACCESS to '" + toWorld.getAlias()
|
||||
+ "' because '" + teleporter.getName()
|
||||
+ "' don't have: multiverse.access." + event.getTo().getWorld().getName());
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
Logging.fine("Player '" + teleportee.getName()
|
||||
+ "' was allowed to go to '" + toWorld.getAlias() + "' because enforceaccess is off.");
|
||||
}
|
||||
|
||||
// Does a limit actually exist?
|
||||
if (toWorld.getPlayerLimit() > -1) {
|
||||
// Are there equal or more people on the world than the limit?
|
||||
if (toWorld.getCBWorld().getPlayers().size() >= toWorld.getPlayerLimit()) {
|
||||
// Ouch the world is full, lets see if the player can bypass that limitation
|
||||
if (!pt.playerCanBypassPlayerLimit(toWorld, teleporter, teleportee)) {
|
||||
Logging.fine("Player '" + teleportee.getName()
|
||||
+ "' was DENIED ACCESS to '" + toWorld.getAlias()
|
||||
+ "' because the world is full and '" + teleporter.getName()
|
||||
+ "' doesn't have: mv.bypass.playerlimit." + event.getTo().getWorld().getName());
|
||||
ResultChain entryResult = worldEntryCheckerProvider.forSender(teleporter).canEnterWorld(fromWorld, toWorld)
|
||||
.onSuccessReason(EntryFeeResult.Success.class, reason -> {
|
||||
if (reason == EntryFeeResult.Success.ENOUGH_MONEY) {
|
||||
economist.payEntryFee((Player) teleporter, toWorld);
|
||||
// Send payment receipt
|
||||
}
|
||||
})
|
||||
.onFailure(results -> {
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
getCommandManager().getCommandIssuer(teleporter).sendError(results.getLastResultMessage());
|
||||
});
|
||||
|
||||
// By this point anything cancelling the event has returned on the method, meaning the teleport is a success \o/
|
||||
this.stateSuccess(teleportee.getName(), toWorld.getAlias());
|
||||
Logging.fine("Teleport result: %s", entryResult);
|
||||
}
|
||||
|
||||
private void stateSuccess(String playerName, String worldName) {
|
||||
|
@ -305,6 +294,10 @@ public class MVPlayerListener implements InjectableListener {
|
|||
if (event.getTo() == null) {
|
||||
return;
|
||||
}
|
||||
if (config.isUsingCustomPortalSearch()) {
|
||||
event.setSearchRadius(config.getCustomPortalSearchRadius());
|
||||
}
|
||||
|
||||
MVWorld fromWorld = getWorldManager().getMVWorld(event.getFrom().getWorld().getName());
|
||||
MVWorld toWorld = getWorldManager().getMVWorld(event.getTo().getWorld().getName());
|
||||
if (event.getFrom().getWorld().equals(event.getTo().getWorld())) {
|
||||
|
@ -312,28 +305,14 @@ public class MVPlayerListener implements InjectableListener {
|
|||
Logging.finer("Player '" + event.getPlayer().getName() + "' is portaling to the same world.");
|
||||
return;
|
||||
}
|
||||
event.setCancelled(!pt.playerHasMoneyToEnter(fromWorld, toWorld, event.getPlayer(), event.getPlayer(), true));
|
||||
if (event.isCancelled()) {
|
||||
Logging.fine("Player '" + event.getPlayer().getName()
|
||||
+ "' was DENIED ACCESS to '" + event.getTo().getWorld().getName()
|
||||
+ "' because they don't have the FUNDS required to enter.");
|
||||
return;
|
||||
}
|
||||
if (config.getEnforceAccess()) {
|
||||
event.setCancelled(!pt.playerCanGoFromTo(fromWorld, toWorld, event.getPlayer(), event.getPlayer()));
|
||||
if (event.isCancelled()) {
|
||||
Logging.fine("Player '" + event.getPlayer().getName()
|
||||
+ "' was DENIED ACCESS to '" + event.getTo().getWorld().getName()
|
||||
+ "' because they don't have: multiverse.access." + event.getTo().getWorld().getName());
|
||||
}
|
||||
} else {
|
||||
Logging.fine("Player '" + event.getPlayer().getName()
|
||||
+ "' was allowed to go to '" + event.getTo().getWorld().getName()
|
||||
+ "' because enforceaccess is off.");
|
||||
}
|
||||
if (!config.isUsingCustomPortalSearch()) {
|
||||
event.setSearchRadius(config.getCustomPortalSearchRadius());
|
||||
}
|
||||
|
||||
ResultChain entryResult = worldEntryCheckerProvider.forSender(event.getPlayer()).canEnterWorld(fromWorld, toWorld)
|
||||
.onFailure(results -> {
|
||||
event.setCancelled(true);
|
||||
getCommandManager().getCommandIssuer(event.getPlayer()).sendError(results.getLastResultMessage());
|
||||
});
|
||||
|
||||
Logging.fine("Teleport result: %s", entryResult);
|
||||
}
|
||||
|
||||
private void sendPlayerToDefaultWorld(final Player player) {
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
package com.onarandombox.MultiverseCore.permissions;
|
||||
|
||||
public class CorePermissions {
|
||||
public static String WORLD_ACCESS = "multiverse.access";
|
||||
public static String WORLD_EXEMPT = "multiverse.exempt";
|
||||
public static String GAMEMODE_BYPASS = "mv.bypass.gamemode";
|
||||
public static String PLAYERLIMIT_BYPASS = "mv.bypass.playerlimit";
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package com.onarandombox.MultiverseCore.permissions;
|
||||
|
||||
import com.dumptruckman.minecraft.util.Logging;
|
||||
import com.onarandombox.MultiverseCore.api.MVWorld;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jvnet.hk2.annotations.Service;
|
||||
|
||||
@Service
|
||||
public class CorePermissionsChecker {
|
||||
public boolean hasWorldAccessPermission(@NotNull CommandSender sender, @NotNull MVWorld world) {
|
||||
return hasPermission(sender, concatPermission(CorePermissions.WORLD_ACCESS, world.getName()));
|
||||
}
|
||||
|
||||
public boolean hasWorldExemptPermission(@NotNull CommandSender sender, @NotNull MVWorld world) {
|
||||
return hasPermission(sender, concatPermission(CorePermissions.WORLD_EXEMPT, world.getName()));
|
||||
}
|
||||
|
||||
public boolean hasPlayerLimitBypassPermission(@NotNull CommandSender sender, @NotNull MVWorld world) {
|
||||
return hasPermission(sender, concatPermission(CorePermissions.PLAYERLIMIT_BYPASS, world.getName()));
|
||||
}
|
||||
|
||||
public boolean hasGameModeBypassPermission(@NotNull CommandSender sender, @NotNull MVWorld world) {
|
||||
return hasPermission(sender, concatPermission(CorePermissions.GAMEMODE_BYPASS, world.getName()));
|
||||
}
|
||||
|
||||
private String concatPermission(String permission, String...child) {
|
||||
return permission + "." + String.join(".", child);
|
||||
}
|
||||
|
||||
private boolean hasPermission(CommandSender sender, String permission) {
|
||||
if (sender.hasPermission(permission)) {
|
||||
Logging.finer("Checking to see if sender [%s] has permission [%s]... YES", sender.getName(), permission);
|
||||
return true;
|
||||
}
|
||||
Logging.finer("Checking to see if sender [%s] has permission [%s]... NO", sender.getName(), permission);
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -84,7 +84,20 @@ public enum MVCorei18n implements MessageKeyProvider {
|
|||
|
||||
// debug command
|
||||
DEBUG_INFO_OFF,
|
||||
DEBUG_INFO_ON;
|
||||
DEBUG_INFO_ON,
|
||||
|
||||
// entry check
|
||||
ENTRYCHECK_BLACKLISTED,
|
||||
ENTRYCHECK_NOTENOUGHMONEY,
|
||||
ENTRYCHECK_CANNOTPAYENTRYFEE,
|
||||
ENTRYCHECK_EXCEEDPLAYERLIMIT,
|
||||
ENTRYCHECK_NOWORLDACCESS,
|
||||
|
||||
// generic
|
||||
GENERIC_SUCCESS,
|
||||
GENERIC_FAILURE
|
||||
|
||||
;
|
||||
|
||||
private final MessageKey key = MessageKey.of("mv-core." + this.name().replace('_', '.').toLowerCase());
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import org.jetbrains.annotations.Nullable;
|
|||
/**
|
||||
* Captures string replacements for {@link Message}s.
|
||||
*/
|
||||
public final class MessageReplacement {
|
||||
public final class MessageReplacement {
|
||||
|
||||
/**
|
||||
* Creates a replacement key for the given key string.
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package com.onarandombox.MultiverseCore.utils.result;
|
||||
|
||||
import co.aikar.locales.MessageKey;
|
||||
import co.aikar.locales.MessageKeyProvider;
|
||||
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
|
||||
|
||||
public interface FailureReason extends MessageKeyProvider {
|
||||
default MessageKey getMessageKey() {
|
||||
return MVCorei18n.GENERIC_FAILURE.getMessageKey();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
package com.onarandombox.MultiverseCore.utils.result;
|
||||
|
||||
import com.onarandombox.MultiverseCore.utils.message.Message;
|
||||
import com.onarandombox.MultiverseCore.utils.message.MessageReplacement;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public sealed interface Result<S extends SuccessReason, F extends FailureReason> permits Result.Success, Result.Failure {
|
||||
static <F extends FailureReason, S extends SuccessReason> Result<S, F> success(S successReason, MessageReplacement...replacements) {
|
||||
return new Success<>(successReason, replacements);
|
||||
}
|
||||
|
||||
static <F extends FailureReason, S extends SuccessReason> Result<S, F> failure(F failureReason, MessageReplacement...replacements) {
|
||||
return new Failure<>(failureReason, replacements);
|
||||
}
|
||||
|
||||
boolean isSuccess();
|
||||
|
||||
boolean isFailure();
|
||||
|
||||
S getSuccessReason();
|
||||
|
||||
F getFailureReason();
|
||||
|
||||
@NotNull Message getReasonMessage();
|
||||
|
||||
default Result<S, F> onSuccess(Consumer<S> consumer) {
|
||||
if (this.isSuccess()) {
|
||||
consumer.accept(this.getSuccessReason());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
default Result<S, F> onFailure(Consumer<F> consumer) {
|
||||
if (this.isFailure()) {
|
||||
consumer.accept(this.getFailureReason());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
default Result<S, F> onSuccessReason(S successReason, Consumer<S> consumer) {
|
||||
if (this.isSuccess() && this.getSuccessReason() == successReason) {
|
||||
consumer.accept(this.getSuccessReason());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
default Result<S, F> onFailureReason(F failureReason, Consumer<F> consumer) {
|
||||
if (this.isFailure() && this.getFailureReason() == failureReason) {
|
||||
consumer.accept(this.getFailureReason());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
final class Success<F extends FailureReason, S extends SuccessReason> implements Result<S, F> {
|
||||
private final S successReason;
|
||||
private final MessageReplacement[] replacements;
|
||||
|
||||
public Success(S successReason, MessageReplacement[] replacements) {
|
||||
this.successReason = successReason;
|
||||
this.replacements = replacements;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSuccess() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFailure() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public S getSuccessReason() {
|
||||
return successReason;
|
||||
}
|
||||
|
||||
@Override
|
||||
public F getFailureReason() {
|
||||
throw new NoSuchElementException("No reason for failure");
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Message getReasonMessage() {
|
||||
return Message.of(successReason, "Success!", replacements);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Success{" +
|
||||
"reason=" + successReason +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
final class Failure<S extends SuccessReason, F extends FailureReason> implements Result<S, F> {
|
||||
private final F failureReason;
|
||||
private final MessageReplacement[] replacements;
|
||||
|
||||
public Failure(F failureReason, MessageReplacement[] replacements) {
|
||||
this.failureReason = failureReason;
|
||||
this.replacements = replacements;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSuccess() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFailure() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public S getSuccessReason() {
|
||||
throw new NoSuchElementException("No reason for success");
|
||||
}
|
||||
|
||||
@Override
|
||||
public F getFailureReason() {
|
||||
return failureReason;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Message getReasonMessage() {
|
||||
return Message.of(failureReason, "Success!", replacements);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Failure{" +
|
||||
"reason=" + failureReason +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
package com.onarandombox.MultiverseCore.utils.result;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.onarandombox.MultiverseCore.utils.message.Message;
|
||||
import io.vavr.control.Option;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ResultChain {
|
||||
public static Builder builder() {
|
||||
return new Builder(true);
|
||||
}
|
||||
|
||||
public static Builder builder(boolean stopOnFailure) {
|
||||
return new Builder(stopOnFailure);
|
||||
}
|
||||
|
||||
private final boolean isSuccess;
|
||||
private final List<Result<?, ?>> results;
|
||||
|
||||
ResultChain(boolean isSuccess, List<Result<?, ?>> results) {
|
||||
this.isSuccess = isSuccess;
|
||||
this.results = results;
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return isSuccess;
|
||||
}
|
||||
|
||||
public boolean isFailure() {
|
||||
return !isSuccess;
|
||||
}
|
||||
|
||||
public ResultChain onSuccess(Runnable successRunnable) {
|
||||
if (isSuccess) {
|
||||
successRunnable.run();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ResultChain onFailure(Runnable failureRunnable) {
|
||||
if (isFailure()) {
|
||||
failureRunnable.run();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ResultChain onSuccess(Consumer<ResultChain> successRunnable) {
|
||||
if (isSuccess) {
|
||||
successRunnable.accept(this);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ResultChain onFailure(Consumer<ResultChain> failureRunnable) {
|
||||
if (isFailure()) {
|
||||
failureRunnable.accept(this);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public <S extends SuccessReason> ResultChain onSuccessReason(Class<S> successReasonClass, Consumer<S> successConsumer) {
|
||||
getSuccessReason(successReasonClass).peek(successConsumer);
|
||||
return this;
|
||||
}
|
||||
|
||||
public <F extends FailureReason> ResultChain onFailureReason(Class<F> failureReasonClass, Consumer<F> failureConsumer) {
|
||||
getFailureReason(failureReasonClass).peek(failureConsumer);
|
||||
return this;
|
||||
}
|
||||
|
||||
public <S extends SuccessReason> ResultChain onSuccessReason(S successReason, Runnable successRunnable) {
|
||||
getSuccessReason(successReason.getClass()).filter(successReason::equals).peek(reason -> successRunnable.run());
|
||||
return this;
|
||||
}
|
||||
|
||||
public <S extends SuccessReason> Option<S> getSuccessReason(Class<S> successReasonClass) {
|
||||
if (isFailure()) {
|
||||
return Option.none();
|
||||
}
|
||||
return Option.ofOptional(results.stream()
|
||||
.map(Result::getSuccessReason)
|
||||
.filter(successReasonClass::isInstance)
|
||||
.map(successReasonClass::cast)
|
||||
.findFirst());
|
||||
}
|
||||
|
||||
public <F extends FailureReason> Option<F> getFailureReason(Class<F> failureReasonClass) {
|
||||
if (isSuccess()) {
|
||||
return Option.none();
|
||||
}
|
||||
return Option.ofOptional(results.stream()
|
||||
.map(Result::getFailureReason)
|
||||
.filter(failureReasonClass::isInstance)
|
||||
.map(failureReasonClass::cast)
|
||||
.findFirst());
|
||||
}
|
||||
|
||||
public Message getLastResultMessage() {
|
||||
return Iterables.getLast(results).getReasonMessage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ResultGroup{" +
|
||||
"isSuccess=" + isSuccess +
|
||||
", results={" + results.stream().map(Objects::toString).collect(Collectors.joining(", ")) + "}" +
|
||||
'}';
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private final boolean stopOnFailure;
|
||||
private final List<Result<?, ?>> results;
|
||||
|
||||
private boolean isSuccess = true;
|
||||
|
||||
public Builder(boolean stopOnFailure) {
|
||||
this.stopOnFailure = stopOnFailure;
|
||||
this.results = new ArrayList<>();
|
||||
}
|
||||
|
||||
public Builder then(Supplier<Result<?, ?>> resultSupplier) {
|
||||
if (!isSuccess && stopOnFailure) {
|
||||
return this;
|
||||
}
|
||||
Result<?, ?> result = resultSupplier.get();
|
||||
if (result.isFailure()) {
|
||||
isSuccess = false;
|
||||
}
|
||||
results.add(result);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ResultChain build() {
|
||||
return new ResultChain(isSuccess, results);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package com.onarandombox.MultiverseCore.utils.result;
|
||||
|
||||
import co.aikar.locales.MessageKey;
|
||||
import co.aikar.locales.MessageKeyProvider;
|
||||
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
|
||||
|
||||
public interface SuccessReason extends MessageKeyProvider {
|
||||
default MessageKey getMessageKey() {
|
||||
return MVCorei18n.GENERIC_SUCCESS.getMessageKey();
|
||||
}
|
||||
}
|
|
@ -22,7 +22,7 @@ public class EntryFee extends SerializationConfig {
|
|||
@Nullable
|
||||
private Material currency;
|
||||
|
||||
private final Material DISABLED_MATERIAL = Material.AIR;
|
||||
public static final Material DISABLED_MATERIAL = Material.AIR;
|
||||
|
||||
public EntryFee() {
|
||||
super();
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
package com.onarandombox.MultiverseCore.world.entrycheck;
|
||||
|
||||
import co.aikar.locales.MessageKey;
|
||||
import co.aikar.locales.MessageKeyProvider;
|
||||
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
|
||||
import com.onarandombox.MultiverseCore.utils.result.FailureReason;
|
||||
import com.onarandombox.MultiverseCore.utils.result.SuccessReason;
|
||||
|
||||
public class BlacklistResult {
|
||||
public enum Success implements SuccessReason {
|
||||
UNKNOWN_FROM_WORLD,
|
||||
BYPASSED_BLACKLISTED,
|
||||
NOT_BLACKLISTED
|
||||
}
|
||||
|
||||
public enum Failure implements FailureReason {
|
||||
BLACKLISTED(MVCorei18n.ENTRYCHECK_BLACKLISTED);
|
||||
|
||||
private final MessageKeyProvider message;
|
||||
|
||||
Failure(MessageKeyProvider message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageKey getMessageKey() {
|
||||
return message.getMessageKey();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package com.onarandombox.MultiverseCore.world.entrycheck;
|
||||
|
||||
|
||||
import co.aikar.locales.MessageKey;
|
||||
import co.aikar.locales.MessageKeyProvider;
|
||||
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
|
||||
import com.onarandombox.MultiverseCore.utils.result.FailureReason;
|
||||
import com.onarandombox.MultiverseCore.utils.result.SuccessReason;
|
||||
|
||||
public class EntryFeeResult {
|
||||
public enum Success implements SuccessReason {
|
||||
FREE_ENTRY,
|
||||
ENOUGH_MONEY,
|
||||
EXEMPT_FROM_ENTRY_FEE,
|
||||
CONSOLE_OR_BLOCK_COMMAND_SENDER
|
||||
}
|
||||
|
||||
public enum Failure implements FailureReason {
|
||||
NOT_ENOUGH_MONEY(MVCorei18n.ENTRYCHECK_NOTENOUGHMONEY),
|
||||
CANNOT_PAY_ENTRY_FEE(MVCorei18n.ENTRYCHECK_CANNOTPAYENTRYFEE);
|
||||
|
||||
private final MessageKeyProvider message;
|
||||
|
||||
Failure(MessageKeyProvider message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageKey getMessageKey() {
|
||||
return message.getMessageKey();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package com.onarandombox.MultiverseCore.world.entrycheck;
|
||||
|
||||
import co.aikar.locales.MessageKey;
|
||||
import co.aikar.locales.MessageKeyProvider;
|
||||
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
|
||||
import com.onarandombox.MultiverseCore.utils.result.FailureReason;
|
||||
import com.onarandombox.MultiverseCore.utils.result.SuccessReason;
|
||||
|
||||
public class PlayerLimitResult {
|
||||
public enum Success implements SuccessReason {
|
||||
NO_PLAYERLIMIT,
|
||||
WITHIN_PLAYERLIMIT,
|
||||
BYPASS_PLAYERLIMIT
|
||||
}
|
||||
|
||||
public enum Failure implements FailureReason {
|
||||
EXCEED_PLAYERLIMIT(MVCorei18n.ENTRYCHECK_EXCEEDPLAYERLIMIT);
|
||||
|
||||
private final MessageKeyProvider message;
|
||||
|
||||
Failure(MessageKeyProvider message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageKey getMessageKey() {
|
||||
return message.getMessageKey();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package com.onarandombox.MultiverseCore.world.entrycheck;
|
||||
|
||||
import co.aikar.locales.MessageKey;
|
||||
import co.aikar.locales.MessageKeyProvider;
|
||||
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
|
||||
import com.onarandombox.MultiverseCore.utils.result.FailureReason;
|
||||
import com.onarandombox.MultiverseCore.utils.result.SuccessReason;
|
||||
|
||||
public class WorldAccessResult {
|
||||
public enum Success implements SuccessReason {
|
||||
NO_ENFORCE_WORLD_ACCESS,
|
||||
HAS_WORLD_ACCESS
|
||||
}
|
||||
|
||||
public enum Failure implements FailureReason {
|
||||
NO_WORLD_ACCESS(MVCorei18n.ENTRYCHECK_NOWORLDACCESS);
|
||||
|
||||
private final MessageKeyProvider message;
|
||||
|
||||
Failure(MessageKeyProvider message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageKey getMessageKey() {
|
||||
return message.getMessageKey();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
package com.onarandombox.MultiverseCore.world.entrycheck;
|
||||
|
||||
import com.onarandombox.MultiverseCore.api.MVWorld;
|
||||
import com.onarandombox.MultiverseCore.config.MVCoreConfig;
|
||||
import com.onarandombox.MultiverseCore.economy.MVEconomist;
|
||||
import com.onarandombox.MultiverseCore.permissions.CorePermissionsChecker;
|
||||
import com.onarandombox.MultiverseCore.utils.result.Result;
|
||||
import com.onarandombox.MultiverseCore.utils.result.ResultChain;
|
||||
import com.onarandombox.MultiverseCore.world.configuration.EntryFee;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.command.BlockCommandSender;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static com.onarandombox.MultiverseCore.utils.message.MessageReplacement.replace;
|
||||
|
||||
public class WorldEntryChecker {
|
||||
private final @NotNull MVCoreConfig config;
|
||||
private final @NotNull MVEconomist economist;
|
||||
private final @NotNull CorePermissionsChecker permissionsChecker;
|
||||
|
||||
private final @NotNull CommandSender sender;
|
||||
|
||||
public WorldEntryChecker(
|
||||
@NotNull MVCoreConfig config,
|
||||
@NotNull CorePermissionsChecker permissionsChecker,
|
||||
@NotNull MVEconomist economist,
|
||||
@NotNull CommandSender sender
|
||||
) {
|
||||
this.config = config;
|
||||
this.permissionsChecker = permissionsChecker;
|
||||
this.economist = economist;
|
||||
this.sender = sender;
|
||||
}
|
||||
|
||||
public ResultChain canStayInWorld(@NotNull MVWorld world) {
|
||||
return canEnterWorld(null, world);
|
||||
}
|
||||
|
||||
public ResultChain canEnterWorld(@Nullable MVWorld fromWorld, @NotNull MVWorld toWorld) {
|
||||
return ResultChain.builder()
|
||||
.then(() -> canAccessWorld(toWorld))
|
||||
.then(() -> isWithinPlayerLimit(toWorld))
|
||||
.then(() -> isNotBlacklisted(fromWorld, toWorld))
|
||||
.then(() -> canPayEntryFee(toWorld))
|
||||
.build();
|
||||
}
|
||||
|
||||
public Result<WorldAccessResult.Success, WorldAccessResult.Failure> canAccessWorld(@NotNull MVWorld world) {
|
||||
if (!config.getEnforceAccess()) {
|
||||
return Result.success(WorldAccessResult.Success.NO_ENFORCE_WORLD_ACCESS);
|
||||
}
|
||||
return permissionsChecker.hasWorldAccessPermission(this.sender, world)
|
||||
? Result.success(WorldAccessResult.Success.HAS_WORLD_ACCESS)
|
||||
: Result.failure(WorldAccessResult.Failure.NO_WORLD_ACCESS);
|
||||
}
|
||||
|
||||
public Result<PlayerLimitResult.Success, PlayerLimitResult.Failure> isWithinPlayerLimit(@NotNull MVWorld world) {
|
||||
final int playerLimit = world.getPlayerLimit();
|
||||
if (playerLimit <= -1) {
|
||||
return Result.success(PlayerLimitResult.Success.NO_PLAYERLIMIT);
|
||||
}
|
||||
if (permissionsChecker.hasPlayerLimitBypassPermission(sender, world)) {
|
||||
return Result.success(PlayerLimitResult.Success.BYPASS_PLAYERLIMIT);
|
||||
}
|
||||
return playerLimit > world.getCBWorld().getPlayers().size()
|
||||
? Result.success(PlayerLimitResult.Success.WITHIN_PLAYERLIMIT)
|
||||
: Result.failure(PlayerLimitResult.Failure.EXCEED_PLAYERLIMIT);
|
||||
}
|
||||
|
||||
public Result<BlacklistResult.Success, BlacklistResult.Failure> isNotBlacklisted(@Nullable MVWorld fromWorld, @NotNull MVWorld toWorld) {
|
||||
if (fromWorld == null) {
|
||||
return Result.success(BlacklistResult.Success.UNKNOWN_FROM_WORLD);
|
||||
}
|
||||
return toWorld.getWorldBlacklist().contains(fromWorld.getName())
|
||||
? Result.failure(BlacklistResult.Failure.BLACKLISTED, replace("{world}").with(fromWorld.getAlias()))
|
||||
: Result.success(BlacklistResult.Success.NOT_BLACKLISTED);
|
||||
}
|
||||
|
||||
public Result<EntryFeeResult.Success, EntryFeeResult.Failure> canPayEntryFee(MVWorld world) {
|
||||
double price = world.getPrice();
|
||||
Material currency = world.getCurrency();
|
||||
if (price == 0D && (currency == null || currency == EntryFee.DISABLED_MATERIAL)) {
|
||||
return Result.success(EntryFeeResult.Success.FREE_ENTRY);
|
||||
}
|
||||
if (sender instanceof ConsoleCommandSender || sender instanceof BlockCommandSender) {
|
||||
return Result.success(EntryFeeResult.Success.CONSOLE_OR_BLOCK_COMMAND_SENDER);
|
||||
}
|
||||
if (permissionsChecker.hasWorldExemptPermission(sender, world)) {
|
||||
return Result.success(EntryFeeResult.Success.EXEMPT_FROM_ENTRY_FEE);
|
||||
}
|
||||
if (!(sender instanceof Player player)) {
|
||||
return Result.failure(EntryFeeResult.Failure.CANNOT_PAY_ENTRY_FEE);
|
||||
}
|
||||
return economist.isPlayerWealthyEnough(player, price, currency)
|
||||
? Result.success(EntryFeeResult.Success.ENOUGH_MONEY)
|
||||
: Result.failure(EntryFeeResult.Failure.NOT_ENOUGH_MONEY, replace("{amount}").with("$##")); //TODO
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package com.onarandombox.MultiverseCore.world.entrycheck;
|
||||
|
||||
import com.onarandombox.MultiverseCore.config.MVCoreConfig;
|
||||
import com.onarandombox.MultiverseCore.economy.MVEconomist;
|
||||
import com.onarandombox.MultiverseCore.permissions.CorePermissionsChecker;
|
||||
import jakarta.inject.Inject;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jvnet.hk2.annotations.Service;
|
||||
|
||||
@Service
|
||||
public class WorldEntryCheckerProvider {
|
||||
|
||||
private final @NotNull MVCoreConfig config;
|
||||
private final @NotNull MVEconomist economist;
|
||||
private final @NotNull CorePermissionsChecker permissionsChecker;
|
||||
|
||||
@Inject
|
||||
WorldEntryCheckerProvider(
|
||||
@NotNull MVCoreConfig config,
|
||||
@NotNull MVEconomist economist,
|
||||
@NotNull CorePermissionsChecker permissionsChecker
|
||||
) {
|
||||
this.config = config;
|
||||
this.economist = economist;
|
||||
this.permissionsChecker = permissionsChecker;
|
||||
}
|
||||
|
||||
public @NotNull WorldEntryChecker forSender(@NotNull CommandSender sender) {
|
||||
return new WorldEntryChecker(config, permissionsChecker, economist, sender);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
package com.onarandombox.MultiverseCore.worldnew;
|
||||
|
||||
import com.dumptruckman.minecraft.util.Logging;
|
||||
import com.onarandombox.MultiverseCore.worldnew.config.WorldConfig;
|
||||
import com.onarandombox.MultiverseCore.worldnew.config.WorldsConfigFile;
|
||||
import jakarta.inject.Inject;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jvnet.hk2.annotations.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class WorldManager {
|
||||
private final WorldsConfigFile worldsConfigFile;
|
||||
|
||||
@Inject
|
||||
WorldManager(@NotNull WorldsConfigFile worldsConfigFile) {
|
||||
this.worldsConfigFile = worldsConfigFile;
|
||||
this.worldsConfigFile.load();
|
||||
}
|
||||
|
||||
public void loadAllWorlds() {
|
||||
for (String worldName : worldsConfigFile.getAllWorldsInConfig()) {
|
||||
Logging.fine("Loading world: " + worldName);
|
||||
loadWorld(worldName);
|
||||
}
|
||||
saveWorldsConfig();
|
||||
}
|
||||
|
||||
public void addWorld(String worldName) {
|
||||
ConfigurationSection worldConfigSection = worldsConfigFile.getWorldConfigSection(worldName);
|
||||
WorldConfig worldConfig = new WorldConfig(worldConfigSection);
|
||||
//todo
|
||||
saveWorldsConfig();
|
||||
}
|
||||
|
||||
public void loadWorld(String worldName) {
|
||||
ConfigurationSection worldConfigSection = worldsConfigFile.getWorldConfigSection(worldName);
|
||||
WorldConfig worldConfig = new WorldConfig(worldConfigSection);
|
||||
//todo
|
||||
}
|
||||
|
||||
public void unloadWorld() {
|
||||
//todo
|
||||
}
|
||||
|
||||
public void removeWorld(String worldName) {
|
||||
//todo
|
||||
worldsConfigFile.deleteWorldConfigSection(worldName);
|
||||
saveWorldsConfig();
|
||||
}
|
||||
|
||||
public void deleteWorld(String worldName) {
|
||||
//todo
|
||||
worldsConfigFile.deleteWorldConfigSection(worldName);
|
||||
saveWorldsConfig();
|
||||
}
|
||||
|
||||
public void getMVWorld(String worldName) {
|
||||
//todo
|
||||
}
|
||||
|
||||
public void getUnloadedWorld(String worldName) {
|
||||
//todo
|
||||
}
|
||||
|
||||
public void saveWorldsConfig() {
|
||||
worldsConfigFile.save();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
package com.onarandombox.MultiverseCore.worldnew.config;
|
||||
|
||||
import com.dumptruckman.minecraft.util.Logging;
|
||||
import com.onarandombox.MultiverseCore.configuration.handle.ConfigurationSectionHandle;
|
||||
import io.vavr.control.Try;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class WorldConfig {
|
||||
private final WorldConfigNodes configNodes;
|
||||
private final ConfigurationSectionHandle configHandle;
|
||||
|
||||
public WorldConfig(@NotNull final ConfigurationSection configSection) {
|
||||
this.configNodes = new WorldConfigNodes();
|
||||
//todo: Config migration and version
|
||||
this.configHandle = ConfigurationSectionHandle.builder(configSection)
|
||||
.logger(Logging.getLogger())
|
||||
.nodes(configNodes.getNodes())
|
||||
.build();
|
||||
this.configHandle.load();
|
||||
}
|
||||
|
||||
public Try<Object> getProperty(String name) {
|
||||
return configHandle.get(name);
|
||||
}
|
||||
|
||||
public Try<Void> setProperty(String name, Object value) {
|
||||
return configHandle.set(name, value);
|
||||
}
|
||||
|
||||
public void setAlias(String alias) {
|
||||
configHandle.set(configNodes.ALIAS, alias);
|
||||
}
|
||||
|
||||
public @Nullable String getAlias() {
|
||||
return configHandle.get(configNodes.ALIAS);
|
||||
}
|
||||
|
||||
public void setHidden(boolean hidden) {
|
||||
configHandle.set(configNodes.HIDDEN, hidden);
|
||||
}
|
||||
|
||||
public boolean isHidden() {
|
||||
return configHandle.get(configNodes.HIDDEN);
|
||||
}
|
||||
|
||||
public List<String> getWorldBlacklist() {
|
||||
return (List<String>) configHandle.get(configNodes.WORLD_BLACKLIST);
|
||||
}
|
||||
|
||||
public void setWorldBlacklist(List<String> worldBlacklist) {
|
||||
configHandle.set(configNodes.WORLD_BLACKLIST, worldBlacklist);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
package com.onarandombox.MultiverseCore.worldnew.config;
|
||||
|
||||
import com.onarandombox.MultiverseCore.configuration.node.ConfigNode;
|
||||
import com.onarandombox.MultiverseCore.configuration.node.Node;
|
||||
import com.onarandombox.MultiverseCore.configuration.node.NodeGroup;
|
||||
import com.onarandombox.MultiverseCore.world.configuration.AllowedPortalType;
|
||||
import org.bukkit.Difficulty;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.World;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class WorldConfigNodes {
|
||||
private final NodeGroup nodes = new NodeGroup();
|
||||
|
||||
WorldConfigNodes() {
|
||||
}
|
||||
|
||||
public NodeGroup getNodes() {
|
||||
return nodes;
|
||||
}
|
||||
|
||||
private <N extends Node> N node(N node) {
|
||||
nodes.add(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
public final ConfigNode<Boolean> ADJUST_SPAWN = node(ConfigNode.builder("adjust-spawn", Boolean.class)
|
||||
.defaultValue(false)
|
||||
.name("adjust-spawn")
|
||||
.build());
|
||||
|
||||
public final ConfigNode<String> ALIAS = node(ConfigNode.builder("alias", String.class)
|
||||
.defaultValue("")
|
||||
.name("alias")
|
||||
.build());
|
||||
|
||||
public final ConfigNode<Boolean> ALLOW_FLIGHT = node(ConfigNode.builder("allow-flight", Boolean.class)
|
||||
.defaultValue(false)
|
||||
.name("allow-flight")
|
||||
.build());
|
||||
|
||||
public final ConfigNode<Boolean> ALLOW_WEATHER = node(ConfigNode.builder("allow-weather", Boolean.class)
|
||||
.defaultValue(true)
|
||||
.name("allow-weather")
|
||||
.build());
|
||||
|
||||
public final ConfigNode<Boolean> AUTO_HEAL = node(ConfigNode.builder("auto-heal", Boolean.class)
|
||||
.defaultValue(true)
|
||||
.name("auto-heal")
|
||||
.build());
|
||||
|
||||
public final ConfigNode<Boolean> AUTO_LOAD = node(ConfigNode.builder("auto-load", Boolean.class)
|
||||
.defaultValue(true)
|
||||
.name("auto-load")
|
||||
.build());
|
||||
|
||||
public final ConfigNode<Difficulty> DIFFICULTY = node(ConfigNode.builder("difficulty", Difficulty.class)
|
||||
.defaultValue(Difficulty.NORMAL)
|
||||
.name("difficulty")
|
||||
.build());
|
||||
|
||||
public final ConfigNode<World.Environment> ENVIRONMENT = node(ConfigNode.builder("environment", World.Environment.class)
|
||||
.defaultValue(World.Environment.NORMAL)
|
||||
.name("environment")
|
||||
.build());
|
||||
|
||||
public final ConfigNode<GameMode> GAMEMODE = node(ConfigNode.builder("gamemode", GameMode.class)
|
||||
.defaultValue(GameMode.SURVIVAL)
|
||||
.name("gamemode")
|
||||
.build());
|
||||
|
||||
public final ConfigNode<String> GENERATOR = node(ConfigNode.builder("generator", String.class)
|
||||
.defaultValue("")
|
||||
.name("generator")
|
||||
.build());
|
||||
|
||||
public final ConfigNode<Boolean> HIDDEN = node(ConfigNode.builder("hidden", Boolean.class)
|
||||
.defaultValue(false)
|
||||
.name("hidden")
|
||||
.build());
|
||||
|
||||
public final ConfigNode<Boolean> HUNGER = node(ConfigNode.builder("hunger", Boolean.class)
|
||||
.defaultValue(true)
|
||||
.name("hunger")
|
||||
.build());
|
||||
|
||||
public final ConfigNode<Boolean> KEEP_SPAWN_IN_MEMORY = node(ConfigNode.builder("keep-spawn-in-memory", Boolean.class)
|
||||
.defaultValue(true)
|
||||
.name("keep-spawn-in-memory")
|
||||
.build());
|
||||
|
||||
public final ConfigNode<Integer> PLAYER_LIMIT = node(ConfigNode.builder("player-limit", Integer.class)
|
||||
.defaultValue(-1)
|
||||
.name("player-limit")
|
||||
.build());
|
||||
|
||||
public final ConfigNode<AllowedPortalType> PORTAL_FORM = node(ConfigNode.builder("portal-form", AllowedPortalType.class)
|
||||
.defaultValue(AllowedPortalType.ALL)
|
||||
.name("portal-form")
|
||||
.build());
|
||||
|
||||
public final ConfigNode<Boolean> PVP = node(ConfigNode.builder("pvp", Boolean.class)
|
||||
.defaultValue(true)
|
||||
.name("pvp")
|
||||
.build());
|
||||
|
||||
public final ConfigNode<String> RESPAWN_WORLD = node(ConfigNode.builder("respawn-world", String.class)
|
||||
.defaultValue("")
|
||||
.name("respawn-world")
|
||||
.build());
|
||||
|
||||
public final ConfigNode<Double> SCALE = node(ConfigNode.builder("scale", Double.class)
|
||||
.defaultValue(1.0)
|
||||
.name("scale")
|
||||
.build());
|
||||
|
||||
public final ConfigNode<String> SEED = node(ConfigNode.builder("seed", String.class)
|
||||
.defaultValue("")
|
||||
.name("seed")
|
||||
.build());
|
||||
|
||||
public final ConfigNode<List> WORLD_BLACKLIST = node(ConfigNode.builder("world-blacklist", List.class)
|
||||
.defaultValue(new ArrayList<>())
|
||||
.name("world-blacklist")
|
||||
.build());
|
||||
|
||||
//todo: color and style
|
||||
//todo: spawning
|
||||
//todo: entryfee
|
||||
//todo: spawnLocation
|
||||
//todo: worldBlacklist
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package com.onarandombox.MultiverseCore.worldnew.config;
|
||||
|
||||
import com.onarandombox.MultiverseCore.MultiverseCore;
|
||||
import jakarta.inject.Inject;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jvnet.hk2.annotations.Service;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
|
||||
@Service
|
||||
public class WorldsConfigFile {
|
||||
private static final String CONFIG_FILENAME = "worlds2.yml";
|
||||
|
||||
private final File worldConfigFile;
|
||||
private YamlConfiguration worldConfig;
|
||||
|
||||
@Inject
|
||||
public WorldsConfigFile(@NotNull MultiverseCore core) {
|
||||
worldConfigFile = core.getDataFolder().toPath().resolve(CONFIG_FILENAME).toFile();
|
||||
}
|
||||
|
||||
public void load() {
|
||||
//todo: Migration from old worlds.yml
|
||||
worldConfig = YamlConfiguration.loadConfiguration(worldConfigFile);
|
||||
}
|
||||
|
||||
public boolean isLoaded() {
|
||||
return worldConfig != null;
|
||||
}
|
||||
|
||||
public void save() {
|
||||
try {
|
||||
worldConfig.save(worldConfigFile);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<String> getAllWorldsInConfig() {
|
||||
return worldConfig.getKeys(false);
|
||||
}
|
||||
|
||||
public ConfigurationSection getWorldConfigSection(String worldName) {
|
||||
return worldConfig.isConfigurationSection(worldName)
|
||||
? worldConfig.getConfigurationSection(worldName) : worldConfig.createSection(worldName);
|
||||
}
|
||||
|
||||
public void deleteWorldConfigSection(String worldName) {
|
||||
worldConfig.set(worldName, null);
|
||||
}
|
||||
}
|
|
@ -118,3 +118,14 @@ mv-core.unload.success=&aUnloaded world '{world}'!
|
|||
|
||||
# /mv usage
|
||||
mv-core.usage.description=Show Multiverse-Core command usage.
|
||||
|
||||
# entry check
|
||||
mv-core.entrycheck.blacklisted='{world}' is blacklisted.
|
||||
mv-core.entrycheck.notenoughmoney=you do not have enough money to pay entry fee. You are required to pay {amount}.
|
||||
mv-core.entrycheck.cannotpayentryfee=you do not have the ability to pay entry fee.
|
||||
mv-core.entrycheck.exceedplayerlimit=the world has reached its player limit.
|
||||
mv-core.entrycheck.noworldaccess=you do not have permissions to access the world.
|
||||
|
||||
# generic
|
||||
mv-core.generic.success=Success!
|
||||
mv-core.generic.failure=Failed!
|
||||
|
|
|
@ -39,7 +39,7 @@ class ConfigTest : TestWithMockBukkit() {
|
|||
fun `Old config is migrated`() {
|
||||
val oldConfig = getResourceAsText("/old_config.yml")
|
||||
assertNotNull(oldConfig)
|
||||
File(Path.of(multiverseCore.dataFolder.absolutePath, "config.yml").absolutePathString()).writeText(oldConfig)
|
||||
multiverseCore.dataFolder.toPath().resolve("config.yml").toFile().writeText(oldConfig)
|
||||
assertTrue(config.load())
|
||||
assertTrue(config.save())
|
||||
|
||||
|
|
|
@ -127,7 +127,7 @@ class InjectionTest : TestWithMockBukkit() {
|
|||
fun `Commands are available as services`() {
|
||||
val commands = multiverseCore.getAllServices(MultiverseCommand::class.java)
|
||||
// TODO come up with a better way to test this like via actually testing the effect of calling each command
|
||||
assertEquals(17, commands.size)
|
||||
assertEquals(18, commands.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
package org.mvplugins.multiverse.core.world
|
||||
|
||||
import com.onarandombox.MultiverseCore.worldnew.config.WorldConfig
|
||||
import com.onarandombox.MultiverseCore.worldnew.config.WorldsConfigFile
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.mvplugins.multiverse.core.TestWithMockBukkit
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.absolutePathString
|
||||
import kotlin.test.BeforeTest
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertNotNull
|
||||
|
||||
class WorldConfigFileTest : TestWithMockBukkit() {
|
||||
|
||||
private lateinit var worldConfigFile : WorldsConfigFile
|
||||
|
||||
@BeforeTest
|
||||
fun setUp() {
|
||||
val defaultConfig = getResourceAsText("/default_worlds.yml")
|
||||
assertNotNull(defaultConfig)
|
||||
File(Path.of(multiverseCore.dataFolder.absolutePath, "worlds2.yml").absolutePathString()).writeText(defaultConfig)
|
||||
|
||||
worldConfigFile = WorldsConfigFile(multiverseCore)
|
||||
worldConfigFile.load()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `World config is loaded`() {
|
||||
assertTrue(worldConfigFile.isLoaded)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Old world config is migrated`() {
|
||||
// TODO: When logic is implemented, check that the old config is migrated
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Add a new world to config`() {
|
||||
val worldConfig = WorldConfig(worldConfigFile.getWorldConfigSection("newworld"))
|
||||
worldConfigFile.save()
|
||||
compareConfigFile("worlds2.yml", "/newworld_worlds.yml")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Updating existing world properties`() {
|
||||
val worldConfig = WorldConfig(worldConfigFile.getWorldConfigSection("world"))
|
||||
worldConfig.setProperty("adjust-spawn", true)
|
||||
worldConfig.setProperty("alias", "newalias")
|
||||
worldConfigFile.save()
|
||||
compareConfigFile("worlds2.yml", "/properties_worlds.yml")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Delete world section from config`() {
|
||||
worldConfigFile.deleteWorldConfigSection("world")
|
||||
worldConfigFile.save()
|
||||
compareConfigFile("worlds2.yml", "/delete_worlds.yml")
|
||||
}
|
||||
|
||||
private fun compareConfigFile(configPath: String, comparePath: String) {
|
||||
// TODO: Map keys may not guaranteed order. Potentially look at Hamkrest and assertThat.
|
||||
val config = multiverseCore.dataFolder.toPath().resolve(configPath).toFile().readText()
|
||||
val configCompare = getResourceAsText(comparePath)
|
||||
assertNotNull(configCompare)
|
||||
assertEquals(configCompare, config)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
package org.mvplugins.multiverse.core.world
|
||||
|
||||
import com.onarandombox.MultiverseCore.worldnew.config.WorldConfig
|
||||
import com.onarandombox.MultiverseCore.worldnew.config.WorldsConfigFile
|
||||
import org.mvplugins.multiverse.core.TestWithMockBukkit
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.absolutePathString
|
||||
import kotlin.test.BeforeTest
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class WorldConfigTest : TestWithMockBukkit() {
|
||||
|
||||
private lateinit var worldConfigFile : WorldsConfigFile
|
||||
private lateinit var worldConfig : WorldConfig
|
||||
|
||||
@BeforeTest
|
||||
fun setUp() {
|
||||
val defaultConfig = getResourceAsText("/default_worlds.yml")
|
||||
assertNotNull(defaultConfig)
|
||||
File(Path.of(multiverseCore.dataFolder.absolutePath, "worlds2.yml").absolutePathString()).writeText(defaultConfig)
|
||||
|
||||
worldConfigFile = WorldsConfigFile(multiverseCore)
|
||||
worldConfigFile.load()
|
||||
worldConfig = WorldConfig(worldConfigFile.getWorldConfigSection("world"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Getting existing world property with getProperty returns expected value`() {
|
||||
assertEquals("my world", worldConfig.getProperty("alias").get())
|
||||
assertEquals(false, worldConfig.getProperty("hidden").get())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Getting non-existing world property with getProperty returns null`() {
|
||||
assertTrue(worldConfig.getProperty("invalid-property").isFailure)
|
||||
assertTrue(worldConfig.getProperty("version").isFailure)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Getting existing world property by getter returns expected value`() {
|
||||
assertEquals("my world", worldConfig.alias)
|
||||
assertEquals(false, worldConfig.isHidden)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Updating an existing world property with setProperty reflects the changes in getProperty`() {
|
||||
assertTrue(worldConfig.setProperty("adjust-spawn", true).isSuccess)
|
||||
assertEquals(true, worldConfig.getProperty("adjust-spawn").get())
|
||||
|
||||
assertTrue(worldConfig.setProperty("alias", "abc").isSuccess)
|
||||
assertEquals("abc", worldConfig.getProperty("alias").get())
|
||||
|
||||
assertTrue(worldConfig.setProperty("scale", 2.0).isSuccess)
|
||||
assertEquals(2.0, worldConfig.getProperty("scale").get())
|
||||
|
||||
val blacklists = listOf("a", "b", "c")
|
||||
assertTrue(worldConfig.setProperty("world-blacklist", blacklists).isSuccess)
|
||||
assertEquals(blacklists, worldConfig.getProperty("world-blacklist").get())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Updating a non-existing property with setProperty returns false`() {
|
||||
assertTrue(worldConfig.setProperty("invalid-property", false).isFailure)
|
||||
assertTrue(worldConfig.setProperty("version", 1.1).isFailure)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package org.mvplugins.multiverse.core.world
|
||||
|
||||
import com.onarandombox.MultiverseCore.worldnew.WorldManager
|
||||
import org.mvplugins.multiverse.core.TestWithMockBukkit
|
||||
import kotlin.test.BeforeTest
|
||||
import kotlin.test.Test
|
||||
|
||||
class WorldManagerTest : TestWithMockBukkit() {
|
||||
|
||||
private lateinit var worldManager: WorldManager
|
||||
|
||||
@BeforeTest
|
||||
fun setUp() {
|
||||
worldManager = multiverseCore.getService(WorldManager::class.java).takeIf { it != null } ?: run {
|
||||
throw IllegalStateException("WorldManager is not available as a service") }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Add world`() {
|
||||
worldManager.addWorld("world")
|
||||
// TODO: When logic is implemented, check that the world is added
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Delete world`() {
|
||||
worldManager.deleteWorld("world")
|
||||
// TODO: When logic is implemented, check that the world is removed
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
world:
|
||||
adjust-spawn: false
|
||||
alias: my world
|
||||
allow-flight: false
|
||||
allow-weather: true
|
||||
auto-heal: true
|
||||
auto-load: true
|
||||
difficulty: NORMAL
|
||||
environment: NORMAL
|
||||
gamemode: SURVIVAL
|
||||
generator: ''
|
||||
hidden: false
|
||||
hunger: true
|
||||
keep-spawn-in-memory: true
|
||||
player-limit: -1
|
||||
portal-form: ALL
|
||||
pvp: true
|
||||
respawn-world: ''
|
||||
scale: 1.0
|
||||
seed: ''
|
||||
world-blacklist: []
|
||||
world_nether:
|
||||
adjust-spawn: false
|
||||
alias: ''
|
||||
allow-flight: false
|
||||
allow-weather: true
|
||||
auto-heal: true
|
||||
auto-load: true
|
||||
difficulty: NORMAL
|
||||
environment: NETHER
|
||||
gamemode: SURVIVAL
|
||||
generator: ''
|
||||
hidden: false
|
||||
hunger: true
|
||||
keep-spawn-in-memory: true
|
||||
player-limit: -1
|
||||
portal-form: ALL
|
||||
pvp: true
|
||||
respawn-world: ''
|
||||
scale: 1.0
|
||||
seed: ''
|
||||
world-blacklist: []
|
|
@ -0,0 +1,21 @@
|
|||
world_nether:
|
||||
adjust-spawn: false
|
||||
alias: ''
|
||||
allow-flight: false
|
||||
allow-weather: true
|
||||
auto-heal: true
|
||||
auto-load: true
|
||||
difficulty: NORMAL
|
||||
environment: NETHER
|
||||
gamemode: SURVIVAL
|
||||
generator: ''
|
||||
hidden: false
|
||||
hunger: true
|
||||
keep-spawn-in-memory: true
|
||||
player-limit: -1
|
||||
portal-form: ALL
|
||||
pvp: true
|
||||
respawn-world: ''
|
||||
scale: 1.0
|
||||
seed: ''
|
||||
world-blacklist: []
|
|
@ -0,0 +1,63 @@
|
|||
world:
|
||||
adjust-spawn: false
|
||||
alias: my world
|
||||
allow-flight: false
|
||||
allow-weather: true
|
||||
auto-heal: true
|
||||
auto-load: true
|
||||
difficulty: NORMAL
|
||||
environment: NORMAL
|
||||
gamemode: SURVIVAL
|
||||
generator: ''
|
||||
hidden: false
|
||||
hunger: true
|
||||
keep-spawn-in-memory: true
|
||||
player-limit: -1
|
||||
portal-form: ALL
|
||||
pvp: true
|
||||
respawn-world: ''
|
||||
scale: 1.0
|
||||
seed: ''
|
||||
world-blacklist: []
|
||||
world_nether:
|
||||
adjust-spawn: false
|
||||
alias: ''
|
||||
allow-flight: false
|
||||
allow-weather: true
|
||||
auto-heal: true
|
||||
auto-load: true
|
||||
difficulty: NORMAL
|
||||
environment: NETHER
|
||||
gamemode: SURVIVAL
|
||||
generator: ''
|
||||
hidden: false
|
||||
hunger: true
|
||||
keep-spawn-in-memory: true
|
||||
player-limit: -1
|
||||
portal-form: ALL
|
||||
pvp: true
|
||||
respawn-world: ''
|
||||
scale: 1.0
|
||||
seed: ''
|
||||
world-blacklist: []
|
||||
newworld:
|
||||
adjust-spawn: false
|
||||
alias: ''
|
||||
allow-flight: false
|
||||
allow-weather: true
|
||||
auto-heal: true
|
||||
auto-load: true
|
||||
difficulty: NORMAL
|
||||
environment: NORMAL
|
||||
gamemode: SURVIVAL
|
||||
generator: ''
|
||||
hidden: false
|
||||
hunger: true
|
||||
keep-spawn-in-memory: true
|
||||
player-limit: -1
|
||||
portal-form: ALL
|
||||
pvp: true
|
||||
respawn-world: ''
|
||||
scale: 1.0
|
||||
seed: ''
|
||||
world-blacklist: []
|
|
@ -0,0 +1,42 @@
|
|||
world:
|
||||
adjust-spawn: true
|
||||
alias: newalias
|
||||
allow-flight: false
|
||||
allow-weather: true
|
||||
auto-heal: true
|
||||
auto-load: true
|
||||
difficulty: NORMAL
|
||||
environment: NORMAL
|
||||
gamemode: SURVIVAL
|
||||
generator: ''
|
||||
hidden: false
|
||||
hunger: true
|
||||
keep-spawn-in-memory: true
|
||||
player-limit: -1
|
||||
portal-form: ALL
|
||||
pvp: true
|
||||
respawn-world: ''
|
||||
scale: 1.0
|
||||
seed: ''
|
||||
world-blacklist: []
|
||||
world_nether:
|
||||
adjust-spawn: false
|
||||
alias: ''
|
||||
allow-flight: false
|
||||
allow-weather: true
|
||||
auto-heal: true
|
||||
auto-load: true
|
||||
difficulty: NORMAL
|
||||
environment: NETHER
|
||||
gamemode: SURVIVAL
|
||||
generator: ''
|
||||
hidden: false
|
||||
hunger: true
|
||||
keep-spawn-in-memory: true
|
||||
player-limit: -1
|
||||
portal-form: ALL
|
||||
pvp: true
|
||||
respawn-world: ''
|
||||
scale: 1.0
|
||||
seed: ''
|
||||
world-blacklist: []
|
Loading…
Reference in New Issue