Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
name: Build and Publish
on:
push:
branches-ignore:
- main
release:
types: [ published ]

jobs:
build:
runs-on: ubuntu-latest
if: github.repository_owner == 'TerrorByteTW'
outputs:
jar: ${{ steps.artifact-upload.outputs.artifact-id }}
steps:
- name: Checkout sources
uses: actions/checkout@v5
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: 21
cache: gradle
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v5
- name: Build Snapshot
if: ${{ github.event_name == 'push' }}
run: ./gradlew build
- name: Build Release
if: ${{ github.event_name == 'release' }}
run: ./gradlew build -PreleaseBuild=true
- name: Upload build artifact
uses: actions/upload-artifact@v4
id: artifact-upload
with:
name: build-output
path: build/libs/*.jar
if-no-files-found: 'error'

publish-snapshot:
runs-on: ubuntu-latest
needs: build
if: ${{ github.event_name == 'push' }}
steps:
- uses: actions/checkout@v4
- name: Download build artifact
uses: actions/download-artifact@v5
with:
artifact-ids: ${{ needs.build.outputs.jar }}
path: ./dist

- name: Determine Version
id: version-meta
run: |
echo "version=$(echo "$BASENAME" | sed -E 's/^[^-]+-(.+)\.jar$/\1/')" >> $GITHUB_OUTPUT

- name: Publish Snapshot to Modrinth
env:
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
PROJECT_ID: 4dRudYCe
TITLE: "${{ steps.version-meta.outputs.version }}"
CHANGELOG: "Automated snapshot built from ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}. DO NOT USE IN PRODUCTION!"
TYPE: "alpha"
run: |
chmod +x ./.github/workflows/publish-to-modrinth.sh
./.github/workflows/publish-to-modrinth.sh


- name: Publish Snapshot to Hangar
env:
HANGAR_TOKEN: ${{ secrets.HANGAR_TOKEN }}
PROJECT_SLUG: 3265
TITLE: "${{ steps.version-meta.outputs.version }}"
DESCRIPTION: "Automated snapshot built from ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}. DO NOT USE IN PRODUCTION!"
CHANNEL: "Snapshot"
run: |
chmod +x ./.github/workflows/publish-to-hangar.sh
./.github/workflows/publish-to-hangar.sh


publish-release:
runs-on: ubuntu-latest
needs: build
if: ${{ github.event_name == 'release' }}
steps:
- uses: actions/checkout@v4
- name: Download build artifact
uses: actions/download-artifact@v5
with:
artifact-ids: ${{ needs.build.outputs.jar }}
path: ./dist

- name: Publish Release
run: |
echo "Publishing release..."
93 changes: 93 additions & 0 deletions .github/workflows/publish-to-hangar.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#!/usr/bin/env bash
set -euo pipefail

# === ⚙️ Config ===
PROJECT_SLUG="${PROJECT_SLUG:-}"
HANGAR_TOKEN="${HANGAR_TOKEN:-}"
CHANNEL="${CHANNEL:-Snapshot}" # Release | Snapshot | Beta
TITLE="${TITLE:-}"
DESCRIPTION="${DESCRIPTION:-}"
DIST_DIR="${DIST_DIR:-dist}"

# === 🔍 Validation ===
if [ -z "$HANGAR_TOKEN" ]; then
echo "❌ Missing HANGAR_TOKEN environment variable."
exit 1
fi
if [ -z "$PROJECT_SLUG" ]; then
echo "❌ Missing PROJECT_SLUG environment variable."
exit 1
fi

FILE=$(ls "$DIST_DIR"/*.jar 2>/dev/null | head -n 1 || true)
if [ -z "$FILE" ]; then
echo "❌ Could not find .jar file in '$DIST_DIR/'."
exit 1
fi

# === 🧮 Version extraction ===
BASENAME=$(basename "$FILE")
VERSION=$(echo "$BASENAME" | sed -E 's/^[^-]+-(.+)\.jar$/\1/')
DATE=$(date -u +"%Y-%m-%d %H:%M UTC")

if [ -z "$TITLE" ]; then
TITLE="Automated Upload $VERSION"
fi
if [ -z "$DESCRIPTION" ]; then
DESCRIPTION="Automated upload from commit ${GITHUB_SHA:-unknown} on $DATE."
fi

echo "📦 Preparing Hangar upload..."
echo " Project : $PROJECT_SLUG"
echo " Version : $VERSION"
echo " Channel : $CHANNEL"
echo " File : $FILE"

# === 🔑 Authenticate ===
AUTH_RESPONSE=$(curl -sfS -X POST "https://hangar.papermc.io/api/v1/authenticate?apiKey=$HANGAR_TOKEN")
TOKEN=$(echo "$AUTH_RESPONSE" | jq -r '.token')

if [ -z "$TOKEN" ] || [ "$TOKEN" = "null" ]; then
echo "❌ Failed to obtain JWT token from Hangar."
echo "$AUTH_RESPONSE"
exit 1
fi

# === 🧱 Version metadata ===
cat > versionUpload.json <<EOF
{
"version": "$VERSION",
"channel": "$CHANNEL",
"description": "$DESCRIPTION",
"platformDependencies": {
"PAPER": ["1.21.11"]
},
"pluginDependencies": {},
"files": [
{
"platforms": [
"PAPER"
]
}
]
}
EOF

# === ☁️ Upload ===
echo "🚀 Uploading to Hangar..."
HTTP_CODE=$(curl -s -w "%{http_code}" -o response.json \
-X POST "https://hangar.papermc.io/api/v1/projects/$PROJECT_SLUG/upload" \
-H "Authorization: HangarAuth $TOKEN" \
-H "accept: application/json" \
-F "files=@$FILE;type=application/java-archive" \
-F "versionUpload=@versionUpload.json;type=application/json")

if [[ "$HTTP_CODE" -lt 200 || "$HTTP_CODE" -ge 300 ]]; then
echo "❌ Upload failed (HTTP $HTTP_CODE)"
cat response.json
exit 1
fi

URL=$(jq -r '.url' response.json 2>/dev/null || echo "unknown")
echo "✅ Upload complete!"
echo "🌐 Version URL: ${URL}"
79 changes: 79 additions & 0 deletions .github/workflows/publish-to-modrinth.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#!/usr/bin/env bash
set -euo pipefail

# === ⚙️ Config ===
MODRINTH_TOKEN="${MODRINTH_TOKEN:-}"
PROJECT_ID="${PROJECT_ID:-}"
DIST_DIR="${DIST_DIR:-dist}"
TITLE="${TITLE:-}"
CHANGELOG="${CHANGELOG:-}"
TYPE="${TYPE:-alpha}" # release | beta | alpha

# === 🔍 Validation ===
if [ -z "$MODRINTH_TOKEN" ]; then
echo "❌ Missing MODRINTH_TOKEN environment variable."
exit 1
fi
if [ -z "$PROJECT_ID" ]; then
echo "❌ Missing PROJECT_ID environment variable."
exit 1
fi

FILE=$(ls "$DIST_DIR"/*.jar 2>/dev/null | head -n 1 || true)
if [ -z "$FILE" ]; then
echo "❌ Could not find any .jar file in '$DIST_DIR/'."
exit 1
fi
FILE_NAME=$(basename "$FILE")

# === 🧮 Version extraction ===
VERSION=$(echo "$FILE_NAME" | sed -E 's/^[^-]+-(.+)\.jar$/\1/')
DATE=$(date -u +"%Y-%m-%d %H:%M UTC")

# === 🧾 Metadata generation ===
if [ -z "$TITLE" ]; then
TITLE="Automated Build $VERSION"
fi
if [ -z "$CHANGELOG" ]; then
CHANGELOG="Build generated automatically from commit ${GITHUB_SHA:-unknown} on $DATE."
fi

echo "🧾 Preparing Modrinth metadata..."
echo " File: $FILE_NAME"
echo " Version: $VERSION"
echo " Type: $TYPE"
echo " Title: $TITLE"

# === 🧱 Create metadata.json ===
cat > metadata.json <<EOF
{
"name": "$TITLE",
"version_number": "$VERSION",
"changelog": "$CHANGELOG",
"version_type": "$TYPE",
"loaders": ["paper"],
"game_versions": ["1.21.11"],
"project_id": "$PROJECT_ID",
"featured": false,
"status": "listed",
"file_parts": ["file"],
"primary_file": "file",
"dependencies": []
}
EOF

# === ☁️ Upload ===
echo "🚀 Uploading to Modrinth..."
HTTP_CODE=$(curl -s -w "%{http_code}" -o response.json \
-X POST "https://api.modrinth.com/v2/version" \
-H "Authorization: Bearer $MODRINTH_TOKEN" \
-F "data=@metadata.json;type=application/json" \
-F "file=@$FILE;type=application/java-archive")

if [[ "$HTTP_CODE" -lt 200 || "$HTTP_CODE" -ge 300 ]]; then
echo "❌ Upload failed (HTTP $HTTP_CODE)"
cat response.json
exit 1
fi

echo "✅ Upload complete!"
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@
# IntelliJ
out/

# Gradle
/.gradle

# Build Artifacts
/build

# Compiled class file
*.class

Expand Down
75 changes: 57 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@
<a href="https://modrinth.com/plugin/dimensionpause">
<img alt="Modrinth" src="https://img.shields.io/badge/Download%20at-Modrinth-brightgreen?style=for-the-badge&logo=modrinth">
</a>
<a href="https://polymart.org/resource/dimensionpause.4411">
<img alt="Polymart" src="https://img.shields.io/badge/Download%20At-Polymart-%2303a092?style=for-the-badge">
</a>
<a href="https://hangar.papermc.io/TerrorByte/DimensionPause">
<img alt="Hangar" src="https://img.shields.io/badge/Download%20At-Hangar-%23f29f22?style=for-the-badge">
</a>
Expand All @@ -13,28 +10,70 @@
# Dimension Pause 🌎⌚

## What is Dimension Pause?
Dimension Pause is a super simple, lightweight plugin that allows you to temporarily block players from creating dimension portals or entering dimensions.

It works by detecting players attempting to create portals, or detecting when a player switches worlds (Such as entering an already-existing portal, or using Essentials's `/home` feature). When this happens,
if the world is paused and certain criteria is not met, the player is either blocked from creating the portal, or kicked out of the world.
Dimension Pause is a super simple, lightweight plugin that allows you to temporarily block players from creating
dimension portals or entering dimensions.

It works by detecting players attempting to create portals, or detecting when a player switches worlds (Such as entering
an already-existing portal, or using Essentials's `/home` feature). When this happens,
if that world's dimension is paused and certain criteria are not met, the player is either blocked from creating the
portal, or kicked out of the world.

If the player is currently in a dimension when it is disabled, then they are kicked out to either their respawn location
or a world defined in config if their respawn location is unavailable.

If the player is currently in a dimension when it is disabled, then they are kicked out to either their bed or a world defined in config.
## Current Features

* Completely block access to dimensions _per world_. See the "World Setup" section below for details
* Players cannot create portals, enter portals or teleport via commands (Such as `/home` or `/warp`) to other
dimensions. If the player was in a dimension that was paused while they were logged off, upon logging back on they
will be teleported out after a configurable delay.
* Pause worlds until manually unpaused by server staff, or after a delay
* Dimensions, by default, are paused indefinitely. However, you can specify a delay to pause the world for a
duration.
* Supports custom translations/formatting for chat messages and titles. You are not locked to "DimensionPause" branding!
* Upon loading the server, check out the `plugins/DimensionPause/lang/` folder for configuration options.
* Persistent & resilient expiration timers. Dimensions will not stay paused by accident if your server restarts or
crashes!
* DimensionPause uses Paper's native scheduler, and will (re)schedule timers for ALL temporarily paused dimensions
on server start, dimension toggle, and world load.
* Folia Support
* NOTE: Folia is **NOT TESTED**. While the plugin has been written with Folia in mind, we have not actually run it on
Folia yet due to dependencies like LuckPerms not working on Folia yet. Use at your own risk, Folia will be tested
in the next version

## Future Features
* Support multiple worlds as well as dimensions
* Currently, if you create multiple Nether worlds with a multi-world plugin, such as MultiVerse, you can only disable *all* Nether worlds, not just specific ones.

* Support Velocity / BungeeCord
* Temporarily disable dimensions (Disable dimensions for an hour, for example)
* Create an API for developers to integrate with DimensionPause

## Commands & Permissions

All commands may substitute `/dimensionpause` with `/dp` for conciseness

| Command | Permission | Description |
|----------------------------------------|-------------------------|-----------------------------------------------------------------------------------------------|
| /dimensionpause | dimensionpause.commands | Displays help menu |
| /dimensionpause toggle <end \| nether> | dimensionpause.toggle | Pauses or unpauses a given dimension type |
| /dimensionpause state <end \| nether> | dimensionpause.state | Checks the state of a given dimension type |
| /dimensionpause reload | dimensionpause.reload | Reloads DimensionPause configs and language files |
| | dimensionpause.bypass | Allows players to bypass a bypassable world. If a world is not bypassable, only Ops may enter |
| | dimensionpause.* | Grants all permissions listed above |
| Command | Permission | Description |
|--------------------------------------------------------------------------------------------------------------------------|-------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------|
| /dimensionpause | dimensionpause.commands | Displays help menu |
| /dimensionpause toggle <world> <end \| nether> \[\<weeks\>w\]\[\<days\>d\]\[\<hours\>h\]\[\<minutes\>m\]\[\<seconds\>s\] | dimensionpause.toggle | Pauses or unpauses a given dimension type for a specific world, with an optional duration. The duration is how long from now the pause will expire. |
| /dimensionpause state <world> <end \| nether> | dimensionpause.state | Checks the state of a given dimension type for a specific world |
| /dimensionpause reload | dimensionpause.reload | Reloads DimensionPause configs and language files |
| | dimensionpause.bypass.[world].[dimension] | Allows players to bypass a pause for a given world & dimension |
| | dimensionpause.* | Grants all permissions listed above |

## Requirements

DimensionPause 1.1.2 works for 1.17 and up. However, DimensionPause 2.0.0 requires the latest version of Paper (At the
time of writing, 1.21.11 & Java 21). Supporting older versions of Paper while maintaining _forward_ compatibility is a
massive pain. Out of the almost 100 servers running DimensionPause, less than 5% of servers are running a version of
Paper
unsupported by this plugin, and almost 75% of servers are supported.

## World Setup

DimensionPause works under the assumption that your Nether and End dimensions will be connected to
an Overworld dimension. DimensionPause does *not* support pausing dimensions for nether-only or end-only worlds, as they
do not have an associated overworld.

Since Paper does not link worlds to each other, instead it's assumed nether and end worlds follow the standard "_nether"
or "_the_end" naming conventions. For example, the default `world` dimension worlds should be called `world_nether` and
`world_the_end`. **If your worlds are not named like this, you cannot use DimensionPause**.
Loading
Loading