Compare commits
6 Commits
0.5
...
74c36a8aa7
| Author | SHA1 | Date | |
|---|---|---|---|
| 74c36a8aa7 | |||
| 33ed09da09 | |||
| 2437d38e70 | |||
| 990ce6811c | |||
| e1d2c8751a | |||
| 012e1f15a3 |
57
.classpath
Normal file
57
.classpath
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<classpath>
|
||||||
|
<classpathentry kind="src" output="target/classes" path="src/main/java">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="optional" value="true"/>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
<attribute name="optional" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="optional" value="true"/>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
<attribute name="test" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
<attribute name="test" value="true"/>
|
||||||
|
<attribute name="optional" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry kind="src" path="target/generated-sources/annotations">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="optional" value="true"/>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
<attribute name="ignore_optional_problems" value="true"/>
|
||||||
|
<attribute name="m2e-apt" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry kind="src" output="target/test-classes" path="target/generated-test-sources/test-annotations">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="optional" value="true"/>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
<attribute name="ignore_optional_problems" value="true"/>
|
||||||
|
<attribute name="m2e-apt" value="true"/>
|
||||||
|
<attribute name="test" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry kind="output" path="target/classes"/>
|
||||||
|
</classpath>
|
||||||
9
.env.example
Normal file
9
.env.example
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Dev-server config for this project's tools/ scripts.
|
||||||
|
# Plain KEY=value only (no shell expansion). Environment variables take precedence.
|
||||||
|
|
||||||
|
PAPER_VERSION=26.2
|
||||||
|
PAPER_BUILD=129
|
||||||
|
MINECRAFT_SERVER_JAR=paper-26.2-129.jar
|
||||||
|
# Relative paths are resolved from this directory; ~/ is expanded.
|
||||||
|
MINECRAFT_SERVER_PATH=./server
|
||||||
|
MINECRAFT_BACKUP_PATH=./backups
|
||||||
15
.gitignore
vendored
Normal file
15
.gitignore
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
.gradle/
|
||||||
|
build/
|
||||||
|
target/
|
||||||
|
.worktrees/
|
||||||
|
bin/
|
||||||
|
/backups/
|
||||||
|
/server/
|
||||||
|
.env
|
||||||
|
|
||||||
|
# Old entries double check these later.
|
||||||
|
/server
|
||||||
|
/Plugin.jardesc
|
||||||
|
/Plugin-Release.jardesc
|
||||||
|
/lib
|
||||||
|
/target
|
||||||
@@ -20,4 +20,15 @@
|
|||||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||||
</natures>
|
</natures>
|
||||||
|
<filteredResources>
|
||||||
|
<filter>
|
||||||
|
<id>1790453742830</id>
|
||||||
|
<name></name>
|
||||||
|
<type>30</type>
|
||||||
|
<matcher>
|
||||||
|
<id>org.eclipse.core.resources.regexFilterMatcher</id>
|
||||||
|
<arguments>node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
|
||||||
|
</matcher>
|
||||||
|
</filter>
|
||||||
|
</filteredResources>
|
||||||
</projectDescription>
|
</projectDescription>
|
||||||
2
.settings/org.eclipse.jdt.apt.core.prefs
Normal file
2
.settings/org.eclipse.jdt.apt.core.prefs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
eclipse.preferences.version=1
|
||||||
|
org.eclipse.jdt.apt.aptEnabled=false
|
||||||
@@ -1,13 +1,16 @@
|
|||||||
#Sat Mar 03 21:02:20 PST 2012
|
|
||||||
eclipse.preferences.version=1
|
eclipse.preferences.version=1
|
||||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
|
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
|
||||||
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
||||||
org.eclipse.jdt.core.compiler.compliance=1.6
|
org.eclipse.jdt.core.compiler.compliance=1.8
|
||||||
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
||||||
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
||||||
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
||||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||||
|
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
|
||||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||||
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
|
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
|
||||||
org.eclipse.jdt.core.compiler.source=1.6
|
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore
|
||||||
|
org.eclipse.jdt.core.compiler.processAnnotations=disabled
|
||||||
|
org.eclipse.jdt.core.compiler.release=disabled
|
||||||
|
org.eclipse.jdt.core.compiler.source=1.8
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<classpath>
|
|
||||||
<classpathentry kind="src" output="target/classes" path="src/main/java"/>
|
|
||||||
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources"/>
|
|
||||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
|
|
||||||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"/>
|
|
||||||
<classpathentry kind="output" path="target/classes"/>
|
|
||||||
</classpath>
|
|
||||||
5
Metropolis/.gitignore
vendored
5
Metropolis/.gitignore
vendored
@@ -1,5 +0,0 @@
|
|||||||
/server
|
|
||||||
/Plugin.jardesc
|
|
||||||
/Plugin-Release.jardesc
|
|
||||||
/lib
|
|
||||||
/target
|
|
||||||
25
Project-Description.json
Normal file
25
Project-Description.json
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"id": "metropolis",
|
||||||
|
"title": "Metropolis",
|
||||||
|
"description": "Legacy Bukkit plugin that automatically creates protected home plots and roads as players join a Minecraft server.",
|
||||||
|
"docId": "Metropolis",
|
||||||
|
"image": {
|
||||||
|
"url": "Pixel-art aerial view of a growing Minecraft city planned in a square grid, small player home plots connected by pale roads, a protected central spawn, blocky green landscape, subtle WorldGuard-inspired boundary lines, warm daylight, clean game-plugin thumbnail composition, no words or logos.",
|
||||||
|
"alt": "Pixel-art aerial view of a Minecraft city with player home plots and connecting roads",
|
||||||
|
"prompt": "Pixel-art aerial view of a growing Minecraft city planned in a square grid, small player home plots connected by pale roads, a protected central spawn, blocky green landscape, subtle WorldGuard-inspired boundary lines, warm daylight, clean game-plugin thumbnail composition, no words or logos.",
|
||||||
|
"width": 0,
|
||||||
|
"height": 0,
|
||||||
|
"backgroundClassName": "bg-surface"
|
||||||
|
},
|
||||||
|
"url": "/projects/metropolis",
|
||||||
|
"tags": [
|
||||||
|
"open-source"
|
||||||
|
],
|
||||||
|
"source": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/headhunter45/Metropolis"
|
||||||
|
},
|
||||||
|
"feedback": {
|
||||||
|
"url": "https://github.com/headhunter45/Metropolis/issues"
|
||||||
|
}
|
||||||
|
}
|
||||||
18
Project-Description.md
Normal file
18
Project-Description.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Metropolis
|
||||||
|
|
||||||
|
Metropolis is a legacy Bukkit plugin that automatically expands a protected Minecraft city as players join the server. Each player receives a home plot surrounded by roads and positioned beside the existing city, with WorldGuard regions protecting the city and individual homes.
|
||||||
|
|
||||||
|
## Details
|
||||||
|
|
||||||
|
- Platform: Bukkit/Minecraft server plugin
|
||||||
|
- Language: Java
|
||||||
|
- Bukkit API: 1.7.9-R0.2
|
||||||
|
- Dependencies: WorldGuard 5.9 and WorldEdit 5.6.3
|
||||||
|
- Features: automatic home plots, road and plot generation, WorldGuard region management, reserved plots, home teleportation, and configurable plot and road materials
|
||||||
|
- Configuration: `plugins/Metropolis/config.yml`
|
||||||
|
- Source: https://github.com/headhunter45/Metropolis
|
||||||
|
- License: GNU General Public License v3
|
||||||
|
|
||||||
|
## Image prompt
|
||||||
|
|
||||||
|
Pixel-art aerial view of a growing Minecraft city planned in a square grid, small player home plots connected by pale roads, a protected central spawn, blocky green landscape, subtle WorldGuard-inspired boundary lines, warm daylight, clean game-plugin thumbnail composition, no words or logos.
|
||||||
124
TODO.md
Normal file
124
TODO.md
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
# Modernize Metropolis Plugin: Implementation Tasks
|
||||||
|
|
||||||
|
| ID | Status | Title |
|
||||||
|
|:-------:|:--------:|:------|
|
||||||
|
| MET-000 | Ready | Add Gradle plugins as needed (e.g., Shadow) |
|
||||||
|
| MET-000 | Ready | Add repositories and dependencies |
|
||||||
|
| MET-000 | Ready | Address any bugs or incompatibilities found during testing on a modern server.
|
||||||
|
| MET-000 | Ready | Analyze all Java source files for deprecated or removed Bukkit API usage, including logger, event registration, listener classes, configuration API, and entity/player mapping. |
|
||||||
|
| MET-000 | Ready | Build the plugin with Gradle (./gradlew build) and test on a modern Paper server using the provided bash scripts.
|
||||||
|
| MET-000 | Ready | Configure Java version |
|
||||||
|
| MET-000 | Ready | Ensure all commands are properly defined in plugin.yml |
|
||||||
|
| MET-000 | Ready | Ensure plugin.yml is present in src/main/resources and update for PaperMC compatibility (api-version, commands, required fields). |
|
||||||
|
| MET-000 | Ready | Ensure resource handling for plugin.yml |
|
||||||
|
| MET-000 | Ready | Identify and document any hardcoded Bukkit references or legacy patterns (e.g., Player as HashMap key, old event registration, CraftBukkit class names). |
|
||||||
|
| MET-000 | Ready | Initialize Gradle in the project root and create a build.gradle file with project metadata, PaperMC API dependency, JUnit, Java toolchain, repository, resource handling, and plugins as needed. |
|
||||||
|
| MET-000 | Ready | Refactor all event listeners to use the modern event system (Listener interface, @EventHandler, registerEvents).
|
||||||
|
| MET-000 | Ready | Refactor all logger usage to use getLogger() from JavaPlugin. |
|
||||||
|
| MET-000 | Ready | Remove Maven-specific files (pom.xml, .mvn/ directory, Maven wrapper scripts) and Eclipse-specific files (.classpath, .project, .settings/) if present. |
|
||||||
|
| MET-000 | Ready | Remove or modernize any old/deprecated event registration (use @EventHandler and registerEvents) |
|
||||||
|
| MET-000 | Ready | Remove or refactor any code using deprecated or removed Bukkit/Spigot/Paper APIs that are not available in the modern Paper API.
|
||||||
|
| MET-000 | Ready | Replace all usage of org.bukkit.util.config.Configuration with the modern Bukkit configuration API (getConfig(), saveConfig(), reloadConfig(), etc.).
|
||||||
|
| MET-000 | Ready | Review and list any custom scripts or resources (e.g., bash scripts in tools/bash/) that may need migration or updates.|
|
||||||
|
| MET-000 | Ready | Review and update player lookup logic to use getPlayerExact or handle case sensitivity |
|
||||||
|
| MET-000 | Ready | Set project metadata in build.gradle |
|
||||||
|
| MET-000 | Ready | Switch all player score storage to use UUID instead of Player as the key |
|
||||||
|
| MET-000 | Ready | Test the Gradle build |
|
||||||
|
| MET-000 | Ready | Update .gitignore to add Gradle-specific ignores and remove Maven/Eclipse-specific ignores. |
|
||||||
|
| MET-000 | Ready | Update README.md and CONTRIBUTING.md with new build, usage, and development instructions. _(Depends on: bugfixes)_
|
||||||
|
| MET-000 | Ready | Update documentation and scripts |
|
||||||
|
| MET-000 | Ready | Update logger usage to use getLogger() from JavaPlugin |
|
||||||
|
| MET-000 | Ready | (Optional) Add new features, quality-of-life improvements, automated tests, or CI configuration. _(Depends on: docs update)_
|
||||||
|
| GIT-009 | Planning | i have three reserve plots and one just got overwritten when a new player joined. |
|
||||||
|
| GIT-008 | Ready | Implement multiple plot sizes by granting multiple plots around each other.
|
||||||
|
| GIT-007 | Ready | Allow buying and selling of homes via an ecnoomy plugin. |
|
||||||
|
| GIT-006 | Ready | Add levels support. |
|
||||||
|
| GIT-005 | Planning | Add the possibility to create multiple plot homes in shapes. |
|
||||||
|
| GIT-004 | Ready | Add a command to request home allocation. |
|
||||||
|
| GIT-003 | Ready | Add support for multiple homes. |
|
||||||
|
| GIT-002 | Ready | Add a command to teleport to a reservation you are part of. |
|
||||||
|
| GIT-001 | Ready | Add a command to move to a different home. |
|
||||||
|
|
||||||
|
# GIT-001 - Add a command to move to a different home.
|
||||||
|
**Status:** Ready
|
||||||
|
**Descriptioon:**
|
||||||
|
|
||||||
|
/metropolis-home-move [playerName]
|
||||||
|
The target player will be the callee or if specified [playerName].
|
||||||
|
This command should set the target player's home to the plot defined by .
|
||||||
|
|
||||||
|
# GIT-002 - Add a command to teleport to a reservation you are part of.
|
||||||
|
**Status:** Ready
|
||||||
|
**Description:**
|
||||||
|
|
||||||
|
/metropolis-plot-go [playerName]
|
||||||
|
The target player should be [playerName] if specified and otherwise should be the callee.
|
||||||
|
The reservation to teleport to is
|
||||||
|
|
||||||
|
### Comments
|
||||||
|
|
||||||
|
Or better yet just use uhome or create your own, integrated that records the home location at the height set in the configuration file, but on the road next to the plot. Using existing plugins to insert into SQL say uhome, would allow privatization and minimal work on your end.
|
||||||
|
|
||||||
|
# GIT-003 - Add support for multiple homes
|
||||||
|
|
||||||
|
**Status:** Ready
|
||||||
|
**Description:**
|
||||||
|
|
||||||
|
The max number of homes per player should be defined by a permission such as
|
||||||
|
metropolis.maxhomes: 2
|
||||||
|
|
||||||
|
A config option should be added to determine when the homes are added with at least the following options
|
||||||
|
|
||||||
|
Allocate all homes that aren't allocated on login
|
||||||
|
Allocate at most 1 home per login
|
||||||
|
Only allocate the first home on login, and require a command to allocate additional homes.
|
||||||
|
|
||||||
|
# GIT-004 - Add a command to request home allocation.
|
||||||
|
**Status:** Ready
|
||||||
|
**Description:**
|
||||||
|
|
||||||
|
Add a command that users can type to get their first or subsequent homes.
|
||||||
|
Add a config option to not generate any homes automatically, but to require this command to generate a user's first home.
|
||||||
|
|
||||||
|
This could be used so that homes won't be generated until a user has read the server rules which would include telling them to type the command towards the end.
|
||||||
|
|
||||||
|
# GIT-005 - Add the possibility to create multiple plot homes in shapes.
|
||||||
|
**Status:** Planning
|
||||||
|
**Description:**
|
||||||
|
|
||||||
|
Add the possibility to create multiple plot homes in shapes
|
||||||
|
|
||||||
|
|
||||||
|
# GIT-006 - Add levels support.
|
||||||
|
**Status:** Ready
|
||||||
|
**Description:**
|
||||||
|
|
||||||
|
Allow generating multilevel cities with stairs or interchanges to go between levels (streets are level but avenues are ramps.
|
||||||
|
|
||||||
|
# GIT-007 - Allow buying and selling of homes via an ecnoomy plugin.
|
||||||
|
**Status:** Ready
|
||||||
|
**Description:**
|
||||||
|
|
||||||
|
Allow buying and selling of homes via an ecnoomy plugin. Include decent real estate support including wtb and wts listings as well as an auction facility.
|
||||||
|
|
||||||
|
This should also support not giving free plots to users with none. This way one would have to go out and mine to earn money which could be spent to purchase a plot.
|
||||||
|
|
||||||
|
# GIT-008 - Implement multiple plot sizes by granting multiple plots around each other.
|
||||||
|
**Status:** Ready
|
||||||
|
**Description:**
|
||||||
|
|
||||||
|
a= user a
|
||||||
|
x= empty plot
|
||||||
|
s= spawn
|
||||||
|
|
||||||
|
user a get's a small plot which is defined as 1 plot
|
||||||
|
a x x
|
||||||
|
x s x
|
||||||
|
x x x
|
||||||
|
|
||||||
|
user b gets a large plot which is defined as 4 plots
|
||||||
|
x x b b x
|
||||||
|
x a b b x
|
||||||
|
x x s x x
|
||||||
|
x x x x x
|
||||||
|
x x x x x
|
||||||
64
tools/backup-server.sh
Executable file
64
tools/backup-server.sh
Executable file
@@ -0,0 +1,64 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Backs up plugins, config, worlds, and settings from MINECRAFT_SERVER_PATH
|
||||||
|
# to MINECRAFT_BACKUP_PATH/backup-<timestamp>[-label].tar.bz2.
|
||||||
|
#
|
||||||
|
# Usage: backup-server.sh [-f|--force] [label]
|
||||||
|
# -f Back up even if the server is running (worlds may be inconsistent).
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
TOOLS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
# shellcheck source=setup-env.sh
|
||||||
|
source "$TOOLS_DIR/setup-env.sh"
|
||||||
|
|
||||||
|
FORCE=false
|
||||||
|
LABEL=""
|
||||||
|
for arg in "$@"; do
|
||||||
|
case "$arg" in
|
||||||
|
-f|--force) FORCE=true ;;
|
||||||
|
-h|--help) sed -n '2,7p' "$0"; exit 0 ;;
|
||||||
|
-*) echo "Unknown option: $arg" >&2; exit 1 ;;
|
||||||
|
*) LABEL="$arg" ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
[[ -d "$MINECRAFT_SERVER_PATH" ]] || { echo "Error: $MINECRAFT_SERVER_PATH does not exist." >&2; exit 1; }
|
||||||
|
|
||||||
|
if pgrep -f "java.*paper-.*\.jar" >/dev/null && ! $FORCE; then
|
||||||
|
echo "Error: the server is running. Stop it first (tools/stop-server.sh) or pass --force." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$MINECRAFT_SERVER_PATH"
|
||||||
|
shopt -s nullglob
|
||||||
|
|
||||||
|
ITEMS=()
|
||||||
|
[[ -d plugins ]] && ITEMS+=(plugins)
|
||||||
|
[[ -d config ]] && ITEMS+=(config)
|
||||||
|
for dir in */; do
|
||||||
|
dir="${dir%/}"
|
||||||
|
[[ -f "$dir/level.dat" ]] && ITEMS+=("$dir")
|
||||||
|
done
|
||||||
|
for f in *.yml *.yaml *.json *.properties eula.txt; do
|
||||||
|
ITEMS+=("$f")
|
||||||
|
done
|
||||||
|
|
||||||
|
if (( ${#ITEMS[@]} == 0 )); then
|
||||||
|
echo "Error: nothing to back up in $MINECRAFT_SERVER_PATH." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Keep labels filename-safe.
|
||||||
|
LABEL="${LABEL//[^A-Za-z0-9._-]/_}"
|
||||||
|
NAME="backup-$(date +%Y%m%d-%H%M%S)${LABEL:+-$LABEL}.tar.bz2"
|
||||||
|
mkdir -p "$MINECRAFT_BACKUP_PATH"
|
||||||
|
ARCHIVE="$MINECRAFT_BACKUP_PATH/$NAME"
|
||||||
|
|
||||||
|
echo "Backing up: ${ITEMS[*]}"
|
||||||
|
trap 'rm -f "$ARCHIVE.part"' EXIT
|
||||||
|
tar --exclude 'plugins/.paper-remapped' --exclude '*/session.lock' \
|
||||||
|
-cjf "$ARCHIVE.part" "${ITEMS[@]}"
|
||||||
|
mv "$ARCHIVE.part" "$ARCHIVE"
|
||||||
|
trap - EXIT
|
||||||
|
|
||||||
|
echo "Created $ARCHIVE ($(du -h "$ARCHIVE" | awk '{print $1}'))"
|
||||||
14
tools/build-plugin.sh
Executable file
14
tools/build-plugin.sh
Executable file
@@ -0,0 +1,14 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Find project root (one directory above this script)
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||||
|
|
||||||
|
cd "$PROJECT_ROOT"
|
||||||
|
|
||||||
|
if [[ -x ./mvnw ]]; then
|
||||||
|
./mvnw package
|
||||||
|
else
|
||||||
|
mvn package
|
||||||
|
fi
|
||||||
29
tools/deploy-plugin.sh
Executable file
29
tools/deploy-plugin.sh
Executable file
@@ -0,0 +1,29 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||||
|
|
||||||
|
# shellcheck source=setup-env.sh
|
||||||
|
source "$SCRIPT_DIR/setup-env.sh"
|
||||||
|
|
||||||
|
if [[ -z "$MINECRAFT_SERVER_PATH" ]]; then
|
||||||
|
echo "Error: MINECRAFT_SERVER_PATH environment variable is not set."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Skip Maven side artifacts (shade originals, sources, javadoc).
|
||||||
|
PLUGIN_JAR=$(ls -t "$PROJECT_ROOT"/target/*.jar 2>/dev/null \
|
||||||
|
| grep -vE '/original-[^/]*\.jar$|-(sources|javadoc|tests)\.jar$' | head -n1 || true)
|
||||||
|
if [[ ! -f "$PLUGIN_JAR" ]]; then
|
||||||
|
echo "Error: No plugin jar found in $PROJECT_ROOT/target. Build the plugin first."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if find "$PROJECT_ROOT/src/main/java" "$PROJECT_ROOT/src/main/resources" -type f -newer "$PLUGIN_JAR" | grep -q .; then
|
||||||
|
echo "Warning: The built plugin jar is older than some source files. Consider rebuilding."
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$MINECRAFT_SERVER_PATH/plugins"
|
||||||
|
cp "$PLUGIN_JAR" "$MINECRAFT_SERVER_PATH/plugins/"
|
||||||
|
echo "Deployed $PLUGIN_JAR to $MINECRAFT_SERVER_PATH/plugins/"
|
||||||
49
tools/publish-local.sh
Executable file
49
tools/publish-local.sh
Executable file
@@ -0,0 +1,49 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||||
|
|
||||||
|
# Use the same local JDK bootstrap pattern as the Gradle projects.
|
||||||
|
export GRADLE_USER_HOME="${GRADLE_USER_HOME:-$PROJECT_ROOT/.gradle/user-home}"
|
||||||
|
mkdir -p "$GRADLE_USER_HOME/jdks"
|
||||||
|
|
||||||
|
ensure_java_25() {
|
||||||
|
local version="25"
|
||||||
|
local os arch archive java_bin actual_home
|
||||||
|
|
||||||
|
case "$(uname -s)" in
|
||||||
|
Darwin) os="mac" ;;
|
||||||
|
Linux) os="linux" ;;
|
||||||
|
*) echo "Unsupported OS: $(uname -s)" >&2; exit 1 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
case "$(uname -m)" in
|
||||||
|
arm64|aarch64) arch="aarch64" ;;
|
||||||
|
x86_64|amd64) arch="x64" ;;
|
||||||
|
*) echo "Unsupported architecture: $(uname -m)" >&2; exit 1 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
java_bin=$(find "$GRADLE_USER_HOME/jdks" -path '*/bin/java' -type f 2>/dev/null | head -n 1 || true)
|
||||||
|
if [[ -z "$java_bin" ]]; then
|
||||||
|
echo "Downloading Temurin JDK $version into $GRADLE_USER_HOME/jdks..."
|
||||||
|
archive="$GRADLE_USER_HOME/jdks/temurin-$version.tar.gz"
|
||||||
|
curl -fsSL "https://api.adoptium.net/v3/binary/latest/$version/ga/${os}/${arch}/jdk/hotspot/normal/eclipse" -o "$archive"
|
||||||
|
tar -xzf "$archive" -C "$GRADLE_USER_HOME/jdks"
|
||||||
|
rm -f "$archive"
|
||||||
|
java_bin=$(find "$GRADLE_USER_HOME/jdks" -path '*/bin/java' -type f 2>/dev/null | head -n 1 || true)
|
||||||
|
fi
|
||||||
|
|
||||||
|
[[ -n "$java_bin" ]] || { echo "Error: could not locate installed JDK 25." >&2; exit 1; }
|
||||||
|
actual_home="$(dirname "$(dirname "$java_bin")")"
|
||||||
|
export JAVA_HOME="$actual_home"
|
||||||
|
export PATH="$JAVA_HOME/bin:$PATH"
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure_java_25
|
||||||
|
cd "$PROJECT_ROOT"
|
||||||
|
if [[ -x ./mvnw ]]; then
|
||||||
|
./mvnw install -DskipTests "$@"
|
||||||
|
else
|
||||||
|
mvn install -DskipTests "$@"
|
||||||
|
fi
|
||||||
54
tools/publish-modrinth.sh
Executable file
54
tools/publish-modrinth.sh
Executable file
@@ -0,0 +1,54 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||||
|
|
||||||
|
# shellcheck source=setup-env.sh
|
||||||
|
source "$SCRIPT_DIR/setup-env.sh"
|
||||||
|
|
||||||
|
MODRINTH_TOKEN="${MODRINTH_TOKEN:-}"
|
||||||
|
MODRINTH_PROJECT_ID="${MODRINTH_PROJECT_ID:-${MODRINTH_PROJECT_SLUG:-metropolis-builder}}"
|
||||||
|
MODRINTH_VERSION="${MODRINTH_VERSION:-${VERSION:-$(grep -E '<version>|<project>' pom.xml | head -n2 | tail -n1 | sed -E 's#.*<version>([^<]+)</version>.*#\1#')}}"
|
||||||
|
MODRINTH_CHANGELOG="${MODRINTH_CHANGELOG:-Auto release via publish-modrinth.sh}"
|
||||||
|
MODRINTH_GAME_VERSIONS="${MODRINTH_GAME_VERSIONS:-1.21,1.21.1,1.21.2,1.21.3,1.21.4,1.21.5,1.21.6,1.21.7}"
|
||||||
|
MODRINTH_LOADERS="${MODRINTH_LOADERS:-paper}"
|
||||||
|
MODRINTH_FILE="${MODRINTH_FILE:-}"
|
||||||
|
MODRINTH_DEPENDENCIES="${MODRINTH_DEPENDENCIES:-}"
|
||||||
|
|
||||||
|
if [[ -z "$MODRINTH_TOKEN" ]]; then
|
||||||
|
echo "Error: MODRINTH_TOKEN is not set in $PROJECT_ROOT/.env" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$MODRINTH_FILE" ]]; then
|
||||||
|
MODRINTH_FILE=$(find "$PROJECT_ROOT/target" -maxdepth 1 -type f -name '*.jar' ! -name '*-sources.jar' ! -name '*-javadoc.jar' | head -n1 || true)
|
||||||
|
fi
|
||||||
|
if [[ -z "$MODRINTH_FILE" || ! -f "$MODRINTH_FILE" ]]; then
|
||||||
|
echo "Error: no jar found for Modrinth upload. Build the project first or set MODRINTH_FILE." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
csv_to_json_array() {
|
||||||
|
jq -cn --arg csv "$1" '$csv | split(",") | map(gsub("^\\s+|\\s+$"; "")) | map(select(length > 0))'
|
||||||
|
}
|
||||||
|
MODRINTH_GAME_VERSIONS_JSON=$(csv_to_json_array "$MODRINTH_GAME_VERSIONS")
|
||||||
|
MODRINTH_LOADERS_JSON=$(csv_to_json_array "$MODRINTH_LOADERS")
|
||||||
|
|
||||||
|
if [[ -n "$MODRINTH_DEPENDENCIES" ]]; then
|
||||||
|
MODRINTH_DEPENDENCIES_ARG=(--form "dependencies=$MODRINTH_DEPENDENCIES")
|
||||||
|
else
|
||||||
|
MODRINTH_DEPENDENCIES_ARG=()
|
||||||
|
fi
|
||||||
|
|
||||||
|
curl -fsSL \
|
||||||
|
-X POST "https://api.modrinth.com/v2/version" \
|
||||||
|
-H "Authorization: $MODRINTH_TOKEN" \
|
||||||
|
-F "project_id=$MODRINTH_PROJECT_ID" \
|
||||||
|
-F "version_number=$MODRINTH_VERSION" \
|
||||||
|
-F "changelog=$MODRINTH_CHANGELOG" \
|
||||||
|
-F "game_versions=${MODRINTH_GAME_VERSIONS_JSON}" \
|
||||||
|
-F "loaders=${MODRINTH_LOADERS_JSON}" \
|
||||||
|
-F "file=@$MODRINTH_FILE" \
|
||||||
|
"${MODRINTH_DEPENDENCIES_ARG[@]}" \
|
||||||
|
| jq .
|
||||||
68
tools/restore-server.sh
Executable file
68
tools/restore-server.sh
Executable file
@@ -0,0 +1,68 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Restores a backup made by backup-server.sh into MINECRAFT_SERVER_PATH.
|
||||||
|
# Each top-level item in the archive (plugins, config, worlds, settings) is
|
||||||
|
# replaced; a "pre-restore" backup of the current state is taken first.
|
||||||
|
#
|
||||||
|
# Usage: restore-server.sh [-y|--yes] [--no-backup] [backup-file|name]
|
||||||
|
# Defaults to the newest backup in MINECRAFT_BACKUP_PATH.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
TOOLS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
# shellcheck source=setup-env.sh
|
||||||
|
source "$TOOLS_DIR/setup-env.sh"
|
||||||
|
|
||||||
|
ASSUME_YES=false
|
||||||
|
SAFETY_BACKUP=true
|
||||||
|
ARCHIVE=""
|
||||||
|
for arg in "$@"; do
|
||||||
|
case "$arg" in
|
||||||
|
-y|--yes) ASSUME_YES=true ;;
|
||||||
|
--no-backup) SAFETY_BACKUP=false ;;
|
||||||
|
-h|--help) sed -n '2,8p' "$0"; exit 0 ;;
|
||||||
|
-*) echo "Unknown option: $arg" >&2; exit 1 ;;
|
||||||
|
*) ARCHIVE="$arg" ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -z "$ARCHIVE" ]]; then
|
||||||
|
ARCHIVE=$(ls -t "$MINECRAFT_BACKUP_PATH"/backup-*.tar.bz2 2>/dev/null | head -n1 || true)
|
||||||
|
[[ -n "$ARCHIVE" ]] || { echo "Error: no backups found in $MINECRAFT_BACKUP_PATH." >&2; exit 1; }
|
||||||
|
elif [[ ! -f "$ARCHIVE" && -f "$MINECRAFT_BACKUP_PATH/$ARCHIVE" ]]; then
|
||||||
|
ARCHIVE="$MINECRAFT_BACKUP_PATH/$ARCHIVE"
|
||||||
|
fi
|
||||||
|
[[ -f "$ARCHIVE" ]] || { echo "Error: backup not found: $ARCHIVE" >&2; exit 1; }
|
||||||
|
ARCHIVE="$(cd "$(dirname "$ARCHIVE")" && pwd)/$(basename "$ARCHIVE")"
|
||||||
|
|
||||||
|
if pgrep -f "java.*paper-.*\.jar" >/dev/null; then
|
||||||
|
echo "Error: the server is running. Stop it first (tools/stop-server.sh)." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ENTRIES=$(tar -tjf "$ARCHIVE")
|
||||||
|
if grep -qE '^/|(^|/)\.\.(/|$)' <<<"$ENTRIES"; then
|
||||||
|
echo "Error: archive contains absolute or '..' paths; refusing to restore." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
TOP_LEVEL=$(sed -E 's#^\./##; s#/.*##' <<<"$ENTRIES" | { grep -vE '^\.?$' || true; } | sort -u)
|
||||||
|
[[ -n "$TOP_LEVEL" ]] || { echo "Error: backup is empty." >&2; exit 1; }
|
||||||
|
|
||||||
|
echo "Restore $(basename "$ARCHIVE") into $MINECRAFT_SERVER_PATH"
|
||||||
|
echo "These items will be replaced:"
|
||||||
|
sed 's/^/ /' <<<"$TOP_LEVEL"
|
||||||
|
if ! $ASSUME_YES; then
|
||||||
|
read -r -p "Continue? [y/N] " reply
|
||||||
|
[[ "$reply" =~ ^[Yy]$ ]] || { echo "Aborted."; exit 1; }
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$MINECRAFT_SERVER_PATH"
|
||||||
|
if $SAFETY_BACKUP && [[ -n "$(ls -A "$MINECRAFT_SERVER_PATH")" ]]; then
|
||||||
|
"$TOOLS_DIR/backup-server.sh" pre-restore
|
||||||
|
fi
|
||||||
|
|
||||||
|
while IFS= read -r item; do
|
||||||
|
rm -rf "${MINECRAFT_SERVER_PATH:?}/$item"
|
||||||
|
done <<<"$TOP_LEVEL"
|
||||||
|
|
||||||
|
tar -xjf "$ARCHIVE" -C "$MINECRAFT_SERVER_PATH"
|
||||||
|
echo "Restored $(basename "$ARCHIVE")."
|
||||||
39
tools/setup-env.sh
Executable file
39
tools/setup-env.sh
Executable file
@@ -0,0 +1,39 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Source (bash or zsh) to export the shared server config from ../.env
|
||||||
|
# (relative to this script):
|
||||||
|
# PAPER_VERSION, PAPER_BUILD, MINECRAFT_SERVER_PATH, MINECRAFT_SERVER_JAR, MINECRAFT_BACKUP_PATH
|
||||||
|
|
||||||
|
_setup_env_self="${BASH_SOURCE[0]:-${(%):-%x}}"
|
||||||
|
_setup_env_root="$(cd "$(dirname "$_setup_env_self")/.." && pwd)"
|
||||||
|
_setup_env_file="${MINECRAFT_ENV_FILE:-$_setup_env_root/.env}"
|
||||||
|
|
||||||
|
if [[ -f "$_setup_env_file" ]]; then
|
||||||
|
while IFS= read -r _line || [[ -n "$_line" ]]; do
|
||||||
|
_line="${_line%$'\r'}"
|
||||||
|
[[ "$_line" =~ ^[[:space:]]*(#|$) ]] && continue
|
||||||
|
_key="${_line%%=*}"
|
||||||
|
_value="${_line#*=}"
|
||||||
|
_key="${_key//[[:space:]]/}"
|
||||||
|
[[ "$_key" =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]] || continue
|
||||||
|
_value="${_value#\"}"; _value="${_value%\"}"
|
||||||
|
_value="${_value#\'}"; _value="${_value%\'}"
|
||||||
|
eval "_current=\"\${$_key:-}\""
|
||||||
|
[[ -z "$_current" ]] && export "$_key=$_value"
|
||||||
|
done < "$_setup_env_file"
|
||||||
|
fi
|
||||||
|
|
||||||
|
PAPER_VERSION="${PAPER_VERSION:-}"
|
||||||
|
PAPER_BUILD="${PAPER_BUILD:-}"
|
||||||
|
MINECRAFT_SERVER_PATH="${MINECRAFT_SERVER_PATH:-MCServer}"
|
||||||
|
MINECRAFT_SERVER_PATH="${MINECRAFT_SERVER_PATH/#\~/$HOME}"
|
||||||
|
[[ "$MINECRAFT_SERVER_PATH" == /* ]] || MINECRAFT_SERVER_PATH="$_setup_env_root/$MINECRAFT_SERVER_PATH"
|
||||||
|
MINECRAFT_BACKUP_PATH="${MINECRAFT_BACKUP_PATH:-backups}"
|
||||||
|
MINECRAFT_BACKUP_PATH="${MINECRAFT_BACKUP_PATH/#\~/$HOME}"
|
||||||
|
[[ "$MINECRAFT_BACKUP_PATH" == /* ]] || MINECRAFT_BACKUP_PATH="$_setup_env_root/$MINECRAFT_BACKUP_PATH"
|
||||||
|
|
||||||
|
if [[ -z "${MINECRAFT_SERVER_JAR:-}" && -n "$PAPER_VERSION" && -n "$PAPER_BUILD" ]]; then
|
||||||
|
MINECRAFT_SERVER_JAR="paper-${PAPER_VERSION}-${PAPER_BUILD}.jar"
|
||||||
|
fi
|
||||||
|
|
||||||
|
export PAPER_VERSION PAPER_BUILD MINECRAFT_SERVER_PATH MINECRAFT_SERVER_JAR MINECRAFT_BACKUP_PATH
|
||||||
|
unset _setup_env_self _setup_env_root _setup_env_file _line _key _value _current
|
||||||
92
tools/start-server.sh
Executable file
92
tools/start-server.sh
Executable file
@@ -0,0 +1,92 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Downloads (if missing) and runs a Paper server via the Fill v3 API.
|
||||||
|
# Server settings come from ../.env relative to this script (see setup-env.sh).
|
||||||
|
#
|
||||||
|
# Env vars:
|
||||||
|
# JAVA_OPTS Extra JVM args (default: -Xms2G -Xmx2G)
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
TOOLS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
# shellcheck source=setup-env.sh
|
||||||
|
source "$TOOLS_DIR/setup-env.sh"
|
||||||
|
|
||||||
|
[[ -n "$PAPER_VERSION" ]] || { echo "Error: PAPER_VERSION is not set (see .env)." >&2; exit 1; }
|
||||||
|
|
||||||
|
PAPER_PROJECT="paper"
|
||||||
|
JAVA_OPTS="${JAVA_OPTS:--Xms2G -Xmx2G}"
|
||||||
|
FILL_API="https://fill.papermc.io/v3/projects/${PAPER_PROJECT}/versions/${PAPER_VERSION}"
|
||||||
|
# Fill requires a non-generic User-Agent with contact info.
|
||||||
|
USER_AGENT="${PAPER_USER_AGENT:-majinnaibu-plugin-dev/1.0 (https://github.com/majinnaibu)}"
|
||||||
|
|
||||||
|
for cmd in curl jq java shasum; do
|
||||||
|
command -v "$cmd" >/dev/null || { echo "Error: '$cmd' is required." >&2; exit 1; }
|
||||||
|
done
|
||||||
|
|
||||||
|
api_get() {
|
||||||
|
curl -fsSL -H "User-Agent: $USER_AGENT" "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
mkdir -p "$MINECRAFT_SERVER_PATH"
|
||||||
|
cd "$MINECRAFT_SERVER_PATH"
|
||||||
|
|
||||||
|
JAR_BUILD="${MINECRAFT_SERVER_JAR:-}"
|
||||||
|
JAR_BUILD="${JAR_BUILD#paper-"$PAPER_VERSION"-}"; JAR_BUILD="${JAR_BUILD%.jar}"
|
||||||
|
if [[ -z "$PAPER_BUILD" && "$JAR_BUILD" =~ ^[0-9]+$ ]]; then
|
||||||
|
PAPER_BUILD="$JAR_BUILD"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$PAPER_BUILD" ]]; then
|
||||||
|
echo "Resolving latest stable build for Paper $PAPER_VERSION..."
|
||||||
|
PAPER_BUILD=$(api_get "$FILL_API/builds" \
|
||||||
|
| jq -r 'map(select(.channel == "STABLE")) | max_by(.id) | .id // empty') || true
|
||||||
|
if [[ -z "$PAPER_BUILD" ]]; then
|
||||||
|
# Offline or no stable build: fall back to the newest local jar for this version.
|
||||||
|
for f in paper-"$PAPER_VERSION"-*.jar; do
|
||||||
|
b="${f#paper-"$PAPER_VERSION"-}"; b="${b%.jar}"
|
||||||
|
[[ "$b" =~ ^[0-9]+$ ]] && (( b > ${PAPER_BUILD:-0} )) && PAPER_BUILD="$b"
|
||||||
|
done
|
||||||
|
[[ -n "$PAPER_BUILD" ]] || { echo "Error: no stable build found for $PAPER_VERSION." >&2; exit 1; }
|
||||||
|
echo "Could not resolve from API; using local build $PAPER_BUILD."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
EXPECTED_JAR="paper-${PAPER_VERSION}-${PAPER_BUILD}.jar"
|
||||||
|
if [[ -n "${MINECRAFT_SERVER_JAR:-}" && "$MINECRAFT_SERVER_JAR" != "$EXPECTED_JAR" ]]; then
|
||||||
|
echo "Error: MINECRAFT_SERVER_JAR=$MINECRAFT_SERVER_JAR does not match PAPER_VERSION/PAPER_BUILD ($EXPECTED_JAR)." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
MINECRAFT_SERVER_JAR="$EXPECTED_JAR"
|
||||||
|
|
||||||
|
if [[ ! -f "$MINECRAFT_SERVER_JAR" ]]; then
|
||||||
|
echo "Downloading $MINECRAFT_SERVER_JAR..."
|
||||||
|
BUILD_JSON=$(api_get "$FILL_API/builds/$PAPER_BUILD")
|
||||||
|
DOWNLOAD_URL=$(jq -r '.downloads."server:default".url // empty' <<<"$BUILD_JSON")
|
||||||
|
EXPECTED_SHA=$(jq -r '.downloads."server:default".checksums.sha256 // empty' <<<"$BUILD_JSON")
|
||||||
|
[[ -n "$DOWNLOAD_URL" ]] || { echo "Error: no download for $PAPER_VERSION build $PAPER_BUILD." >&2; exit 1; }
|
||||||
|
|
||||||
|
TMP_JAR="$MINECRAFT_SERVER_JAR.part"
|
||||||
|
trap 'rm -f "$TMP_JAR"' EXIT
|
||||||
|
curl -fL --progress-bar -H "User-Agent: $USER_AGENT" -o "$TMP_JAR" "$DOWNLOAD_URL"
|
||||||
|
|
||||||
|
ACTUAL_SHA=$(shasum -a 256 "$TMP_JAR" | awk '{print $1}')
|
||||||
|
if [[ -n "$EXPECTED_SHA" && "$ACTUAL_SHA" != "$EXPECTED_SHA" ]]; then
|
||||||
|
echo "Error: checksum mismatch (expected $EXPECTED_SHA, got $ACTUAL_SHA)." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
mv "$TMP_JAR" "$MINECRAFT_SERVER_JAR"
|
||||||
|
trap - EXIT
|
||||||
|
fi
|
||||||
|
|
||||||
|
export MINECRAFT_SERVER_PATH MINECRAFT_SERVER_JAR
|
||||||
|
|
||||||
|
"$TOOLS_DIR/stop-server.sh"
|
||||||
|
|
||||||
|
if ! grep -qs '^eula=true' eula.txt; then
|
||||||
|
echo "Note: accept the EULA by setting eula=true in $MINECRAFT_SERVER_PATH/eula.txt (server will exit until then)."
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Starting $MINECRAFT_SERVER_JAR in $MINECRAFT_SERVER_PATH..."
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
exec java $JAVA_OPTS -jar "$MINECRAFT_SERVER_JAR" --nogui
|
||||||
|
|
||||||
34
tools/stop-server.sh
Executable file
34
tools/stop-server.sh
Executable file
@@ -0,0 +1,34 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Stops any running Paper server (java process running a paper-*.jar).
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
STOP_TIMEOUT="${STOP_TIMEOUT:-60}"
|
||||||
|
|
||||||
|
SERVER_PIDS=$(pgrep -f "java.*paper-.*\.jar" || true)
|
||||||
|
|
||||||
|
if [[ -z "$SERVER_PIDS" ]]; then
|
||||||
|
echo "Minecraft server is not running."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
for pid in $SERVER_PIDS; do
|
||||||
|
echo "Stopping Minecraft server (PID: $pid)..."
|
||||||
|
# SIGTERM lets Paper run its shutdown hook and save worlds.
|
||||||
|
kill "$pid"
|
||||||
|
done
|
||||||
|
|
||||||
|
for ((i = 0; i < STOP_TIMEOUT; i++)); do
|
||||||
|
remaining=""
|
||||||
|
for pid in $SERVER_PIDS; do
|
||||||
|
kill -0 "$pid" 2>/dev/null && remaining="$remaining $pid"
|
||||||
|
done
|
||||||
|
if [[ -z "$remaining" ]]; then
|
||||||
|
echo "Minecraft server stopped."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Server did not stop within ${STOP_TIMEOUT}s; forcing:$remaining"
|
||||||
|
kill -9 $remaining
|
||||||
Reference in New Issue
Block a user