21 Commits

Author SHA1 Message Date
D.L.
3cb145e1d6 Update preview-release.yml 2025-10-14 17:55:34 +02:00
D.L.
cdf73058e6 Create release-on-merge.yml 2025-10-14 17:50:47 +02:00
D.L.
eb5ec93be5 Create preview-release.yml 2025-10-14 17:50:23 +02:00
D.L.
e90fa7fc44 Delete .github/workflows/gradle-publish.yml 2025-10-14 17:50:06 +02:00
D.L.
f19def6dc6 Update LICENSE 2025-09-01 13:37:43 +02:00
D.L.
427d71e2b8 Add files via upload 2025-09-01 08:00:16 +02:00
D.L.
feeae861ea Update index.html 2025-09-01 07:59:51 +02:00
D.L.
8c23b627c8 Update index.html 2025-09-01 07:56:28 +02:00
D.L.
54dc959395 Update index.html 2025-09-01 07:54:15 +02:00
D.L.
843d42b0d6 Create static.yml 2025-09-01 07:50:17 +02:00
D.L.
5b06b106da Create index.html 2025-09-01 07:48:45 +02:00
66c6cb76c4 Merge remote-tracking branch 'origin/master' 2025-09-01 06:20:42 +02:00
e37b176a3e Added some languages, a configurable cooldown and tab-completion. 2025-09-01 06:20:01 +02:00
D.L.
798841215e Create LICENSE 2025-08-31 14:28:24 +02:00
70c91b4ad2 Merge remote-tracking branch 'origin/master' 2025-08-31 10:51:45 +02:00
8252b23797 Added some languages and a configurable cooldown 2025-08-31 10:51:25 +02:00
D.L.
5443f7f02c Create gradle-publish.yml 2025-08-31 09:56:26 +02:00
4f05bcd479 Changed to Version 1.1 and added a Link to SpigotMC. 2025-08-31 09:49:16 +02:00
1f49160eb9 Fixed a typo in the plugin.yml 2025-08-31 09:12:36 +02:00
46069f52d9 Fixed a typo in the plugin.yml 2025-08-31 08:59:31 +02:00
2f5c9f8e24 Added the /homes Command to see the current homes. 2025-08-31 08:56:45 +02:00
19 changed files with 1216 additions and 19 deletions

46
.github/workflows/preview-release.yml vendored Normal file
View File

