diff --git a/services/discord-rules/1.18.2/.gradle/8.8/checksums/checksums.lock b/services/discord-rules/1.18.2/.gradle/8.8/checksums/checksums.lock new file mode 100644 index 0000000..7dd1d21 Binary files /dev/null and b/services/discord-rules/1.18.2/.gradle/8.8/checksums/checksums.lock differ diff --git a/services/discord-rules/1.18.2/.gradle/8.8/checksums/md5-checksums.bin b/services/discord-rules/1.18.2/.gradle/8.8/checksums/md5-checksums.bin new file mode 100644 index 0000000..8c1eba7 Binary files /dev/null and b/services/discord-rules/1.18.2/.gradle/8.8/checksums/md5-checksums.bin differ diff --git a/services/discord-rules/1.18.2/.gradle/8.8/checksums/sha1-checksums.bin b/services/discord-rules/1.18.2/.gradle/8.8/checksums/sha1-checksums.bin new file mode 100644 index 0000000..34e875f Binary files /dev/null and b/services/discord-rules/1.18.2/.gradle/8.8/checksums/sha1-checksums.bin differ diff --git a/services/discord-rules/1.18.2/.gradle/8.8/dependencies-accessors/gc.properties b/services/discord-rules/1.18.2/.gradle/8.8/dependencies-accessors/gc.properties new file mode 100644 index 0000000..e69de29 diff --git a/services/discord-rules/1.18.2/.gradle/8.8/executionHistory/executionHistory.bin b/services/discord-rules/1.18.2/.gradle/8.8/executionHistory/executionHistory.bin new file mode 100644 index 0000000..d5fd4fc Binary files /dev/null and b/services/discord-rules/1.18.2/.gradle/8.8/executionHistory/executionHistory.bin differ diff --git a/services/discord-rules/1.18.2/.gradle/8.8/executionHistory/executionHistory.lock b/services/discord-rules/1.18.2/.gradle/8.8/executionHistory/executionHistory.lock new file mode 100644 index 0000000..ee76780 Binary files /dev/null and b/services/discord-rules/1.18.2/.gradle/8.8/executionHistory/executionHistory.lock differ diff --git a/services/discord-rules/1.18.2/.gradle/8.8/fileChanges/last-build.bin b/services/discord-rules/1.18.2/.gradle/8.8/fileChanges/last-build.bin new file mode 100644 index 0000000..f76dd23 Binary files /dev/null and b/services/discord-rules/1.18.2/.gradle/8.8/fileChanges/last-build.bin differ diff --git a/services/discord-rules/1.18.2/.gradle/8.8/fileHashes/fileHashes.bin b/services/discord-rules/1.18.2/.gradle/8.8/fileHashes/fileHashes.bin new file mode 100644 index 0000000..36f1935 Binary files /dev/null and b/services/discord-rules/1.18.2/.gradle/8.8/fileHashes/fileHashes.bin differ diff --git a/services/discord-rules/1.18.2/.gradle/8.8/fileHashes/fileHashes.lock b/services/discord-rules/1.18.2/.gradle/8.8/fileHashes/fileHashes.lock new file mode 100644 index 0000000..9cd9d55 Binary files /dev/null and b/services/discord-rules/1.18.2/.gradle/8.8/fileHashes/fileHashes.lock differ diff --git a/services/discord-rules/1.18.2/.gradle/8.8/fileHashes/resourceHashesCache.bin b/services/discord-rules/1.18.2/.gradle/8.8/fileHashes/resourceHashesCache.bin new file mode 100644 index 0000000..ddf1edc Binary files /dev/null and b/services/discord-rules/1.18.2/.gradle/8.8/fileHashes/resourceHashesCache.bin differ diff --git a/services/discord-rules/1.18.2/.gradle/8.8/gc.properties b/services/discord-rules/1.18.2/.gradle/8.8/gc.properties new file mode 100644 index 0000000..e69de29 diff --git a/services/discord-rules/1.18.2/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/services/discord-rules/1.18.2/.gradle/buildOutputCleanup/buildOutputCleanup.lock new file mode 100644 index 0000000..4da24aa Binary files /dev/null and b/services/discord-rules/1.18.2/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/services/discord-rules/1.18.2/.gradle/buildOutputCleanup/cache.properties b/services/discord-rules/1.18.2/.gradle/buildOutputCleanup/cache.properties new file mode 100644 index 0000000..a4229be --- /dev/null +++ b/services/discord-rules/1.18.2/.gradle/buildOutputCleanup/cache.properties @@ -0,0 +1,2 @@ +#Tue Apr 14 08:26:28 CDT 2026 +gradle.version=8.8 diff --git a/services/discord-rules/1.18.2/.gradle/buildOutputCleanup/outputFiles.bin b/services/discord-rules/1.18.2/.gradle/buildOutputCleanup/outputFiles.bin new file mode 100644 index 0000000..18e733c Binary files /dev/null and b/services/discord-rules/1.18.2/.gradle/buildOutputCleanup/outputFiles.bin differ diff --git a/services/discord-rules/1.18.2/.gradle/file-system.probe b/services/discord-rules/1.18.2/.gradle/file-system.probe new file mode 100644 index 0000000..6e0abff Binary files /dev/null and b/services/discord-rules/1.18.2/.gradle/file-system.probe differ diff --git a/services/discord-rules/1.18.2/.gradle/vcs-1/gc.properties b/services/discord-rules/1.18.2/.gradle/vcs-1/gc.properties new file mode 100644 index 0000000..e69de29 diff --git a/services/discord-rules/1.18.2/build.gradle b/services/discord-rules/1.18.2/build.gradle new file mode 100644 index 0000000..6c35202 --- /dev/null +++ b/services/discord-rules/1.18.2/build.gradle @@ -0,0 +1,50 @@ +buildscript { + repositories { + maven { url = 'https://maven.minecraftforge.net/' } + mavenCentral() + } + dependencies { + classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '6.0.+' + } +} + +apply plugin: 'net.minecraftforge.gradle' + +version = '1.0.5' +group = 'com.discordrules' +archivesBaseName = 'discordrules' + +java.toolchain.languageVersion = JavaLanguageVersion.of(17) + +minecraft { + mappings channel: 'official', version: '1.18.2' + runs { + server { + workingDirectory project.file('run') + property 'forge.logging.markers', 'REGISTRIES' + property 'forge.logging.console.level', 'debug' + mods { + discordrules { + source sourceSets.main + } + } + } + } +} + +dependencies { + minecraft 'net.minecraftforge:forge:1.18.2-40.2.4' +} + +jar { + manifest { + attributes([ + "Implementation-Title": "Discord Rules", + "Implementation-Version": project.version, + ]) + } +} + +tasks.withType(JavaCompile).configureEach { + options.encoding = 'UTF-8' +} diff --git a/services/discord-rules/1.18.2/gradle.properties b/services/discord-rules/1.18.2/gradle.properties new file mode 100644 index 0000000..29b2ac4 --- /dev/null +++ b/services/discord-rules/1.18.2/gradle.properties @@ -0,0 +1,2 @@ +org.gradle.jvmargs=-Xmx3G +org.gradle.daemon=false diff --git a/services/discord-rules/1.18.2/gradle/wrapper/gradle-wrapper.jar b/services/discord-rules/1.18.2/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..e644113 Binary files /dev/null and b/services/discord-rules/1.18.2/gradle/wrapper/gradle-wrapper.jar differ diff --git a/services/discord-rules/1.18.2/gradle/wrapper/gradle-wrapper.properties b/services/discord-rules/1.18.2/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..a441313 --- /dev/null +++ b/services/discord-rules/1.18.2/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/services/discord-rules/1.18.2/gradlew b/services/discord-rules/1.18.2/gradlew new file mode 100755 index 0000000..b740cf1 --- /dev/null +++ b/services/discord-rules/1.18.2/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/services/discord-rules/1.18.2/gradlew.bat b/services/discord-rules/1.18.2/gradlew.bat new file mode 100644 index 0000000..7101f8e --- /dev/null +++ b/services/discord-rules/1.18.2/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/services/discord-rules/1.18.2/settings.gradle b/services/discord-rules/1.18.2/settings.gradle new file mode 100644 index 0000000..428f51d --- /dev/null +++ b/services/discord-rules/1.18.2/settings.gradle @@ -0,0 +1,8 @@ +pluginManagement { + repositories { + gradlePluginPortal() + maven { url = 'https://maven.minecraftforge.net/' } + } +} + +rootProject.name = 'discordrules' diff --git a/services/discord-rules/1.18.2/src/main/java/com/discordrules/CooldownManager.java b/services/discord-rules/1.18.2/src/main/java/com/discordrules/CooldownManager.java new file mode 100644 index 0000000..1837c64 --- /dev/null +++ b/services/discord-rules/1.18.2/src/main/java/com/discordrules/CooldownManager.java @@ -0,0 +1,32 @@ +package com.discordrules; + +import net.minecraft.Util; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.server.level.ServerPlayer; +import java.time.Duration; +import java.time.Instant; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public class CooldownManager { + private static final ConcurrentHashMap COOLDOWNS = new ConcurrentHashMap<>(); + + public static boolean checkAndUpdateCooldown(ServerPlayer player) { + UUID playerId = player.getUUID(); + Instant now = Instant.now(); + int cooldownSeconds = ServerRulesConfig.COOLDOWN_SECONDS.get(); + Instant lastUsed = COOLDOWNS.get(playerId); + if (lastUsed != null) { + long secondsSinceLastUse = Duration.between(lastUsed, now).getSeconds(); + if (secondsSinceLastUse < cooldownSeconds) { + long remaining = cooldownSeconds - secondsSinceLastUse; + player.sendMessage(new TextComponent("\u00a7cPlease wait " + remaining + " seconds before checking the rules again."), Util.NIL_UUID); + return false; + } + } + COOLDOWNS.put(playerId, now); + return true; + } + + public static void removePlayer(UUID playerId) { COOLDOWNS.remove(playerId); } +} diff --git a/services/discord-rules/1.18.2/src/main/java/com/discordrules/DiscordFetcher.java b/services/discord-rules/1.18.2/src/main/java/com/discordrules/DiscordFetcher.java new file mode 100644 index 0000000..9f35c3a --- /dev/null +++ b/services/discord-rules/1.18.2/src/main/java/com/discordrules/DiscordFetcher.java @@ -0,0 +1,45 @@ +package com.discordrules; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.time.Duration; +import java.util.concurrent.CompletableFuture; + +public class DiscordFetcher { + private static final Logger LOGGER = LoggerFactory.getLogger(DiscordFetcher.class); + private static final HttpClient CLIENT = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build(); + + public static CompletableFuture fetchRulesAsync() { + if (!ServerRulesConfig.isMessageIdValid()) { + LOGGER.error("Invalid Discord Message ID in config."); + return CompletableFuture.completedFuture(null); + } + String token = ServerRulesConfig.BOT_TOKEN.get(); + String channelId = ServerRulesConfig.CHANNEL_ID.get(); + String messageId = ServerRulesConfig.MESSAGE_ID.get(); + URI uri = URI.create("https://discord.com/api/v10/channels/" + channelId + "/messages/" + messageId); + HttpRequest request = HttpRequest.newBuilder().uri(uri) + .header("Authorization", "Bot " + token) + .header("Accept", "application/json").GET().build(); + return CLIENT.sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .thenApply(response -> { + if (response.statusCode() == 200) { + JsonObject json = JsonParser.parseString(response.body()).getAsJsonObject(); + return json.get("content").getAsString(); + } else { + LOGGER.error("Discord API returned status: {}", response.statusCode()); + return null; + } + }) + .exceptionally(ex -> { + LOGGER.error("Network error while fetching Discord rules", ex); + return null; + }); + } +} diff --git a/services/discord-rules/1.18.2/src/main/java/com/discordrules/DiscordFormatter.java b/services/discord-rules/1.18.2/src/main/java/com/discordrules/DiscordFormatter.java new file mode 100644 index 0000000..34913b9 --- /dev/null +++ b/services/discord-rules/1.18.2/src/main/java/com/discordrules/DiscordFormatter.java @@ -0,0 +1,45 @@ +package com.discordrules; + +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.network.chat.TextComponent; + +public class DiscordFormatter { + public static MutableComponent formatRules(String rawDiscordText) { + String processedText = ServerRulesConfig.STRIP_EMOJIS.get() + ? stripEmojis(rawDiscordText) : rawDiscordText; + ChatFormatting headerColor = parseColor(ServerRulesConfig.HEADER_COLOR.get(), ChatFormatting.GOLD); + ChatFormatting bodyColor = parseColor(ServerRulesConfig.BODY_COLOR.get(), ChatFormatting.YELLOW); + MutableComponent rootComponent = new TextComponent(""); + String[] lines = processedText.split("\n"); + for (String line : lines) { + MutableComponent lineComponent; + if (line.startsWith("**") && line.endsWith("**")) { + String cleanLine = line.replace("**", ""); + lineComponent = new TextComponent(cleanLine); + lineComponent.withStyle(headerColor, ChatFormatting.BOLD); + } else if (line.trim().startsWith("-") || line.trim().startsWith("\u2022")) { + lineComponent = new TextComponent(" " + line.trim()); + lineComponent.withStyle(bodyColor); + } else { + lineComponent = new TextComponent(line); + lineComponent.withStyle(bodyColor); + } + rootComponent.append(lineComponent).append(new TextComponent("\n")); + } + return rootComponent; + } + + private static String stripEmojis(String text) { + if (text == null) return ""; + return text.replaceAll("[\\x{1F300}-\\x{1F9FF}]", ""); + } + + private static ChatFormatting parseColor(String name, ChatFormatting fallback) { + try { + return ChatFormatting.valueOf(name.toUpperCase()); + } catch (IllegalArgumentException e) { + return fallback; + } + } +} diff --git a/services/discord-rules/1.18.2/src/main/java/com/discordrules/RulesCache.java b/services/discord-rules/1.18.2/src/main/java/com/discordrules/RulesCache.java new file mode 100644 index 0000000..6ffab0b --- /dev/null +++ b/services/discord-rules/1.18.2/src/main/java/com/discordrules/RulesCache.java @@ -0,0 +1,26 @@ +package com.discordrules; + +import java.time.Instant; + +public class RulesCache { + private static String cachedRules = null; + private static Instant lastFetchTime = Instant.MIN; + private static final String FALLBACK_RULES = + "Server Rules\n1. Be respectful to all players.\n2. No griefing or cheating.\n3. Follow staff instructions.\nPlease check Discord for the full rules list."; + + public static boolean isCacheValid() { + if (cachedRules == null) return false; + long cacheMinutes = ServerRulesConfig.CACHE_MINUTES.get(); + return Instant.now().isBefore(lastFetchTime.plusSeconds(cacheMinutes * 60)); + } + + public static void updateCache(String newRules) { + if (newRules != null && !newRules.trim().isEmpty()) { + cachedRules = newRules; + lastFetchTime = Instant.now(); + } + } + + public static String getRules() { return cachedRules != null ? cachedRules : FALLBACK_RULES; } + public static void invalidate() { cachedRules = null; lastFetchTime = Instant.MIN; } +} diff --git a/services/discord-rules/1.18.2/src/main/java/com/discordrules/RulesCommand.java b/services/discord-rules/1.18.2/src/main/java/com/discordrules/RulesCommand.java new file mode 100644 index 0000000..cc5789b --- /dev/null +++ b/services/discord-rules/1.18.2/src/main/java/com/discordrules/RulesCommand.java @@ -0,0 +1,62 @@ +package com.discordrules; + +import com.mojang.brigadier.CommandDispatcher; +import net.minecraft.Util; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.server.level.ServerPlayer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RulesCommand { + private static final Logger LOGGER = LoggerFactory.getLogger(RulesCommand.class); + + public static void register(CommandDispatcher dispatcher) { + dispatcher.register(Commands.literal("rules").executes(context -> { + CommandSourceStack source = context.getSource(); + LOGGER.info("[DIAG] /rules invoked. isPlayer={}, token length={}, channel={}, messageId={}, isValid={}", + source.getEntity() instanceof ServerPlayer, + ServerRulesConfig.BOT_TOKEN.get().length(), + ServerRulesConfig.CHANNEL_ID.get(), + ServerRulesConfig.MESSAGE_ID.get(), + ServerRulesConfig.isMessageIdValid()); + if (source.getEntity() == null || !(source.getEntity() instanceof ServerPlayer)) { + LOGGER.info("[DIAG] Console path — fetching from Discord"); + DiscordFetcher.fetchRulesAsync().thenAccept(fetchedRules -> { + String rulesText; + if (fetchedRules != null) { + RulesCache.updateCache(fetchedRules); + rulesText = fetchedRules; + } else { + LOGGER.warn("Discord fetch failed. Using fallback rules."); + rulesText = RulesCache.getRules(); + } + source.getServer().execute(() -> source.sendSuccess(DiscordFormatter.formatRules(rulesText), false)); + }); + return 1; + } + ServerPlayer player = (ServerPlayer) source.getEntity(); + if (!CooldownManager.checkAndUpdateCooldown(player)) return 0; + if (RulesCache.isCacheValid()) { + player.sendMessage(DiscordFormatter.formatRules(RulesCache.getRules()), Util.NIL_UUID); + return 1; + } + player.sendMessage(new TextComponent("\u00a77Fetching latest rules..."), Util.NIL_UUID); + DiscordFetcher.fetchRulesAsync().thenAccept(fetchedRules -> { + String rulesText; + if (fetchedRules != null) { + RulesCache.updateCache(fetchedRules); + rulesText = fetchedRules; + } else { + LOGGER.warn("Discord fetch failed. Falling back to cached rules for {}", player.getName().getString()); + rulesText = RulesCache.getRules(); + } + MutableComponent formattedRules = DiscordFormatter.formatRules(rulesText); + source.getServer().execute(() -> player.sendMessage(formattedRules, Util.NIL_UUID)); + }); + return 1; + })); + } +} diff --git a/services/discord-rules/1.18.2/src/main/java/com/discordrules/ServerRules.java b/services/discord-rules/1.18.2/src/main/java/com/discordrules/ServerRules.java new file mode 100644 index 0000000..d6f1abd --- /dev/null +++ b/services/discord-rules/1.18.2/src/main/java/com/discordrules/ServerRules.java @@ -0,0 +1,53 @@ +package com.discordrules; + +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.RegisterCommandsEvent; +import net.minecraftforge.event.entity.player.PlayerEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.ModLoadingContext; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.config.ModConfig; +import net.minecraftforge.fml.event.config.ModConfigEvent; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Mod("discordrules") +public class ServerRules { + private static final Logger LOGGER = LoggerFactory.getLogger(ServerRules.class); + + public ServerRules() { + ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, ServerRulesConfig.SPEC); + FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onConfigLoaded); + FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onConfigReload); + MinecraftForge.EVENT_BUS.register(this); + LOGGER.info("Discord Rules Mod Initialized."); + } + + @SubscribeEvent + public void onRegisterCommands(RegisterCommandsEvent event) { + RulesCommand.register(event.getDispatcher()); + LOGGER.info("Registered /rules command."); + } + + @SubscribeEvent + public void onPlayerLogout(PlayerEvent.PlayerLoggedOutEvent event) { + CooldownManager.removePlayer(event.getPlayer().getUUID()); + } + + private void onConfigLoaded(ModConfigEvent.Loading event) { + if (event.getConfig().getSpec() == ServerRulesConfig.SPEC) { + String token = ServerRulesConfig.BOT_TOKEN.get(); + if (token.equals("YOUR_TOKEN_HERE")) { + LOGGER.warn("Rules mod is using default config values! Edit config/discordrules-common.toml"); + } else { + LOGGER.info("Rules mod config loaded successfully. Channel: {}", ServerRulesConfig.CHANNEL_ID.get()); + } + } + } + + private void onConfigReload(ModConfigEvent.Reloading event) { + LOGGER.info("Rules configuration reloaded! Invalidating cache."); + RulesCache.invalidate(); + } +} diff --git a/services/discord-rules/1.18.2/src/main/java/com/discordrules/ServerRulesConfig.java b/services/discord-rules/1.18.2/src/main/java/com/discordrules/ServerRulesConfig.java new file mode 100644 index 0000000..ba475d5 --- /dev/null +++ b/services/discord-rules/1.18.2/src/main/java/com/discordrules/ServerRulesConfig.java @@ -0,0 +1,43 @@ +package com.discordrules; + +import net.minecraftforge.common.ForgeConfigSpec; +import org.apache.commons.lang3.StringUtils; + +public class ServerRulesConfig { + public static final ForgeConfigSpec SPEC; + public static final ForgeConfigSpec.ConfigValue BOT_TOKEN; + public static final ForgeConfigSpec.ConfigValue CHANNEL_ID; + public static final ForgeConfigSpec.ConfigValue MESSAGE_ID; + public static final ForgeConfigSpec.IntValue COOLDOWN_SECONDS; + public static final ForgeConfigSpec.IntValue CACHE_MINUTES; + public static final ForgeConfigSpec.ConfigValue HEADER_COLOR; + public static final ForgeConfigSpec.ConfigValue BODY_COLOR; + public static final ForgeConfigSpec.BooleanValue STRIP_EMOJIS; + + static { + ForgeConfigSpec.Builder builder = new ForgeConfigSpec.Builder(); + builder.comment("Discord Rules Mod Configuration", + "IMPORTANT: Do not remove [section] headers when editing.", + "Values must stay under their section or the mod cannot read them.") + .push("discord"); + BOT_TOKEN = builder.comment("Discord Bot Token").define("bot_token", "YOUR_TOKEN_HERE"); + CHANNEL_ID = builder.comment("Discord Channel ID").define("channel_id", "1234567890123456789"); + MESSAGE_ID = builder.comment("Discord Message ID").define("message_id", "1234567890123456789"); + builder.pop(); + builder.push("display"); + HEADER_COLOR = builder.comment("Header color (bold lines). Valid: BLACK, DARK_BLUE, DARK_GREEN, DARK_AQUA, DARK_RED, DARK_PURPLE, GOLD, GRAY, DARK_GRAY, BLUE, GREEN, AQUA, RED, LIGHT_PURPLE, YELLOW, WHITE").define("header_color", "GOLD"); + BODY_COLOR = builder.comment("Body color (regular lines and bullet points)").define("body_color", "YELLOW"); + STRIP_EMOJIS = builder.comment("Strip emojis that Minecraft can't render (recommended: true)").define("strip_emojis", true); + builder.pop(); + builder.push("performance"); + COOLDOWN_SECONDS = builder.comment("Per-player cooldown in seconds").defineInRange("cooldown_seconds", 60, 0, 3600); + CACHE_MINUTES = builder.comment("Cache duration in minutes").defineInRange("cache_minutes", 30, 1, 1440); + builder.pop(); + SPEC = builder.build(); + } + + public static boolean isMessageIdValid() { + String id = MESSAGE_ID.get(); + return StringUtils.isNotBlank(id) && id.matches("^\\d{17,20}$"); + } +} diff --git a/services/discord-rules/1.18.2/src/main/resources/META-INF/mods.toml b/services/discord-rules/1.18.2/src/main/resources/META-INF/mods.toml new file mode 100644 index 0000000..3401644 --- /dev/null +++ b/services/discord-rules/1.18.2/src/main/resources/META-INF/mods.toml @@ -0,0 +1,27 @@ +modLoader="javafml" +loaderVersion="[40,)" +license="MIT" + +[[mods]] +modId="discordrules" +version="${file.jarVersion}" +displayName="Discord Rules" +authors="FirefrostGaming" +description=''' +Fetches server rules dynamically from a Discord message for the /rules command. +Configure your Discord bot token, channel, and message ID — players type /rules and see your latest rules in chat, formatted with configurable colors. +''' + +[[dependencies.discordrules]] +modId="forge" +mandatory=true +versionRange="[40,)" +ordering="NONE" +side="SERVER" + +[[dependencies.discordrules]] +modId="minecraft" +mandatory=true +versionRange="[1.18.2,1.19)" +ordering="NONE" +side="SERVER" diff --git a/services/discord-rules/discord-rules-1.0.5-1.18.2-forge.jar b/services/discord-rules/discord-rules-1.0.5-1.18.2-forge.jar new file mode 100644 index 0000000..de6f745 Binary files /dev/null and b/services/discord-rules/discord-rules-1.0.5-1.18.2-forge.jar differ diff --git a/services/rules-mod/1.18.2/.gradle/8.8/checksums/checksums.lock b/services/rules-mod/1.18.2/.gradle/8.8/checksums/checksums.lock new file mode 100644 index 0000000..0d66df5 Binary files /dev/null and b/services/rules-mod/1.18.2/.gradle/8.8/checksums/checksums.lock differ diff --git a/services/rules-mod/1.18.2/.gradle/8.8/checksums/md5-checksums.bin b/services/rules-mod/1.18.2/.gradle/8.8/checksums/md5-checksums.bin new file mode 100644 index 0000000..5f20b2e Binary files /dev/null and b/services/rules-mod/1.18.2/.gradle/8.8/checksums/md5-checksums.bin differ diff --git a/services/rules-mod/1.18.2/.gradle/8.8/checksums/sha1-checksums.bin b/services/rules-mod/1.18.2/.gradle/8.8/checksums/sha1-checksums.bin new file mode 100644 index 0000000..b57c30d Binary files /dev/null and b/services/rules-mod/1.18.2/.gradle/8.8/checksums/sha1-checksums.bin differ diff --git a/services/rules-mod/1.18.2/.gradle/8.8/dependencies-accessors/gc.properties b/services/rules-mod/1.18.2/.gradle/8.8/dependencies-accessors/gc.properties new file mode 100644 index 0000000..e69de29 diff --git a/services/rules-mod/1.18.2/.gradle/8.8/executionHistory/executionHistory.bin b/services/rules-mod/1.18.2/.gradle/8.8/executionHistory/executionHistory.bin new file mode 100644 index 0000000..b33a65f Binary files /dev/null and b/services/rules-mod/1.18.2/.gradle/8.8/executionHistory/executionHistory.bin differ diff --git a/services/rules-mod/1.18.2/.gradle/8.8/executionHistory/executionHistory.lock b/services/rules-mod/1.18.2/.gradle/8.8/executionHistory/executionHistory.lock new file mode 100644 index 0000000..fcf9ed8 Binary files /dev/null and b/services/rules-mod/1.18.2/.gradle/8.8/executionHistory/executionHistory.lock differ diff --git a/services/rules-mod/1.18.2/.gradle/8.8/expanded/expanded.lock b/services/rules-mod/1.18.2/.gradle/8.8/expanded/expanded.lock new file mode 100644 index 0000000..fea0266 Binary files /dev/null and b/services/rules-mod/1.18.2/.gradle/8.8/expanded/expanded.lock differ diff --git a/services/rules-mod/1.18.2/.gradle/8.8/fileChanges/last-build.bin b/services/rules-mod/1.18.2/.gradle/8.8/fileChanges/last-build.bin new file mode 100644 index 0000000..f76dd23 Binary files /dev/null and b/services/rules-mod/1.18.2/.gradle/8.8/fileChanges/last-build.bin differ diff --git a/services/rules-mod/1.18.2/.gradle/8.8/fileHashes/fileHashes.bin b/services/rules-mod/1.18.2/.gradle/8.8/fileHashes/fileHashes.bin new file mode 100644 index 0000000..fcc13f9 Binary files /dev/null and b/services/rules-mod/1.18.2/.gradle/8.8/fileHashes/fileHashes.bin differ diff --git a/services/rules-mod/1.18.2/.gradle/8.8/fileHashes/fileHashes.lock b/services/rules-mod/1.18.2/.gradle/8.8/fileHashes/fileHashes.lock new file mode 100644 index 0000000..e1d837a Binary files /dev/null and b/services/rules-mod/1.18.2/.gradle/8.8/fileHashes/fileHashes.lock differ diff --git a/services/rules-mod/1.18.2/.gradle/8.8/fileHashes/resourceHashesCache.bin b/services/rules-mod/1.18.2/.gradle/8.8/fileHashes/resourceHashesCache.bin new file mode 100644 index 0000000..33abbfb Binary files /dev/null and b/services/rules-mod/1.18.2/.gradle/8.8/fileHashes/resourceHashesCache.bin differ diff --git a/services/rules-mod/1.18.2/.gradle/8.8/gc.properties b/services/rules-mod/1.18.2/.gradle/8.8/gc.properties new file mode 100644 index 0000000..e69de29 diff --git a/services/rules-mod/1.18.2/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/services/rules-mod/1.18.2/.gradle/buildOutputCleanup/buildOutputCleanup.lock new file mode 100644 index 0000000..aef0a98 Binary files /dev/null and b/services/rules-mod/1.18.2/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/services/rules-mod/1.18.2/.gradle/buildOutputCleanup/cache.properties b/services/rules-mod/1.18.2/.gradle/buildOutputCleanup/cache.properties new file mode 100644 index 0000000..bef2f17 --- /dev/null +++ b/services/rules-mod/1.18.2/.gradle/buildOutputCleanup/cache.properties @@ -0,0 +1,2 @@ +#Tue Apr 14 08:26:19 CDT 2026 +gradle.version=8.8 diff --git a/services/rules-mod/1.18.2/.gradle/buildOutputCleanup/outputFiles.bin b/services/rules-mod/1.18.2/.gradle/buildOutputCleanup/outputFiles.bin new file mode 100644 index 0000000..f6bbfc0 Binary files /dev/null and b/services/rules-mod/1.18.2/.gradle/buildOutputCleanup/outputFiles.bin differ diff --git a/services/rules-mod/1.18.2/.gradle/file-system.probe b/services/rules-mod/1.18.2/.gradle/file-system.probe new file mode 100644 index 0000000..70f4e7a Binary files /dev/null and b/services/rules-mod/1.18.2/.gradle/file-system.probe differ diff --git a/services/rules-mod/1.18.2/.gradle/vcs-1/gc.properties b/services/rules-mod/1.18.2/.gradle/vcs-1/gc.properties new file mode 100644 index 0000000..e69de29 diff --git a/services/rules-mod/1.18.2/build.gradle b/services/rules-mod/1.18.2/build.gradle new file mode 100644 index 0000000..90d3564 --- /dev/null +++ b/services/rules-mod/1.18.2/build.gradle @@ -0,0 +1,50 @@ +buildscript { + repositories { + maven { url = 'https://maven.minecraftforge.net/' } + mavenCentral() + } + dependencies { + classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '6.0.+' + } +} + +apply plugin: 'net.minecraftforge.gradle' + +version = '1.0.5' +group = 'com.firefrostgaming.rules' +archivesBaseName = 'firefrostrules' + +java.toolchain.languageVersion = JavaLanguageVersion.of(17) + +minecraft { + mappings channel: 'official', version: '1.18.2' + runs { + server { + workingDirectory project.file('run') + property 'forge.logging.markers', 'REGISTRIES' + property 'forge.logging.console.level', 'debug' + mods { + firefrostrules { + source sourceSets.main + } + } + } + } +} + +dependencies { + minecraft 'net.minecraftforge:forge:1.18.2-40.2.4' +} + +jar { + manifest { + attributes([ + "Implementation-Title": "Firefrost Rules", + "Implementation-Version": project.version, + ]) + } +} + +tasks.withType(JavaCompile).configureEach { + options.encoding = 'UTF-8' +} diff --git a/services/rules-mod/1.18.2/gradle.properties b/services/rules-mod/1.18.2/gradle.properties new file mode 100644 index 0000000..29b2ac4 --- /dev/null +++ b/services/rules-mod/1.18.2/gradle.properties @@ -0,0 +1,2 @@ +org.gradle.jvmargs=-Xmx3G +org.gradle.daemon=false diff --git a/services/rules-mod/1.18.2/gradle/wrapper/gradle-wrapper.jar b/services/rules-mod/1.18.2/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..e644113 Binary files /dev/null and b/services/rules-mod/1.18.2/gradle/wrapper/gradle-wrapper.jar differ diff --git a/services/rules-mod/1.18.2/gradle/wrapper/gradle-wrapper.properties b/services/rules-mod/1.18.2/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..a441313 --- /dev/null +++ b/services/rules-mod/1.18.2/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/services/rules-mod/1.18.2/gradlew b/services/rules-mod/1.18.2/gradlew new file mode 100755 index 0000000..b740cf1 --- /dev/null +++ b/services/rules-mod/1.18.2/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/services/rules-mod/1.18.2/gradlew.bat b/services/rules-mod/1.18.2/gradlew.bat new file mode 100644 index 0000000..7101f8e --- /dev/null +++ b/services/rules-mod/1.18.2/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/services/rules-mod/1.18.2/settings.gradle b/services/rules-mod/1.18.2/settings.gradle new file mode 100644 index 0000000..7c71559 --- /dev/null +++ b/services/rules-mod/1.18.2/settings.gradle @@ -0,0 +1,8 @@ +pluginManagement { + repositories { + gradlePluginPortal() + maven { url = 'https://maven.minecraftforge.net/' } + } +} + +rootProject.name = 'firefrostrules' diff --git a/services/rules-mod/1.18.2/src/main/java/com/firefrostgaming/rules/CooldownManager.java b/services/rules-mod/1.18.2/src/main/java/com/firefrostgaming/rules/CooldownManager.java new file mode 100644 index 0000000..9d57220 --- /dev/null +++ b/services/rules-mod/1.18.2/src/main/java/com/firefrostgaming/rules/CooldownManager.java @@ -0,0 +1,32 @@ +package com.firefrostgaming.rules; + +import net.minecraft.Util; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.server.level.ServerPlayer; +import java.time.Duration; +import java.time.Instant; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public class CooldownManager { + private static final ConcurrentHashMap COOLDOWNS = new ConcurrentHashMap<>(); + + public static boolean checkAndUpdateCooldown(ServerPlayer player) { + UUID playerId = player.getUUID(); + Instant now = Instant.now(); + int cooldownSeconds = ServerRulesConfig.COOLDOWN_SECONDS.get(); + Instant lastUsed = COOLDOWNS.get(playerId); + if (lastUsed != null) { + long secondsSinceLastUse = Duration.between(lastUsed, now).getSeconds(); + if (secondsSinceLastUse < cooldownSeconds) { + long remaining = cooldownSeconds - secondsSinceLastUse; + player.sendMessage(new TextComponent("\u00A7cPlease wait " + remaining + " seconds before checking the rules again."), Util.NIL_UUID); + return false; + } + } + COOLDOWNS.put(playerId, now); + return true; + } + + public static void removePlayer(UUID playerId) { COOLDOWNS.remove(playerId); } +} diff --git a/services/rules-mod/1.18.2/src/main/java/com/firefrostgaming/rules/DiscordFetcher.java b/services/rules-mod/1.18.2/src/main/java/com/firefrostgaming/rules/DiscordFetcher.java new file mode 100644 index 0000000..bb5aad3 --- /dev/null +++ b/services/rules-mod/1.18.2/src/main/java/com/firefrostgaming/rules/DiscordFetcher.java @@ -0,0 +1,47 @@ +package com.firefrostgaming.rules; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.time.Duration; +import java.util.concurrent.CompletableFuture; + +public class DiscordFetcher { + private static final Logger LOGGER = LoggerFactory.getLogger(DiscordFetcher.class); + private static final HttpClient CLIENT = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build(); + + public static CompletableFuture fetchRulesAsync() { + String messageId = ServerRulesConfig.MESSAGE_ID.get(); + LOGGER.info("[DIAG] fetchRulesAsync called. messageId='{}', isValid={}", messageId, ServerRulesConfig.isMessageIdValid()); + if (!ServerRulesConfig.isMessageIdValid()) { + LOGGER.error("Invalid Discord Message ID in config. Raw value: '{}'", messageId); + return CompletableFuture.completedFuture(null); + } + String token = ServerRulesConfig.BOT_TOKEN.get(); + String channelId = ServerRulesConfig.CHANNEL_ID.get(); + URI uri = URI.create("https://discord.com/api/v10/channels/" + channelId + "/messages/" + messageId); + LOGGER.info("[DIAG] Fetching from Discord: channel={}, message={}", channelId, messageId); + HttpRequest request = HttpRequest.newBuilder().uri(uri) + .header("Authorization", "Bot " + token) + .header("Accept", "application/json").GET().build(); + return CLIENT.sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .thenApply(response -> { + if (response.statusCode() == 200) { + JsonObject json = JsonParser.parseString(response.body()).getAsJsonObject(); + return json.get("content").getAsString(); + } else { + LOGGER.error("Discord API returned status: {}", response.statusCode()); + return null; + } + }) + .exceptionally(ex -> { + LOGGER.error("Network error while fetching Discord rules", ex); + return null; + }); + } +} diff --git a/services/rules-mod/1.18.2/src/main/java/com/firefrostgaming/rules/DiscordFormatter.java b/services/rules-mod/1.18.2/src/main/java/com/firefrostgaming/rules/DiscordFormatter.java new file mode 100644 index 0000000..f4859f4 --- /dev/null +++ b/services/rules-mod/1.18.2/src/main/java/com/firefrostgaming/rules/DiscordFormatter.java @@ -0,0 +1,45 @@ +package com.firefrostgaming.rules; + +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.network.chat.TextComponent; + +public class DiscordFormatter { + public static MutableComponent formatRules(String rawDiscordText) { + String processedText = convertEmojis(rawDiscordText); + String lowerText = processedText.toLowerCase(); + ChatFormatting headerColor = ChatFormatting.DARK_PURPLE; + ChatFormatting bodyColor = ChatFormatting.LIGHT_PURPLE; + if (lowerText.contains("fire") || lowerText.contains("[fire]")) { + headerColor = ChatFormatting.GOLD; + bodyColor = ChatFormatting.YELLOW; + } else if (lowerText.contains("frost") || lowerText.contains("[frost]")) { + headerColor = ChatFormatting.AQUA; + bodyColor = ChatFormatting.DARK_AQUA; + } + MutableComponent rootComponent = new TextComponent(""); + String[] lines = processedText.split("\n"); + for (String line : lines) { + MutableComponent lineComponent; + if (line.startsWith("**") && line.endsWith("**")) { + String cleanLine = line.replace("**", ""); + lineComponent = new TextComponent(cleanLine); + lineComponent.withStyle(headerColor, ChatFormatting.BOLD); + } else if (line.trim().startsWith("-") || line.trim().startsWith("\u2022")) { + lineComponent = new TextComponent(" " + line.trim()); + lineComponent.withStyle(bodyColor); + } else { + lineComponent = new TextComponent(line); + lineComponent.withStyle(bodyColor); + } + rootComponent.append(lineComponent).append(new TextComponent("\n")); + } + return rootComponent; + } + + private static String convertEmojis(String text) { + if (text == null) return ""; + return text.replace("\uD83D\uDD25", "[Fire]").replace("\u2744\uFE0F", "[Frost]") + .replace("\uD83D\uDC9C", "[Arcane]").replaceAll("[\\x{1F300}-\\x{1F9FF}]", ""); + } +} diff --git a/services/rules-mod/1.18.2/src/main/java/com/firefrostgaming/rules/RulesCache.java b/services/rules-mod/1.18.2/src/main/java/com/firefrostgaming/rules/RulesCache.java new file mode 100644 index 0000000..c4f557d --- /dev/null +++ b/services/rules-mod/1.18.2/src/main/java/com/firefrostgaming/rules/RulesCache.java @@ -0,0 +1,26 @@ +package com.firefrostgaming.rules; + +import java.time.Instant; + +public class RulesCache { + private static String cachedRules = null; + private static Instant lastFetchTime = Instant.MIN; + private static final String FALLBACK_RULES = + "[Fire] Server Rules\n1. Be respectful to all players.\n2. No griefing or cheating.\n3. Follow staff instructions.\nPlease check Discord for the full rules list."; + + public static boolean isCacheValid() { + if (cachedRules == null) return false; + long cacheMinutes = ServerRulesConfig.CACHE_MINUTES.get(); + return Instant.now().isBefore(lastFetchTime.plusSeconds(cacheMinutes * 60)); + } + + public static void updateCache(String newRules) { + if (newRules != null && !newRules.trim().isEmpty()) { + cachedRules = newRules; + lastFetchTime = Instant.now(); + } + } + + public static String getRules() { return cachedRules != null ? cachedRules : FALLBACK_RULES; } + public static void invalidate() { cachedRules = null; lastFetchTime = Instant.MIN; } +} diff --git a/services/rules-mod/1.18.2/src/main/java/com/firefrostgaming/rules/RulesCommand.java b/services/rules-mod/1.18.2/src/main/java/com/firefrostgaming/rules/RulesCommand.java new file mode 100644 index 0000000..1d032c5 --- /dev/null +++ b/services/rules-mod/1.18.2/src/main/java/com/firefrostgaming/rules/RulesCommand.java @@ -0,0 +1,62 @@ +package com.firefrostgaming.rules; + +import com.mojang.brigadier.CommandDispatcher; +import net.minecraft.Util; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.server.level.ServerPlayer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RulesCommand { + private static final Logger LOGGER = LoggerFactory.getLogger(RulesCommand.class); + + public static void register(CommandDispatcher dispatcher) { + dispatcher.register(Commands.literal("rules").executes(context -> { + CommandSourceStack source = context.getSource(); + LOGGER.info("[DIAG] /rules invoked. isPlayer={}, token length={}, channel={}, messageId={}, isValid={}", + source.getEntity() instanceof ServerPlayer, + ServerRulesConfig.BOT_TOKEN.get().length(), + ServerRulesConfig.CHANNEL_ID.get(), + ServerRulesConfig.MESSAGE_ID.get(), + ServerRulesConfig.isMessageIdValid()); + if (source.getEntity() == null || !(source.getEntity() instanceof ServerPlayer)) { + LOGGER.info("[DIAG] Console path — fetching from Discord"); + DiscordFetcher.fetchRulesAsync().thenAccept(fetchedRules -> { + String rulesText; + if (fetchedRules != null) { + RulesCache.updateCache(fetchedRules); + rulesText = fetchedRules; + } else { + LOGGER.warn("Discord fetch failed. Using fallback rules."); + rulesText = RulesCache.getRules(); + } + source.getServer().execute(() -> source.sendSuccess(DiscordFormatter.formatRules(rulesText), false)); + }); + return 1; + } + ServerPlayer player = (ServerPlayer) source.getEntity(); + if (!CooldownManager.checkAndUpdateCooldown(player)) return 0; + if (RulesCache.isCacheValid()) { + player.sendMessage(DiscordFormatter.formatRules(RulesCache.getRules()), Util.NIL_UUID); + return 1; + } + player.sendMessage(new TextComponent("\u00A77Fetching latest rules..."), Util.NIL_UUID); + DiscordFetcher.fetchRulesAsync().thenAccept(fetchedRules -> { + String rulesText; + if (fetchedRules != null) { + RulesCache.updateCache(fetchedRules); + rulesText = fetchedRules; + } else { + LOGGER.warn("Discord fetch failed. Falling back to cached rules for {}", player.getName().getString()); + rulesText = RulesCache.getRules(); + } + MutableComponent formattedRules = DiscordFormatter.formatRules(rulesText); + source.getServer().execute(() -> player.sendMessage(formattedRules, Util.NIL_UUID)); + }); + return 1; + })); + } +} diff --git a/services/rules-mod/1.18.2/src/main/java/com/firefrostgaming/rules/ServerRules.java b/services/rules-mod/1.18.2/src/main/java/com/firefrostgaming/rules/ServerRules.java new file mode 100644 index 0000000..f275076 --- /dev/null +++ b/services/rules-mod/1.18.2/src/main/java/com/firefrostgaming/rules/ServerRules.java @@ -0,0 +1,53 @@ +package com.firefrostgaming.rules; + +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.RegisterCommandsEvent; +import net.minecraftforge.event.entity.player.PlayerEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.ModLoadingContext; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.config.ModConfig; +import net.minecraftforge.fml.event.config.ModConfigEvent; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Mod("firefrostrules") +public class ServerRules { + private static final Logger LOGGER = LoggerFactory.getLogger(ServerRules.class); + + public ServerRules() { + ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, ServerRulesConfig.SPEC); + FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onConfigLoaded); + FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onConfigReload); + MinecraftForge.EVENT_BUS.register(this); + LOGGER.info("Firefrost Rules Mod Initialized."); + } + + @SubscribeEvent + public void onRegisterCommands(RegisterCommandsEvent event) { + RulesCommand.register(event.getDispatcher()); + LOGGER.info("Registered /rules command."); + } + + @SubscribeEvent + public void onPlayerLogout(PlayerEvent.PlayerLoggedOutEvent event) { + CooldownManager.removePlayer(event.getPlayer().getUUID()); + } + + private void onConfigLoaded(ModConfigEvent.Loading event) { + if (event.getConfig().getSpec() == ServerRulesConfig.SPEC) { + String token = ServerRulesConfig.BOT_TOKEN.get(); + if (token.equals("YOUR_TOKEN_HERE")) { + LOGGER.warn("Rules mod is using default config values! Edit config/firefrostrules-common.toml"); + } else { + LOGGER.info("Rules mod config loaded successfully. Channel: {}", ServerRulesConfig.CHANNEL_ID.get()); + } + } + } + + private void onConfigReload(ModConfigEvent.Reloading event) { + LOGGER.info("Rules configuration reloaded! Invalidating cache."); + RulesCache.invalidate(); + } +} diff --git a/services/rules-mod/1.18.2/src/main/java/com/firefrostgaming/rules/ServerRulesConfig.java b/services/rules-mod/1.18.2/src/main/java/com/firefrostgaming/rules/ServerRulesConfig.java new file mode 100644 index 0000000..e0e74c2 --- /dev/null +++ b/services/rules-mod/1.18.2/src/main/java/com/firefrostgaming/rules/ServerRulesConfig.java @@ -0,0 +1,35 @@ +package com.firefrostgaming.rules; + +import net.minecraftforge.common.ForgeConfigSpec; +import org.apache.commons.lang3.StringUtils; + +public class ServerRulesConfig { + public static final ForgeConfigSpec SPEC; + public static final ForgeConfigSpec.ConfigValue BOT_TOKEN; + public static final ForgeConfigSpec.ConfigValue CHANNEL_ID; + public static final ForgeConfigSpec.ConfigValue MESSAGE_ID; + public static final ForgeConfigSpec.IntValue COOLDOWN_SECONDS; + public static final ForgeConfigSpec.IntValue CACHE_MINUTES; + + static { + ForgeConfigSpec.Builder builder = new ForgeConfigSpec.Builder(); + builder.comment("Firefrost Rules Mod Configuration", + "IMPORTANT: Do not remove [section] headers when editing.", + "Values must stay under their section or the mod cannot read them.") + .push("discord"); + BOT_TOKEN = builder.comment("Discord Bot Token").define("bot_token", "YOUR_TOKEN_HERE"); + CHANNEL_ID = builder.comment("Discord Channel ID").define("channel_id", "1234567890123456789"); + MESSAGE_ID = builder.comment("Discord Message ID").define("message_id", "1234567890123456789"); + builder.pop(); + builder.push("performance"); + COOLDOWN_SECONDS = builder.comment("Per-player cooldown in seconds").defineInRange("cooldown_seconds", 60, 0, 3600); + CACHE_MINUTES = builder.comment("Cache duration in minutes").defineInRange("cache_minutes", 30, 1, 1440); + builder.pop(); + SPEC = builder.build(); + } + + public static boolean isMessageIdValid() { + String id = MESSAGE_ID.get(); + return StringUtils.isNotBlank(id) && id.matches("^\\d{17,20}$"); + } +} diff --git a/services/rules-mod/1.18.2/src/main/resources/META-INF/mods.toml b/services/rules-mod/1.18.2/src/main/resources/META-INF/mods.toml new file mode 100644 index 0000000..d83ddc8 --- /dev/null +++ b/services/rules-mod/1.18.2/src/main/resources/META-INF/mods.toml @@ -0,0 +1,28 @@ +modLoader="javafml" +loaderVersion="[40,)" +license="All Rights Reserved" +issueTrackerURL="https://firefrostgaming.com/support" + +[[mods]] +modId="firefrostrules" +version="${file.jarVersion}" +displayName="Firefrost Rules" +displayURL="https://firefrostgaming.com" +authors="Firefrost Gaming" +description=''' +Fetches server rules dynamically from Discord for the /rules command. +''' + +[[dependencies.firefrostrules]] +modId="forge" +mandatory=true +versionRange="[40,)" +ordering="NONE" +side="SERVER" + +[[dependencies.firefrostrules]] +modId="minecraft" +mandatory=true +versionRange="[1.18.2,1.19)" +ordering="NONE" +side="SERVER" diff --git a/services/rules-mod/firefrostrules-1.0.5-1.18.2-forge.jar b/services/rules-mod/firefrostrules-1.0.5-1.18.2-forge.jar new file mode 100644 index 0000000..ae2f9f9 Binary files /dev/null and b/services/rules-mod/firefrostrules-1.0.5-1.18.2-forge.jar differ