1
0
mirror of https://github.com/chylex/Minecraft-Server-Properties-Reload.git synced 2025-05-04 08:34:10 +02:00

Add main mod files & prepare '/properties reload' command

This commit is contained in:
chylex 2021-10-16 03:24:15 +02:00
parent 7f231f0db1
commit 4bf0dc7419
Signed by: chylex
GPG Key ID: 4DE42C8F19A80548
16 changed files with 586 additions and 0 deletions

46
Fabric/build.gradle.kts Normal file
View File

@ -0,0 +1,46 @@
val modId: String by project
val minecraftVersion: String by project
val fabricVersion: String by project
plugins {
idea
id("fabric-loom") version "0.9-SNAPSHOT"
}
dependencies {
minecraft("com.mojang:minecraft:$minecraftVersion")
modImplementation("net.fabricmc:fabric-loader:$fabricVersion")
mappings(loom.officialMojangMappings())
}
loom {
runs {
named("client") {
configName = "Fabric Client"
client()
runDir("run")
ideConfigGenerated(true)
}
named("server") {
configName = "Fabric Server"
server()
runDir("run")
ideConfigGenerated(true)
}
}
mixin {
add(sourceSets.main.get(), "$modId.refmap.json")
}
}
tasks.processResources {
filesMatching("fabric.mod.json") {
expand(inputs.properties)
}
}
tasks.remapJar {
archiveVersion.set(tasks.jar.get().archiveVersion)
}

View File

@ -0,0 +1,22 @@
{
"schemaVersion": 1,
"id": "serverpropertiesreload",
"name": "${name}",
"description": "${description}",
"version": "${version}",
"license": "${license}",
"authors": [
"${author}"
],
"contact": {
"issues": "${issuesURL}",
"sources": "${sourcesURL}"
},
"environment": "server",
"mixins": [
"serverpropertiesreload.mixins.json"
]
}

95
Forge/build.gradle.kts Normal file
View File

@ -0,0 +1,95 @@
import net.minecraftforge.gradle.userdev.UserDevExtension
import org.gradle.api.file.DuplicatesStrategy.INCLUDE
import org.spongepowered.asm.gradle.plugins.MixinExtension
val modId: String by project
val minecraftVersion: String by project
val forgeVersion: String by project
val mixinVersion: String by project
buildscript {
repositories {
maven("https://maven.minecraftforge.net")
maven("https://repo.spongepowered.org/maven")
mavenCentral()
}
dependencies {
classpath(group = "net.minecraftforge.gradle", name = "ForgeGradle", version = "5.1.+") { isChanging = true }
classpath(group = "org.spongepowered", name = "mixingradle", version = "0.7-SNAPSHOT")
}
}
plugins {
java
eclipse
}
apply {
plugin("net.minecraftforge.gradle")
plugin("org.spongepowered.mixin")
}
dependencies {
"minecraft"("net.minecraftforge:forge:$minecraftVersion-$forgeVersion")
if (System.getProperty("idea.sync.active") != "true") {
annotationProcessor("org.spongepowered:mixin:$mixinVersion:processor")
}
}
configure<UserDevExtension> {
mappings("official", minecraftVersion)
runs {
create("client") {
taskName = "Client"
workingDirectory(rootProject.file("run"))
ideaModule("${rootProject.name}.${project.name}.main")
property("mixin.env.remapRefMap", "true")
property("mixin.env.refMapRemappingFile", "$projectDir/build/createSrgToMcp/output.srg")
arg("-mixin.config=$modId.mixins.json")
mods {
create(modId) {
source(sourceSets.main.get())
source(rootProject.sourceSets.main.get())
}
}
}
create("server") {
taskName = "Server"
workingDirectory(rootProject.file("run"))
ideaModule("${rootProject.name}.${project.name}.main")
property("mixin.env.remapRefMap", "true")
property("mixin.env.refMapRemappingFile", "$projectDir/build/createSrgToMcp/output.srg")
arg("-mixin.config=$modId.mixins.json")
mods {
create(modId) {
source(sourceSets.main.get())
source(rootProject.sourceSets.main.get())
}
}
}
}
}
configure<MixinExtension> {
add(sourceSets.main.get(), "$modId.refmap.json")
}
tasks.processResources {
from(sourceSets.main.get().resources.srcDirs) {
include("META-INF/mods.toml")
expand(inputs.properties)
duplicatesStrategy = INCLUDE
}
}
tasks.jar {
finalizedBy("reobfJar")
}