@@ -0,0 +1,46 @@
name: Preview Release
on:
push:
branches:
- dev
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
- run: chmod +x gradlew
- run: ./gradlew clean build -x test
# Tag erstellen, bevor Release angelegt wird
- name: Create Preview Tag
run: |
git config user.name "github-actions"
git config user.email "github-actions@users.noreply.github.com"
git tag preview-${{ github.run_number }}
git push origin preview-${{ github.run_number }}
- name: Create Preview Release
uses: softprops/action-gh-release@v2
with:
tag_name: preview-${{ github.run_number }}
name: "Preview Build #${{ github.run_number }}"
prerelease: true
files: build/libs/*.jar
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Cleanup old Preview Releases
run: |
gh release list --limit 100 --repo $GITHUB_REPOSITORY \
| grep preview \
| sort -rk2 \
| awk 'NR>10 {print $1}' \
| xargs -I {} gh release delete {} --repo $GITHUB_REPOSITORY --yes
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

56
.github/workflows/release-on-merge.yml vendored Normal file
View File

@@ -0,0 +1,56 @@
name: Release on Merge to Master
on:
push:
branches:
- master # oder "main", je nach Repo
paths-ignore:
- '**.md' # ignoriert reine Dokumentationsänderungen
- '.github/**' # ignoriert Änderungen an Actions selbst
jobs:
build-and-release:
runs-on: ubuntu-latest
steps:
# 🧾 Schritt 1: Repository auschecken
- name: Checkout code
uses: actions/checkout@v4
# ☕ Schritt 2: Java einrichten
- name: Set up Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '21'
# 🛠️ Schritt 3: Gradle Wrapper Berechtigungen setzen (unter Linux erforderlich)
- name: Grant execute permission for Gradle Wrapper
run: chmod +x gradlew
# 🧱 Schritt 4: Plugin bauen
- name: Build with Gradle
run: ./gradlew clean build -x test
# 🏷️ Schritt 5: Version aus Gradle ermitteln
- name: Get version
id: get_version
run: |
VERSION=$(./gradlew properties -q | grep "^version:" | awk '{print $2}')
echo "version=$VERSION" >> $GITHUB_OUTPUT
# 🚀 Schritt 6: GitHub Release erstellen
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.get_version.outputs.version }}
name: "Release v${{ steps.get_version.outputs.version }}"
body: |
**v${{ steps.get_version.outputs.version }}**
- Commit: ${{ github.sha }}
- Branch: ${{ github.ref_name }}
- Version: v${{ steps.get_version.outputs.version }}
files: |
build/libs/*.jar
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

43
.github/workflows/static.yml vendored Normal file
View File

@@ -0,0 +1,43 @@
# Simple workflow for deploying static content to GitHub Pages
name: Deploy static content to Pages
on:
# Runs on pushes targeting the default branch
push:
branches: ["master"]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
cancel-in-progress: false
jobs:
# Single deploy job since we're just deploying
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Pages
uses: actions/configure-pages@v5
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
# Upload entire repository
path: './web'
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 Deutschich aka User404
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -3,7 +3,7 @@ plugins {
}
group = "com.user404_"
version = "1.0-SNAPSHOT"
version = "1.1"
repositories {
mavenCentral()

View File

@@ -4,6 +4,7 @@ import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
@@ -11,22 +12,46 @@ import org.bukkit.plugin.java.JavaPlugin;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Level;
public class InfiniteHomes extends JavaPlugin {
public class InfiniteHomes extends JavaPlugin implements TabCompleter {
private Map<UUID, Map<String, Location>> homes;
private Map<UUID, Long> cooldowns;
private FileConfiguration homesConfig;
private File homesFile;
private Map<String, FileConfiguration> translations;
private File translationsDir;
@Override
public void onEnable() {
homes = new HashMap<>();
cooldowns = new HashMap<>();
translations = new HashMap<>();
setupHomesConfig();
loadHomesFromConfig();
setupTranslations();
// Standardkonfiguration erstellen, falls nicht vorhanden
getConfig().addDefault("max-homes", -1);
getConfig().addDefault("home-cooldown", -1);
getConfig().options().copyDefaults(true);
saveConfig();
// TabCompleter registrieren
getCommand("home").setTabCompleter(this);
getCommand("delhome").setTabCompleter(this);
getLogger().info("InfiniteHomes plugin enabled!");
}
@@ -36,6 +61,43 @@ public class InfiniteHomes extends JavaPlugin {
getLogger().info("InfiniteHomes plugin disabled!");
}
@Override
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
List<String> completions = new ArrayList<>();
// Nur für Spieler und für die Befehle home und delhome
if (!(sender instanceof Player) || (!command.getName().equalsIgnoreCase("home") &&
!command.getName().equalsIgnoreCase("delhome"))) {
return completions;
}
Player player = (Player) sender;
UUID playerUuid = player.getUniqueId();
// Wenn der Spieler keine Homes hat, leere Liste zurückgeben
if (!homes.containsKey(playerUuid) || homes.get(playerUuid).isEmpty()) {
return completions;
}
// Home-Namen des Spielers holen
Set<String> homeNames = homes.get(playerUuid).keySet();
// Wenn kein Argument vorhanden ist, alle Home-Namen zurückgeben
if (args.length == 0 || args[0].isEmpty()) {
completions.addAll(homeNames);
} else {
// Home-Namen filtern, die mit dem eingegebenen Text beginnen
String input = args[0].toLowerCase();
for (String home : homeNames) {
if (home.toLowerCase().startsWith(input)) {
completions.add(home);
}
}
}
return completions;
}
private void setupHomesConfig() {
if (!getDataFolder().exists()) {
getDataFolder().mkdirs();
@@ -54,6 +116,87 @@ public class InfiniteHomes extends JavaPlugin {
homesConfig = YamlConfiguration.loadConfiguration(homesFile);
}
private void setupTranslations() {
translationsDir = new File(getDataFolder(), "translations");
if (!translationsDir.exists()) {
translationsDir.mkdirs();
}
// Standard-Übersetzung (Englisch) aus Ressourcen laden
saveResource("translations/texts_en.yml", false);
// Verfügbare Übersetzungen laden
loadTranslations();
}
private void loadTranslations() {
translations.clear();
File[] translationFiles = translationsDir.listFiles((dir, name) ->
name.startsWith("texts_") && name.endsWith(".yml"));
if (translationFiles != null) {
for (File file : translationFiles) {
try {
String locale = file.getName().replace("texts_", "").replace(".yml", "");
FileConfiguration config = YamlConfiguration.loadConfiguration(file);
translations.put(locale, config);
getLogger().info("Loaded translation: " + locale);
} catch (Exception e) {
getLogger().log(Level.WARNING, "Error loading translation file: " + file.getName(), e);
}
}
}
// Fallback: Englische Übersetzung aus Ressourcen laden
if (!translations.containsKey("en")) {
try {
InputStream stream = getResource("translations/texts_en.yml");
if (stream != null) {
FileConfiguration config = YamlConfiguration.loadConfiguration(
new InputStreamReader(stream, StandardCharsets.UTF_8));
translations.put("en", config);
}
} catch (Exception e) {
getLogger().log(Level.WARNING, "Error loading default English translation", e);
}
}
}
private String getMessage(Player player, String key) {
// Sprache des Clients ermitteln
String clientLanguage = player.getLocale().toLowerCase();
// Sprache auf Standardformat normalisieren (z.B. "de_de" -> "de")
String languageCode = clientLanguage.split("_")[0];
// Passende Übersetzung finden
FileConfiguration translation = translations.get(languageCode);
if (translation == null) {
// Fallback: Englisch
translation = translations.get("en");
if (translation == null) {
return "Translation not found for key: " + key;
}
}
// Nachricht aus der Übersetzung holen
String message = translation.getString(key);
if (message == null || message.isEmpty()) {
// Fallback: Englisch
FileConfiguration enTranslation = translations.get("en");
if (enTranslation != null) {
message = enTranslation.getString(key);
}
if (message == null || message.isEmpty()) {
return "Message not found: " + key;
}
}
return message;
}
private void loadHomesFromConfig() {
try {
for (String playerUuidString : homesConfig.getKeys(false)) {
@@ -107,7 +250,7 @@ public class InfiniteHomes extends JavaPlugin {
if (cmd.getName().equalsIgnoreCase("sethome")) {
if (args.length != 1) {
player.sendMessage("Usage: /sethome <name>");
player.sendMessage(getMessage(player, "usage.sethome"));
return true;
}
@@ -116,7 +259,7 @@ public class InfiniteHomes extends JavaPlugin {
if (maxHomes != -1) {
int currentHomes = homes.containsKey(playerUuid) ? homes.get(playerUuid).size() : 0;
if (currentHomes >= maxHomes) {
player.sendMessage("§cYou have reached the maximum number of homes (" + maxHomes + ").");
player.sendMessage(getMessage(player, "homes.limit.reached").replace("{max}", String.valueOf(maxHomes)));
return true;
}
}
@@ -128,13 +271,13 @@ public class InfiniteHomes extends JavaPlugin {
homes.get(playerUuid).put(homeName, player.getLocation());
saveHomesToConfig();
player.sendMessage("§aHome '" + homeName + "' set!");
player.sendMessage(getMessage(player, "home.set").replace("{home}", homeName));
return true;
}
if (cmd.getName().equalsIgnoreCase("delhome")) {
if (args.length != 1) {
player.sendMessage("Usage: /delhome <name>");
player.sendMessage(getMessage(player, "usage.delhome"));
return true;
}
@@ -142,37 +285,84 @@ public class InfiniteHomes extends JavaPlugin {
if (homes.containsKey(playerUuid) && homes.get(playerUuid).containsKey(homeName)) {
homes.get(playerUuid).remove(homeName);
saveHomesToConfig();
player.sendMessage("§aHome '" + homeName + "' deleted!");
player.sendMessage(getMessage(player, "home.deleted").replace("{home}", homeName));
} else {
player.sendMessage("§cHome '" + homeName + "' does not exist.");
player.sendMessage(getMessage(player, "home.not_exist").replace("{home}", homeName));
}
return true;
}
if (cmd.getName().equalsIgnoreCase("home")) {
if (args.length != 1) {
player.sendMessage("Usage: /home <name>");
player.sendMessage(getMessage(player, "usage.home"));
return true;
}
// Check cooldown
int cooldown = getConfig().getInt("home-cooldown", -1);
if (cooldown > 0) {
long currentTime = System.currentTimeMillis();
if (cooldowns.containsKey(playerUuid)) {
long lastUsed = cooldowns.get(playerUuid);
long timeLeft = ((lastUsed / 1000) + cooldown) - (currentTime / 1000);
if (timeLeft > 0) {
player.sendMessage(getMessage(player, "home.cooldown").replace("{time}", String.valueOf(timeLeft)));
return true;
}
}
// Set cooldown
cooldowns.put(playerUuid, currentTime);
}
String homeName = args[0].toLowerCase();
if (homes.containsKey(playerUuid) && homes.get(playerUuid).containsKey(homeName)) {
player.teleport(homes.get(playerUuid).get(homeName));
player.sendMessage("§aTeleported to home '" + homeName + "'!");
player.sendMessage(getMessage(player, "home.teleport").replace("{home}", homeName));
} else {
player.sendMessage("§cHome '" + homeName + "' does not exist.");
player.sendMessage(getMessage(player, "home.not_exist").replace("{home}", homeName));
}
return true;
}
if (cmd.getName().equalsIgnoreCase("homes")) {
// List all homes of the player
if (!homes.containsKey(playerUuid) || homes.get(playerUuid).isEmpty()) {
player.sendMessage(getMessage(player, "homes.none"));
return true;
}
Set<String> homeNames = homes.get(playerUuid).keySet();
int maxHomes = getConfig().getInt("max-homes", -1);
int currentHomes = homeNames.size();
String limitText = (maxHomes == -1) ? getMessage(player, "homes.unlimited") : String.valueOf(maxHomes);
player.sendMessage(getMessage(player, "homes.list.header")
.replace("{current}", String.valueOf(currentHomes))
.replace("{max}", limitText));
// List all home names
StringBuilder homesList = new StringBuilder();
for (String home : homeNames) {
if (homesList.length() > 0) {
homesList.append(", ");
}
homesList.append(home);
}
player.sendMessage(getMessage(player, "homes.list.items").replace("{homes}", homesList.toString()));
return true;
}
if (cmd.getName().equalsIgnoreCase("homecount")) {
if (!player.isOp()) {
player.sendMessage("§cYou do not have permission to use this command.");
player.sendMessage(getMessage(player, "no_permission"));
return true;
}
if (args.length != 1) {
player.sendMessage("Usage: /homecount <number>");
player.sendMessage(getMessage(player, "usage.homecount"));
return true;
}
@@ -180,9 +370,41 @@ public class InfiniteHomes extends JavaPlugin {
int newMax = Integer.parseInt(args[0]);
getConfig().set("max-homes", newMax);
saveConfig();
player.sendMessage("§aGlobal home limit set to " + newMax + ".");
player.sendMessage(getMessage(player, "homes.limit.set").replace("{max}", String.valueOf(newMax)));
} catch (NumberFormatException e) {
player.sendMessage("§cPlease provide a valid number.");
player.sendMessage(getMessage(player, "invalid_number"));
}
return true;
}
if (cmd.getName().equalsIgnoreCase("homecooldown")) {
if (!player.isOp()) {
player.sendMessage(getMessage(player, "no_permission"));
return true;
}
if (args.length != 1) {
player.sendMessage(getMessage(player, "usage.homecooldown"));
return true;
}
try {
int cooldown = Integer.parseInt(args[0]);
if (cooldown < -1 || cooldown > 60) {
player.sendMessage(getMessage(player, "cooldown.range"));
return true;
}
getConfig().set("home-cooldown", cooldown);
saveConfig();
if (cooldown == -1) {
player.sendMessage(getMessage(player, "cooldown.disabled"));
} else {
player.sendMessage(getMessage(player, "cooldown.set").replace("{time}", String.valueOf(cooldown)));
}
} catch (NumberFormatException e) {
player.sendMessage(getMessage(player, "invalid_number"));
}
return true;
}

View File

@@ -1,2 +1,5 @@
# Default home limit (-1 = unlimited)
max-homes: -1
# Home-Limit Configuration
max-homes: -1
# Home-Cooldown in seconds (-1 um to disable)
home-cooldown: -1

View File

@@ -1,6 +1,7 @@
name: InfiniteHomes
version: 1.0
version: 1.1
main: com.user404_.infinitehomes.InfiniteHomes
website: https://www.spigotmc.org/resources/infinitehomes-unlimited-or-configurable-homes-system.128492/
api-version: 1.21
commands:
sethome:
@@ -9,9 +10,18 @@ commands:
delhome:
description: Delete a home with the given name.
usage: /delhome <name>
aliases: [deletehome]
home:
description: Teleport to a home with the given name.
usage: /home <name>
aliases: [h]
homes:
description: List all your homes.
usage: /homes
aliases: [listhomes]
homecount:
description: Set the global home limit (OP only).
usage: /homecount <number>
usage: /homecount <number>
homecooldown:
description: Set the global home cooldown (OP only).
usage: /homecooldown <seconds>

View File

@@ -0,0 +1,34 @@
# InfiniteHomes - Translation File
# You can create translations for different languages in the translations folder
# Name the files texts_[lang].yml (e.g., texts_de.yml for German)
# General messages
no_permission: "§cYou don't have permission to use this command."
invalid_number: "§cPlease provide a valid number."
cooldown.range: "§cCooldown must be between -1 and 60 seconds."
# Command usage
usage.sethome: "§cUsage: /sethome <name>"
usage.delhome: "§cUsage: /delhome <name>"
usage.home: "§cUsage: /home <name>"
usage.homecount: "§cUsage: /homecount <number>"
usage.homecooldown: "§cUsage: /homecooldown <seconds>"
# Home messages
home.set: "§aHome '{home}' set!"
home.deleted: "§aHome '{home}' deleted!"
home.not_exist: "§cHome '{home}' does not exist."
home.teleport: "§aTeleported to home '{home}'!"
home.cooldown: "§cYou must wait {time} seconds before using /home again."
# Homes list messages
homes.none: "§cYou don't have any homes set."
homes.unlimited: "unlimited"
homes.list.header: "§aYour homes (§e{current}§a/§e{max}§a):"
homes.list.items: "§e{homes}"
homes.limit.reached: "§cYou have reached the maximum number of homes ({max})."
homes.limit.set: "§aGlobal home limit set to {max}."
# Cooldown messages
cooldown.set: "§aGlobal home cooldown set to {time} seconds."
cooldown.disabled: "§aHome cooldown disabled."

View File

@@ -0,0 +1,32 @@
# InfiniteHomes - Deutsche Übersetzung
# Allgemeine Nachrichten
no_permission: "§cDu hast keine Berechtigung, diesen Befehl zu verwenden."
invalid_number: "§cBitte gib eine gültige Zahl ein."
cooldown.range: "§cCooldown muss zwischen -1 und 60 Sekunden liegen."
# Befehlsverwendung
usage.sethome: "§cVerwendung: /sethome <Name>"
usage.delhome: "§cVerwendung: /delhome <Name>"
usage.home: "§cVerwendung: /home <Name>"
usage.homecount: "§cVerwendung: /homecount <Zahl>"
usage.homecooldown: "§cVerwendung: /homecooldown <Sekunden>"
# Home-Nachrichten
home.set: "§aHome '{home}' gesetzt!"
home.deleted: "§aHome '{home}' gelöscht!"
home.not_exist: "§cHome '{home}' existiert nicht."
home.teleport: "§aZu Home '{home}' teleportiert!"
home.cooldown: "§cDu musst {time} Sekunden warten, bevor du /home wieder verwenden kannst."
# Home-Liste Nachrichten
homes.none: "§cDu hast keine Homes gesetzt."
homes.unlimited: "unbegrenzt"
homes.list.header: "§aDeine Homes (§e{current}§a/§e{max}§a):"
homes.list.items: "§e{homes}"
homes.limit.reached: "§cDu hast die maximale Anzahl an Homes ({max}) erreicht."
homes.limit.set: "§aGlobales Home-Limit auf {max} gesetzt."
# Cooldown-Nachrichten
cooldown.set: "§aGlobaler Home-Cooldown auf {time} Sekunden gesetzt."
cooldown.disabled: "§aHome-Cooldown deaktiviert."

View File

@@ -0,0 +1,32 @@
# InfiniteHomes - English Translation
# General messages
no_permission: "§cYou don't have permission to use this command."
invalid_number: "§cPlease provide a valid number."
cooldown.range: "§cCooldown must be between -1 and 60 seconds."
# Command usage
usage.sethome: "§cUsage: /sethome <name>"
usage.delhome: "§cUsage: /delhome <name>"
usage.home: "§cUsage: /home <name>"
usage.homecount: "§cUsage: /homecount <number>"
usage.homecooldown: "§cUsage: /homecooldown <seconds>"
# Home messages
home.set: "§aHome '{home}' set!"
home.deleted: "§aHome '{home}' deleted!"
home.not_exist: "§cHome '{home}' does not exist."
home.teleport: "§aTeleported to home '{home}'!"
home.cooldown: "§cYou must wait {time} seconds before using /home again."
# Homes list messages
homes.none: "§cYou don't have any homes set."
homes.unlimited: "unlimited"
homes.list.header: "§aYour homes (§e{current}§a/§e{max}§a):"
homes.list.items: "§e{homes}"
homes.limit.reached: "§cYou have reached the maximum number of homes ({max})."
homes.limit.set: "§aGlobal home limit set to {max}."
# Cooldown messages
cooldown.set: "§aGlobal home cooldown set to {time} seconds."
cooldown.disabled: "§aHome cooldown disabled."

View File

@@ -0,0 +1,30 @@
# Mensajes generales
no_permission: "§cNo tienes permiso para usar este comando."
invalid_number: "§cPor favor, proporciona un número válido."
cooldown.range: "§cEl tiempo de espera debe estar entre -1 y 60 segundos."
# Uso de comandos
usage.sethome: "§cUso: /sethome <nombre>"
usage.delhome: "§cUso: /delhome <nombre>"
usage.home: "§cUso: /home <nombre>"
usage.homecount: "§cUso: /homecount <número>"
usage.homecooldown: "§cUso: /homecooldown <segundos>"
# Mensajes de home
home.set: "§aHome '{home}' establecido!"
home.deleted: "§aHome '{home}' eliminado!"
home.not_exist: "§cHome '{home}' no existe."
home.teleport: "§aTeletransportado a home '{home}'!"
home.cooldown: "§cDebes esperar {time} segundos antes de usar /home de nuevo."
# Lista de homes
homes.none: "§cNo tienes ningún home establecido."
homes.unlimited: "ilimitado"
homes.list.header: "§aTus homes (§e{current}§a/§e{max}§a):"
homes.list.items: "§e{homes}"
homes.limit.reached: "§cHas alcanzado el número máximo de homes ({max})."
homes.limit.set: "§aLímite global de homes establecido a {max}."
# Mensajes de cooldown
cooldown.set: "§aCooldown global de homes establecido a {time} segundos."
cooldown.disabled: "§aCooldown de homes desactivado."

View File

@@ -0,0 +1,30 @@
# Messages générales
no_permission: "§cVous n'avez pas la permission d'utiliser cette commande."
invalid_number: "§cVeuillez fournir un nombre valide."
cooldown.range: "§cLe cooldown doit être compris entre -1 et 60 secondes."
# Utilisation des commandes
usage.sethome: "§cUtilisation : /sethome <nom>"
usage.delhome: "§cUtilisation : /delhome <nom>"
usage.home: "§cUtilisation : /home <nom>"
usage.homecount: "§cUtilisation : /homecount <nombre>"
usage.homecooldown: "§cUtilisation : /homecooldown <secondes>"
# Messages de home
home.set: "§aHome '{home}' défini !"
home.deleted: "§aHome '{home}' supprimé !"
home.not_exist: "§cHome '{home}' n'existe pas."
home.teleport: "§aTéléporté vers le home '{home}' !"
home.cooldown: "§cVous devez attendre {time} secondes avant d'utiliser /home à nouveau."
# Liste des homes
homes.none: "§cVous n'avez aucun home défini."
homes.unlimited: "illimité"
homes.list.header: "§aVos homes (§e{current}§a/§e{max}§a) :"
homes.list.items: "§e{homes}"
homes.limit.reached: "§cVous avez atteint le nombre maximum de homes ({max})."
homes.limit.set: "§aLimite globale de homes définie à {max}."
# Messages de cooldown
cooldown.set: "§aCooldown global des homes défini à {time} secondes."
cooldown.disabled: "§aCooldown des homes désactivé."

View File

@@ -0,0 +1,30 @@
# Messaggi generali
no_permission: "§cNon hai il permesso di usare questo comando."
invalid_number: "§cPer favore, inserisci un numero valido."
cooldown.range: "§cIl cooldown deve essere tra -1 e 60 secondi."
# Uso dei comandi
usage.sethome: "§cUso: /sethome <nome>"
usage.delhome: "§cUso: /delhome <nome>"
usage.home: "§cUso: /home <nome>"
usage.homecount: "§cUso: /homecount <numero>"
usage.homecooldown: "§cUso: /homecooldown <secondi>"
# Messaggi di home
home.set: "§aHome '{home}' impostata!"
home.deleted: "§aHome '{home}' eliminata!"
home.not_exist: "§cHome '{home}' non esiste."
home.teleport: "§aTeletrasportato a home '{home}'!"
home.cooldown: "§cDevi aspettare {time} secondi prima di usare di nuovo /home."
# Lista delle home
homes.none: "§cNon hai nessuna home impostata."
homes.unlimited: "illimitato"
homes.list.header: "§aLe tue home (§e{current}§a/§e{max}§a):"
homes.list.items: "§e{homes}"
homes.limit.reached: "§cHai raggiunto il numero massimo di home ({max})."
homes.limit.set: "§aLimite globale di home impostato a {max}."
# Messaggi di cooldown
cooldown.set: "§aCooldown globale delle home impostato a {time} secondi."
cooldown.disabled: "§aCooldown delle home disattivato."

View File

@@ -0,0 +1,30 @@
# Algemene berichten
no_permission: "§cJe hebt geen toestemming om dit commando te gebruiken."
invalid_number: "§cVoer een geldig nummer in."
cooldown.range: "§cCooldown moet tussen -1 en 60 seconden liggen."
# Gebruik van commando's
usage.sethome: "§cGebruik: /sethome <naam>"
usage.delhome: "§cGebruik: /delhome <naam>"
usage.home: "§cGebruik: /home <naam>"
usage.homecount: "§cGebruik: /homecount <nummer>"
usage.homecooldown: "§cGebruik: /homecooldown <seconden>"
# Home berichten
home.set: "§aHome '{home}' ingesteld!"
home.deleted: "§aHome '{home}' verwijderd!"
home.not_exist: "§cHome '{home}' bestaat niet."
home.teleport: "§aGeteleporteerd naar home '{home}'!"
home.cooldown: "§cJe moet {time} seconden wachten voordat je /home opnieuw kunt gebruiken."
# Home lijst
homes.none: "§cJe hebt geen homes ingesteld."
homes.unlimited: "onbeperkt"
homes.list.header: "§aJe homes (§e{current}§a/§e{max}§a):"
homes.list.items: "§e{homes}"
homes.limit.reached: "§cJe hebt het maximale aantal homes ({max}) bereikt."
homes.limit.set: "§aGlobale homelimit ingesteld op {max}."
# Cooldown berichten
cooldown.set: "§aGlobale home cooldown ingesteld op {time} seconden."
cooldown.disabled: "§aHome cooldown uitgeschakeld."

View File

@@ -0,0 +1,30 @@
# Mensagens gerais
no_permission: "§cVocê não tem permissão para usar este comando."
invalid_number: "§cPor favor, forneça um número válido."
cooldown.range: "§cO cooldown deve estar entre -1 e 60 segundos."
# Uso de comandos
usage.sethome: "§cUso: /sethome <nome>"
usage.delhome: "§cUso: /delhome <nome>"
usage.home: "§cUso: /home <nome>"
usage.homecount: "§cUso: /homecount <número>"
usage.homecooldown: "§cUso: /homecooldown <segundos>"
# Mensagens de home
home.set: "§aHome '{home}' definida!"
home.deleted: "§aHome '{home}' deletada!"
home.not_exist: "§cHome '{home}' não existe."
home.teleport: "§aTeleportado para a home '{home}'!"
home.cooldown: "§cVocê deve esperar {time} segundos antes de usar /home novamente."
# Lista de homes
homes.none: "§cVocê não tem nenhuma home definida."
homes.unlimited: "ilimitado"
homes.list.header: "§aSuas homes (§e{current}§a/§e{max}§a):"
homes.list.items: "§e{homes}"
homes.limit.reached: "§cVocê atingiu o número máximo de homes ({max})."
homes.limit.set: "§aLimite global de homes definido para {max}."
# Mensagens de cooldown
cooldown.set: "§aCooldown global de homes definido para {time} segundos."
cooldown.disabled: "§aCooldown de homes desativado."

View File

@@ -0,0 +1,30 @@
# Общие сообщения
no_permission: "§cУ вас нет разрешения на использование этой команды."
invalid_number: "§cПожалуйста, укажите корректное число."
cooldown.range: "§cВремя восстановления должно быть от -1 до 60 секунд."
# Использование команд
usage.sethome: "§cИспользование: /sethome <имя>"
usage.delhome: "§cИспользование: /delhome <имя>"
usage.home: "§cИспользование: /home <имя>"
usage.homecount: "§cИспользование: /homecount <число>"
usage.homecooldown: "§cИспользование: /homecooldown <секунды>"
# Сообщения о доме
home.set: "§aДом '{home}' установлен!"
home.deleted: "§aДом '{home}' удалён!"
home.not_exist: "§cДом '{home}' не существует."
home.teleport: "§aТелепортировано к дому '{home}'!"
home.cooldown: "§cВы должны подождать {time} секунд перед повторным использованием /home."
# Список домов
homes.none: "§cУ вас нет установленных домов."
homes.unlimited: "безлимитно"
homes.list.header: "§aВаши дома (§e{current}§a/§e{max}§a):"
homes.list.items: "§e{homes}"
homes.limit.reached: "§cВы достигли максимального числа домов ({max})."
homes.limit.set: "§aГлобальный лимит домов установлен на {max}."
# Сообщения о перезарядке
cooldown.set: "§aГлобальный таймер восстановления домов установлен на {time} секунд."
cooldown.disabled: "§aПерезарядка домов отключена."

BIN
web/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

518
web/index.html Normal file
View File

@@ -0,0 +1,518 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" href=icon.ico>
<title>InfiniteHomes - Minecraft Plugin</title>
<style>
:root {
--primary-color: #4CAF50;
--secondary-color: #2E7D32;
--accent-color: #8BC34A;
--dark-color: #1B5E20;
--light-color: #F1F8E9;
--text-dark: #212121;
--text-light: #757575;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background-color: #f5f5f5;
color: var(--text-dark);
line-height: 1.6;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
header {
background: linear-gradient(135deg, var(--primary-color), var(--dark-color));
color: white;
padding: 1rem 0;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
nav {
display: flex;
justify-content: space-between;
align-items: center;
}
.logo {
font-size: 1.8rem;
font-weight: bold;
}
.nav-links {
display: flex;
list-style: none;
}
.nav-links li {
margin-left: 1.5rem;
}
.nav-links a {
color: white;
text-decoration: none;
transition: opacity 0.3s;
}
.nav-links a:hover {
opacity: 0.8;
}
.hero {
background: url('https://via.placeholder.com/1500x500') no-repeat center center/cover;
color: white;
padding: 4rem 0;
text-align: center;
}
.hero-content {
max-width: 800px;
margin: 0 auto;
}
.hero h1 {
font-size: 3rem;
margin-bottom: 1rem;
text-shadow: 2px 2px 4px rgba(0,0,0,0.5);
}
.hero p {
font-size: 1.2rem;
margin-bottom: 2rem;
text-shadow: 1px 1px 2px rgba(0,0,0,0.5);
}
.btn {
display: inline-block;
background: var(--accent-color);
color: white;
padding: 0.8rem 1.5rem;
border: none;
border-radius: 4px;
text-decoration: none;
font-size: 1rem;
cursor: pointer;
transition: background 0.3s;
}
.btn:hover {
background: var(--secondary-color);
}
section {
padding: 4rem 0;
}
.section-title {
text-align: center;
margin-bottom: 3rem;
color: var(--dark-color);
}
.features {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
margin-bottom: 3rem;
}
.feature-card {
background: white;
border-radius: 8px;
padding: 1.5rem;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
transition: transform 0.3s;
}
.feature-card:hover {
transform: translateY(-5px);
}
.feature-card h3 {
color: var(--primary-color);
margin-bottom: 1rem;
}
.commands {
background-color: var(--light-color);
}
.command-list {
background: white;
border-radius: 8px;
padding: 2rem;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.command-item {
margin-bottom: 1.5rem;
padding-bottom: 1.5rem;
border-bottom: 1px solid #eee;
}
.command-item:last-child {
border-bottom: none;
margin-bottom: 0;
padding-bottom: 0;
}
.command-name {
font-weight: bold;
color: var(--secondary-color);
}
.command-desc {
margin-top: 0.5rem;
color: var(--text-light);
}
.download-platforms {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 2rem;
margin-top: 2rem;
}
.platform-card {
background: white;
border-radius: 8px;
padding: 1.5rem;
text-align: center;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
transition: transform 0.3s;
}
.platform-card:hover {
transform: translateY(-5px);
}
.platform-logo {
font-size: 2rem;
margin-bottom: 1rem;
color: var(--primary-color);
}
.screenshot-gallery {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 1.5rem;
}
.screenshot-item {
border-radius: 8px;
overflow: hidden;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.screenshot-item img {
width: 100%;
height: 200px;
object-fit: cover;
transition: transform 0.3s;
}
.screenshot-item:hover img {
transform: scale(1.05);
}
.faq-item {
margin-bottom: 1.5rem;
background: white;
border-radius: 8px;
padding: 1.5rem;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.faq-question {
font-weight: bold;
color: var(--primary-color);
margin-bottom: 0.5rem;
}
footer {
background: var(--dark-color);
color: white;
padding: 2rem 0;
text-align: center;
}
.footer-content {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 2rem;
text-align: left;
}
.footer-links {
list-style: none;
}
.footer-links li {
margin-bottom: 0.5rem;
}
.footer-links a {
color: var(--light-color);
text-decoration: none;
transition: color 0.3s;
}
.footer-links a:hover {
color: var(--accent-color);
}
.copyright {
margin-top: 2rem;
padding-top: 1rem;
border-top: 1px solid rgba(255,255,255,0.1);
}
@media (max-width: 768px) {
.nav-links {
flex-direction: column;
align-items: flex-end;
}
.nav-links li {
margin: 0.5rem 0;
}
.hero h1 {
font-size: 2rem;
}
}
</style>
</head>
<body>
<header>
<div class="container">
<nav>
<div class="logo">InfiniteHomes</div>
<ul class="nav-links">
<li><a href="#features">Features</a></li>
<li><a href="#commands">Commands</a></li>
<li><a href="#download">Download</a></li>
<li><a href="#screenshots">Screenshots</a></li>
<li><a href="#faq">FAQ</a></li>
</ul>
</nav>
</div>
</header>
<section class="hero">
<div class="container">
<div class="hero-content">
<h1>InfiniteHomes Minecraft Plugin</h1>
<p>Unbegrenzte oder kontrollierbare Homes für deine Spieler! Leichtgewichtig, einfach zu bedienen und perfekt für jeden Survival- oder Freebuild-Server.</p>
<a href="#download" class="btn">Jetzt herunterladen</a>
</div>
</div>
</section>
<section id="features">
<div class="container">
<h2 class="section-title">Plugin-Features</h2>
<div class="features">
<div class="feature-card">
<h3>Unbegrenzte Homes</h3>
<p>Spieler können standardmäßig so viele Homes setzen, wie sie möchten - oder du als Serverbetreiber legst fest, wie viele Homes erlaubt sind.</p>
</div>
<div class="feature-card">
<h3>Einfache Konfiguration</h3>
<p>Keine Datenbank erforderlich! Einfach installieren und loslegen. Perfekt für kleine und große Server.</p>
</div>
<div class="feature-card">
<h3>Multilingual Support</h3>
<p>Unterstützung für mehrere Sprachen durch benutzerdefinierte Textdateien. Einfache Anpassung aller Plugin-Nachrichten.</p>
</div>
<div class="feature-card">
<h3>Cooldown-System</h3>
<p>Konfigurierbare Cooldowns zwischen Teleportationen, um Missbrauch zu verhindern.</p>
</div>
<div class="feature-card">
<h3>Leistungsoptimiert</h3>
<p>Leichtgewichtiges Plugin, das keine Serverressourcen überlastet und selbst auf großen Servern reibungslos läuft.</p>
</div>
<div class="feature-card">
<h3>Permissions</h3>
<p>Vollständige Integration mit Permission-Systemen, um differentierte Zugriffsrechte zu verwalten.</p>
</div>
</div>
</div>
</section>
<section id="commands" class="commands">
<div class="container">
<h2 class="section-title">Befehle</h2>
<div class="command-list">
<div class="command-item">
<div class="command-name">/sethome &lt;name&gt;</div>
<div class="command-desc">Setzt ein Home mit einem benutzerdefinierten Namen</div>
</div>
<div class="command-item">
<div class="command-name">/home &lt;name&gt;</div>
<div class="command-desc">Teleportiert dich zu deinem Home</div>
</div>
<div class="command-item">
<div class="command-name">/delhome &lt;name&gt;</div>
<div class="command-desc">Löscht ein Home</div>
</div>
<div class="command-item">
<div class="command-name">/homes</div>
<div class="command-desc">Listet alle Homes des aktuellen Benutzers auf</div>
</div>
<div class="command-item">
<div class="command-name">/homecooldown &lt;number/-1&gt;</div>
<div class="command-desc">Setzt den Cooldown zwischen Teleports (OP only, -1 = kein Cooldown)</div>
</div>
<div class="command-item">
<div class="command-name">/homecount &lt;number/-1&gt;</div>
<div class="command-desc">Setzt die maximale Anzahl von Homes (OP only, -1 = unbegrenzt)</div>
</div>
</div>
</div>
</section>
<section id="download">
<div class="container">
<h2 class="section-title">Download & Links</h2>
<p style="text-align: center; margin-bottom: 2rem;">Lade das InfiniteHomes Plugin von diesen Plattformen herunter:</p>
<div class="download-platforms">
<div class="platform-card">
<div class="platform-logo">🔗</div>
<h3>SpigotMC</h3>
<p>Download für Spigot und Bukkit Server</p>
<a href="https://www.spigotmc.org/resources/infinitehomes-unlimited-or-configurable-homes-system.128492/" class="btn">Zum Download</a>
</div>
<div class="platform-card">
<div class="platform-logo">🔗</div>
<h3>Modrinth</h3>
<p>Download auf Modrinth</p>
<a href="https://modrinth.com/plugin/infinitehomes" class="btn">Zum Download</a>
</div>
<div class="platform-card">
<div class="platform-logo">🔗</div>
<h3>GitHub</h3>
<p>Quellcode auf GitHub</p>
<a href="https://github.com/deutschich/InfiniteHomes" class="btn">Zum Repository</a>
</div>
<div class="platform-card">
<div class="platform-logo">🔗</div>
<h3>PaperMC Hangar</h3>
<p>Download auf PaperMC Hangar</p>
<a href="https://hangar.papermc.io/user404/InfiniteHomes" class="btn">Zum Download</a>
</div>
</div>
<div style="margin-top: 3rem; background: white; padding: 2rem; border-radius: 8px; box-shadow: 0 4px 6px rgba(0,0,0,0.1);">
<h3 style="color: var(--primary-color); margin-bottom: 1rem;">Installationsanleitung</h3>
<ol style="padding-left: 1.5rem;">
<li style="margin-bottom: 0.5rem;">Lade die neueste Version des Plugins von einer der Plattformen herunter</li>
<li style="margin-bottom: 0.5rem;">Füge die JAR-Datei in den Plugins-Ordner deines Minecraft-Servers ein</li>
<li style="margin-bottom: 0.5rem;">Starte den Server neu, um das Plugin zu laden</li>
<li style="margin-bottom: 0.5rem;">Passe die Konfiguration in der generierten Config-Datei nach Bedarf an</li>
<li>Füge benutzerdefinierte Übersetzungen im /plugins/Infinitehomes/translations/ Ordner hinzu</li>
</ol>
</div>
</div>
</section>
<section id="screenshots">
<div class="container">
<h2 class="section-title">Screenshots</h2>
<p style="text-align: center; margin-bottom: 2rem;"></p>
<div class="screenshot-gallery">
<div class="screenshot-item">
<img src="https://cdn.modrinth.com/data/XMQtfIwI/images/27341dc3d536c51f337d813445954fce1c953119.png" alt="InfiniteHomes Screenshot 1">
</div>
<div class="screenshot-item">
<img src="https://cdn.modrinth.com/data/XMQtfIwI/images/01d647600635f6ab25004a8e8d400634552945ef.png" alt="InfiniteHomes Screenshot 2">
</div>
<div class="screenshot-item">
<img src="https://cdn.modrinth.com/data/XMQtfIwI/images/b04c4ac935befe93bf8d4f5cc15d903764976ddf.png" alt="InfiniteHomes Screenshot 3">
</div>
</div>
</div>
</section>
<section id="faq" class="commands">
<div class="container">
<h2 class="section-title">Häufig gestellte Fragen</h2>
<div class="faq-item">
<div class="faq-question">Wie installiere ich das InfiniteHomes Plugin?</div>
<div class="faq-answer">Lade die JAR-Datei herunter, lege sie in den Plugins-Ordner deines Servers und starte den Server neu. Das Plugin generiert automatisch eine Config-Datei, die du nach deinen Wünschen anpassen kannst.</div>
</div>
<div class="faq-item">
<div class="faq-question">Unterstützt das Plugin mehrere Sprachen?</div>
<div class="faq-answer">Ja, das Plugin unterstützt mehrere Sprachen. Du kannst benutzerdefinierte Übersetzungen im /plugins/Infinitehomes/translations/ Ordner hinzufügen, z.B. texts_de.yml für Deutsch.</div>
</div>
<div class="faq-item">
<div class="faq-question">Kann ich die maximale Anzahl von Homes pro Spieler begrenzen?</div>
<div class="faq-answer">Ja, als Serveroperator kannst du mit /homecount &lt;number&gt; die maximale Anzahl von Homes festlegen. Verwende -1 für unbegrenzte Homes.</div>
</div>
<div class="faq-item">
<div class="faq-question">Ist eine Datenbank für dieses Plugin erforderlich?</div>
<div class="faq-answer">Nein, das Plugin ist leichtgewichtig und benötigt keine Datenbank. Alle Daten werden in lokalen Dateien gespeichert.</div>
</div>
<div class="faq-item">
<div class="faq-question">Kann ich Cooldowns für Teleports festlegen?</div>
<div class="faq-answer">Ja, du kannst mit /homecooldown &lt;seconds&gt; einen Cooldown zwischen Teleportationen festlegen. Verwende -1, um Cooldowns zu deaktivieren.</div>
</div>
</div>
</section>
<footer>
<div class="container">
<div class="footer-content">
<div>
<h3>InfiniteHomes</h3>
<p>Ein leistungsstarkes Home-Plugin für Minecraft-Server.</p>
</div>
<div>
<h3>Links</h3>
<ul class="footer-links">
<li><a href="#features">Features</a></li>
<li><a href="#commands">Commands</a></li>
<li><a href="#download">Download</a></li>
<li><a href="#screenshots">Screenshots</a></li>
<li><a href="#faq">FAQ</a></li>
</ul>
</div>
<div>
<h3>Externe Links</h3>
<ul class="footer-links">
<li><a href="https://www.spigotmc.org/resources/infinitehomes-unlimited-or-configurable-homes-system.128492/">SpigotMC</a></li>
<li><a href="https://modrinth.com/plugin/infinitehomes">Modrinth</a></li>
<li><a href="https://github.com/deutschich/InfiniteHomes">GitHub</a></li>
<li><a href="https://hangar.papermc.io/user404/InfiniteHomes">PaperMC Hangar</a></li>
</ul>
</div>
</div>
<div class="copyright">
<p>&copy; 2025 InfiniteHomes Plugin. Alle Rechte vorbehalten.</p>
</div>
</div>
</footer>
</body>
</html>