View File

@ -0,0 +1,12 @@
package chylex.serverproperties;
import net.minecraftforge.fml.IExtensionPoint.DisplayTest;
import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fmllegacy.network.FMLNetworkConstants;
@Mod("serverpropertiesreload")
public final class ForgeMod {
public ForgeMod() {
ModLoadingContext.get().registerExtensionPoint(DisplayTest.class, () -> new DisplayTest(() -> FMLNetworkConstants.IGNORESERVERONLY, (a, b) -> true));
}
}

View File

@ -0,0 +1,26 @@
modLoader = "javafml"
loaderVersion = "[37,)"
authors = "${author}"
license = "${license}"
issueTrackerURL = "${issuesURL}"
[[mods]]
modId = "serverpropertiesreload"
version = "${version}"
displayName = "${name}"
description = "${description}"
[[dependencies.serverpropertiesreload]]
modId = "minecraft"
mandatory = true
versionRange = "[1.17.1,)"
ordering = "NONE"
side = "BOTH"
[[dependencies.serverpropertiesreload]]
modId = "forge"
mandatory = true
versionRange = "[37,)"
ordering = "NONE"
side = "BOTH"

146
build.gradle.kts Normal file
View File

@ -0,0 +1,146 @@
import org.gradle.api.file.DuplicatesStrategy.EXCLUDE
import java.text.SimpleDateFormat
import java.util.Date
val modId: String by project
val modName: String by project
val modDescription: String by project
val modAuthor: String by project
val modVersion: String by project
val modLicense: String by project
val modSourcesURL: String by project
val modIssuesURL: String by project
val minecraftVersion: String by project
val mixinVersion: String by project
val modNameStripped = modName.replace(" ", "")
val jarVersion = "$minecraftVersion+v$modVersion"
buildscript {
repositories {
maven("https://repo.spongepowered.org/maven")
}
}
plugins {
`java-library`
idea
id("org.spongepowered.gradle.vanilla") version "0.2.1-SNAPSHOT"
}
idea {
module {
excludeDirs.add(project.file("run"))
}
}
repositories {
maven("https://repo.spongepowered.org/maven")
mavenCentral()
}
dependencies {
implementation("org.spongepowered:mixin:$mixinVersion")
api("com.google.code.findbugs:jsr305:3.0.2")
}
base {
archivesName.set("$modNameStripped-Common")
}
minecraft {
version(minecraftVersion)
runs.clear()
}
allprojects {
group = "com.$modAuthor.$modId"
version = modVersion
apply(plugin = "java")
dependencies {
implementation("org.jetbrains:annotations:22.0.0")
}
extensions.getByType<JavaPluginExtension>().apply {
toolchain.languageVersion.set(JavaLanguageVersion.of(16))
}
tasks.withType<JavaCompile> {
options.encoding = "UTF-8"
options.release.set(16)
}
}
subprojects {
repositories {
maven("https://repo.spongepowered.org/maven")
}
dependencies {
implementation(rootProject)
}
base {
archivesName.set("$modNameStripped-${project.name}")
}
tasks.withType<JavaCompile> {
source({ rootProject.sourceSets.main.get().allSource })
}
tasks.processResources {
from(rootProject.sourceSets.main.get().resources)
inputs.property("name", modName)
inputs.property("description", modDescription)
inputs.property("version", modVersion)
inputs.property("author", modAuthor)
inputs.property("license", modLicense)
inputs.property("sourcesURL", modSourcesURL)
inputs.property("issuesURL", modIssuesURL)
}
tasks.jar {
archiveVersion.set(jarVersion)
from(rootProject.file("LICENSE"))
manifest {
attributes(
"Specification-Title" to modId,
"Specification-Vendor" to modAuthor,
"Specification-Version" to "1",
"Implementation-Title" to "$modNameStripped-${project.name}",
"Implementation-Vendor" to modAuthor,
"Implementation-Version" to modVersion,
"Implementation-Timestamp" to SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").format(Date()),
"MixinConfigs" to "$modId.mixins.json"
)
}
}
}
tasks.register("setupIdea") {
group = "mod"
dependsOn(project(":Forge").tasks.getByName("genIntellijRuns"))
dependsOn(project(":Fabric").tasks.getByName("genSources"))
}
val copyJars = tasks.register<Copy>("copyJars") {
group = "build"
duplicatesStrategy = EXCLUDE
for (subproject in subprojects) {
dependsOn(subproject.tasks.build)
from(subproject.base.libsDirectory.file("${subproject.base.archivesName.get()}-$jarVersion.jar"))
}
into(file("${project.buildDir}/dist"))
}
tasks.build {
finalizedBy(copyJars)
}

19
gradle.properties Normal file
View File

@ -0,0 +1,19 @@
# Mod
modId=serverpropertiesreload
modName=Server Properties Reload
modDescription=Adds '/properties' command to modify or reload server properties
modAuthor=chylex
modVersion=1.0.0
modLicense=MPL-2.0
modSourcesURL=https://github.com/chylex/Minecraft-Server-Properties-Reload
modIssuesURL=https://github.com/chylex/Minecraft-Server-Properties-Reload/issues
# Dependencies
minecraftVersion=1.17.1
forgeVersion=37.0.75
fabricVersion=0.11.7
mixinVersion=0.8.4
# Gradle
org.gradle.jvmargs=-Xmx3G
org.gradle.daemon=false

17
settings.gradle.kts Normal file
View File

@ -0,0 +1,17 @@
rootProject.name = "Server-Properties-Reload"
pluginManagement {
repositories {
gradlePluginPortal()
maven(url = "https://maven.fabricmc.net/") { name = "Fabric" }
maven(url = "https://repo.spongepowered.org/repository/maven-public/") { name = "Sponge Snapshots" }
}
}
if (settings.extra.has("forgeVersion")) {
include("Forge")
}
if (settings.extra.has("fabricVersion")) {
include("Fabric")
}

View File

@ -0,0 +1,89 @@
package chylex.serverproperties.command;
import chylex.serverproperties.mixin.DedicatedServerPropertiesMixin;
import chylex.serverproperties.mixin.SettingsMixin;
import chylex.serverproperties.props.ServerProperties;
import chylex.serverproperties.props.ServerProperty;
import com.mojang.brigadier.CommandDispatcher;
import net.minecraft.ChatFormatting;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.server.dedicated.DedicatedServerProperties;
import java.nio.file.Paths;
import java.util.HashSet;
import java.util.Map.Entry;
import java.util.Set;
import static net.minecraft.commands.Commands.literal;
public final class PropertiesCommand {
private PropertiesCommand() {}
public static void register(final CommandDispatcher<CommandSourceStack> dispatcher) {
dispatcher.register(literal("properties")
.requires(s -> s.hasPermission(2))
.then(literal("reload")
.executes(c -> reloadPropertiesFile(c.getSource())))
);
}
@SuppressWarnings("CastToIncompatibleInterface")
private static int reloadPropertiesFile(final CommandSourceStack s) {
final MinecraftServer server = s.getServer();
if (!(server instanceof final DedicatedServer dedicatedServer)) {
s.sendFailure(new TextComponent("This command is only supported on dedicated servers!"));
return 0;
}
final DedicatedServerProperties oldProperties = dedicatedServer.getProperties();
final DedicatedServerProperties newProperties = DedicatedServerProperties.fromFile(Paths.get("server.properties"));
final Set<String> unknownPropertyNames = new HashSet<>(((SettingsMixin)newProperties).getProperties().stringPropertyNames());
s.sendSuccess(new TextComponent("Reloading server properties:"), true);
int reloadedProperties = 0;
int failedProperties = 0;
for (final Entry<String, ServerProperty<?>> entry : ServerProperties.all().stream().sorted(Entry.comparingByKey()).toList()) {
final String name = entry.getKey();
final ServerProperty<?> prop = entry.getValue();
unknownPropertyNames.remove(name);
if (prop.hasChanged(oldProperties, newProperties)) {
final String oldValue = prop.toStringFrom(oldProperties);
final String newValue = prop.toStringFrom(newProperties);
try {
prop.apply(dedicatedServer, newProperties, (DedicatedServerPropertiesMixin)oldProperties);
} catch (final UnsupportedOperationException e) {
s.sendSuccess(new TextComponent(" " + name + ':').withStyle(ChatFormatting.RED)
.append(new TextComponent(" cannot be reloaded").withStyle(ChatFormatting.WHITE)), true);
++failedProperties;
continue;
}
s.sendSuccess(new TextComponent(" " + name + ": ").withStyle(ChatFormatting.LIGHT_PURPLE)
.append(new TextComponent(oldValue).withStyle(ChatFormatting.WHITE))
.append(new TextComponent(" -> ").withStyle(ChatFormatting.GRAY))
.append(new TextComponent(newValue).withStyle(ChatFormatting.WHITE)), true);
++reloadedProperties;
}
}
for (final String name : unknownPropertyNames.stream().sorted().toList()) {
s.sendSuccess(new TextComponent(" " + name + ':').withStyle(ChatFormatting.GRAY)
.append(new TextComponent(" skipped unknown property").withStyle(ChatFormatting.WHITE)), true);
}
if (reloadedProperties == 0 && failedProperties == 0) {
s.sendSuccess(new TextComponent(" Found no changes").withStyle(ChatFormatting.GRAY), true);
}
return reloadedProperties;
}
}

View File

@ -0,0 +1,19 @@
package chylex.serverproperties.mixin;
import chylex.serverproperties.command.PropertiesCommand;
import net.minecraft.commands.Commands;
import net.minecraft.commands.Commands.CommandSelection;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(Commands.class)
public class CommandMixin {
@Inject(method = "<init>", at = @At("RETURN"))
private void init(final CommandSelection commandSelection, final CallbackInfo ci) {
@SuppressWarnings("ConstantConditions")
final Commands commands = (Commands)(Object)this;
PropertiesCommand.register(commands.getDispatcher());
}
}

View File

@ -0,0 +1,8 @@
package chylex.serverproperties.mixin;
import net.minecraft.server.dedicated.DedicatedServerProperties;
import org.spongepowered.asm.mixin.Mixin;
@Mixin(DedicatedServerProperties.class)
public interface DedicatedServerPropertiesMixin {
}

View File

@ -0,0 +1,12 @@
package chylex.serverproperties.mixin;
import net.minecraft.server.dedicated.Settings;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import java.util.Properties;
@Mixin(Settings.class)
public interface SettingsMixin {
@Accessor
Properties getProperties();
}

View File

@ -0,0 +1,25 @@
package chylex.serverproperties.props;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
public final class ServerProperties {
private static final Map<String, ServerProperty<?>> ALL = new HashMap<>();
public static Collection<Entry<String, ServerProperty<?>>> all() {
return Collections.unmodifiableCollection(ALL.entrySet());
}
private static void register(final String name, final ServerProperty<?> property) {
if (ALL.put(name, property) != null) {
throw new IllegalArgumentException("Server property with name '" + name + "' is already registered!");
}
}
static {
}
private ServerProperties() {}
}

View File

@ -0,0 +1,28 @@
package chylex.serverproperties.props;
import chylex.serverproperties.mixin.DedicatedServerPropertiesMixin;
import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.server.dedicated.DedicatedServerProperties;
import java.util.Objects;
public abstract class ServerProperty<T> {
public final boolean hasChanged(final DedicatedServerProperties oldProperties, final DedicatedServerProperties newProperties) {
return !Objects.equals(get(oldProperties), get(newProperties));
}
public final void apply(final DedicatedServer server, final DedicatedServerProperties source, final DedicatedServerPropertiesMixin target) {
apply(server, target, get(source));
}
public final String toStringFrom(final DedicatedServerProperties source) {
return toString(get(source));
}
public abstract T get(DedicatedServerProperties properties);
public abstract void apply(DedicatedServer server, DedicatedServerPropertiesMixin target, T value);
public String toString(final T value) {
return Objects.toString(value, "<null>");
}
}

View File

@ -0,0 +1,7 @@
{
"pack": {
"description": "Server Properties Reload",
"pack_format": 7,
"_comment": ""
}
}

View File

@ -0,0 +1,15 @@
{
"required": true,
"minVersion": "0.8",
"package": "chylex.serverproperties.mixin",
"refmap": "serverpropertiesreload.refmap.json",
"compatibilityLevel": "JAVA_16",
"server": [
"CommandMixin",
"DedicatedServerPropertiesMixin",
"SettingsMixin"
],
"injectors": {
"defaultRequire": 1
}
}