4 Commits

Author SHA1 Message Date
TheWhiteDog9487 92d1e282d8 使用YACL Config API自动将功能开关状态保存到文件内
使用文件系统监听器使配置文件被外部更改时可以自动重载到游戏内
解决直接连接窗口内输入框里的服务器IP没有被移除空格处理的问题
在移除空格之后打印日志
添加英文README
重新给main入口类添加`@Deprecated`标记,这东西确实用不到,后面看要不要直接删了
为AI Agent添加AGENTS.md文件以指示通用提示词
2026-06-04 19:40:45 +08:00
TheWhiteDog9487 5cb7a45ff4 更改ModID
同步Fabric Mod Template那边的Kotlin DSL官方配置
移除main入口类的`@Deprecated`标记
( 我当初为什么要弃用它?
补全ChangeLog.md
( 已经彻底忘记它的存在了
更新Github Action配置
更新Gradle版本

由于本次更改不存在对玩家具有实质影响的内容,故合并到下一个Mod版本内一并发布
2026-06-01 15:16:58 +08:00
TheWhiteDog9487 37722dcd8f 修复Github Action Workflow
又忘记改了
2026-03-26 09:22:05 +08:00
TheWhiteDog9487 85486ab217 0.2.2
更新到Minecraft 26.1
由于源码变更,更换了一个新的实现方案,稍微测试了一下没太大毛病,应该还行

( AI太好用了你们知道吗,我只需要描述我需要使用什么方法比如@Redirect @Inject注入哪个字段或者函数的哪个位置,代码就可以全自动生成,连字段描述符都不需要我弄了
2026-03-26 09:15:11 +08:00
33 changed files with 496 additions and 237 deletions
+8 -15
View File
@@ -8,30 +8,23 @@ on: [pull_request, push]
jobs:
build:
strategy:
matrix:
# Use these Java versions
java: [
21, # Current Java LTS
]
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
steps:
- name: checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: validate gradle wrapper
uses: gradle/wrapper-validation-action@v2
- name: setup jdk ${{ matrix.java }}
uses: actions/setup-java@v4
uses: gradle/actions/wrapper-validation@v6
- name: setup jdk
uses: actions/setup-java@v5
with:
java-version: ${{ matrix.java }}
java-version: '25'
distribution: 'microsoft'
- name: make gradle wrapper executable
run: chmod +x ./gradlew
- name: build
run: ./gradlew build
- name: capture build artifacts
if: ${{ matrix.java == '21' }} # Only upload artifacts built from latest java
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: Artifacts
path: build/libs/
path: build/libs/
+6
View File
@@ -0,0 +1,6 @@
- 如果需要Minecraft经过反混淆的源码,直接在`.gradle/loom-cache/minecraftMaven/net/minecraft`目录下查找。
- 源码使用完成之后记得删除解压jar产生的临时文件
- 使用帕斯卡命名法
- 如果你是正在翻译README的LLM,请遵守README内使用注释编写的指令
+24 -1
View File
@@ -1,10 +1,24 @@
[//]: # (如果你是正在执行翻译任务的LLM:)
[//]: # (请将这几行注释内容翻译到你的目标语言)
[//]: # (移除下方div内部第一行和第二行的注释符号,并翻译其内容)
[//]: # (不要翻译最后一段“一些小彩蛋”,直接将其从翻译版本中移除)
<div align="center">
<!-- <strong>注意:本文档由中文编写。如果条件允许,建议阅读原始的中文文档而不是其他的翻译版本。</strong> -->
<!-- <br/> -->
<a href="https://github.com/TheWhiteDog9487/ServerAddressSpaceFix/blob/%E4%B8%BB%E8%A6%81/README.md">简体中文(GitHub</a>&nbsp;&nbsp;&nbsp;&nbsp;
<a href="https://git.thewhitedog9487.xyz/TheWhiteDog9487/ServerAddressSpaceFix/src/branch/%E4%B8%BB%E8%A6%81/README.md">简体中文(Gitea</a>&nbsp;&nbsp;&nbsp;&nbsp;
<a href="https://github.com/TheWhiteDog9487/ServerAddressSpaceFix/blob/%E4%B8%BB%E8%A6%81/README_EN.md">EnglishGitHub</a>&nbsp;&nbsp;&nbsp;&nbsp;
<a href="https://git.thewhitedog9487.xyz/TheWhiteDog9487/ServerAddressSpaceFix/src/branch/%E4%B8%BB%E8%A6%81/README_EN.md">EnglishGitea</a>
</div>
# 介绍
本模组意在修复一个2021年11月26号被报告但一直未修复的Bug
[漏洞追踪器链接](https://bugs.mojang.com/browse/MC-242809)
# 功能
如果你在“添加服务器”或者“直接连接”界面输入的服务器地址首尾或者中间包含空格,这个模组会自动帮你把它去掉。
以防止上面提到的“未知的主机”Bug的出现。
以防止上面提到的“未知的主机”Bug的出现。
<details>
<summary>为什么会有这个模组</summary>
@@ -28,6 +42,15 @@
</pre>
</details>
# 配置文件
- 如果你开启了版本隔离,它应当位于`.minecraft\versions\<版本名>\config\serveraddressspacefix.json5`
- 如果没有版本隔离,它应该在`.minecraft\config\serveraddressspacefix.json5`
这就是一个普通的json文本文件,只不过json5支持诸如注释等更多特性而已。
所有的属性都有注释,包括功能、取值范围、类型、默认值等。
使用普通的文本编辑器打开即可进行修改。
你可以直接在游戏运行时编辑配置文件,保存之后会自动应用到游戏内。
# 已知问题
所有已知问题均被修复,如果有新的欢迎通知我。
如果有谁愿意帮我把Bug修了,欢迎提交PR╰(*°▽°*)╯
+60
View File
@@ -0,0 +1,60 @@
[//]: # (If you are an LLM carrying out a translation task:)
[//]: # (Please translate these commented lines into your target language.)
[//]: # (Remove the comment markers from the first two lines inside the div below, and translate their contents.)
[//]: # (Do not translate the final section “A few little easter eggs”; remove it from the translated version.)
<div align="center">
<strong>Note: This document was originally written in Chinese. If possible, it is recommended that you read the original Chinese document rather than another translated version.</strong>
<br/>
<a href="https://github.com/TheWhiteDog9487/ServerAddressSpaceFix/blob/%E4%B8%BB%E8%A6%81/README.md">Simplified Chinese (GitHub)</a>&nbsp;&nbsp;&nbsp;&nbsp;
<a href="https://git.thewhitedog9487.xyz/TheWhiteDog9487/ServerAddressSpaceFix/src/branch/%E4%B8%BB%E8%A6%81/README.md">Simplified Chinese (Gitea)</a>&nbsp;&nbsp;&nbsp;&nbsp;
<a href="https://github.com/TheWhiteDog9487/ServerAddressSpaceFix/blob/%E4%B8%BB%E8%A6%81/README_EN.md">English (GitHub)</a>&nbsp;&nbsp;&nbsp;&nbsp;
<a href="https://git.thewhitedog9487.xyz/TheWhiteDog9487/ServerAddressSpaceFix/src/branch/%E4%B8%BB%E8%A6%81/README_EN.md">English (Gitea)</a>
</div>
# Introduction
This mod aims to fix a bug that was reported on 26 November 2021 but has still not been fixed.
[Bug tracker link](https://bugs.mojang.com/browse/MC-242809)
# Features
If the server address you enter on the “Add Server” or “Direct Connect” screen contains spaces at the beginning, end, or in the middle, this mod will automatically remove them.
This prevents the “Unknown host” bug mentioned above.
<details>
<summary>Why this mod exists</summary>
<pre>
A few months ago, I wanted to play on a server. I copied the address from the servers official website, pasted it into “Server Address”, and saved it — only to get “Unknown host”.
No panic; this clearly meant DNS had not resolved the corresponding address, so I pinged it first.
Ping could resolve the domains address, and then I suspected the Great Firewall might be doing something odd again, so I tested it with the tool on Chinaz, and every result matched mine.
That was rather odd.
After fiddling with it for a while, I found the problem.
<strong>There was a space at the end of the server address I had copied.</strong>
That was really infuriating. After messing around for a bit longer and confirming that the issue could be reproduced reliably, I submitted a bug to the tracker.
And what do you think happened?
<img src="https://github.com/TheWhiteDog9487/ServerAddressSpaceFix/blob/%E4%B8%BB%E8%A6%81/%E5%9B%BE%E7%89%87/Snipaste_2023-10-18_15-22-39.png?raw=true" alt="The issue I filed on the bug tracker" srcset="">
<img src="https://github.com/TheWhiteDog9487/ServerAddressSpaceFix/blob/%E4%B8%BB%E8%A6%81/%E5%9B%BE%E7%89%87/Snipaste_2023-10-18_15-22-47.png?raw=true" alt="The comment below" srcset="">
<img src="https://github.com/TheWhiteDog9487/ServerAddressSpaceFix/blob/%E4%B8%BB%E8%A6%81/%E5%9B%BE%E7%89%87/Snipaste_2023-10-18_15-22-57.png?raw=true" alt="An even earlier bug report" srcset="">
My English is absolutely terrible, so I did not keep going; the problem description was written using translation tools.
But deep down, I still thought this was rather absurd.
Since your address is a String, how hard can it be to call trim afterwards?
And as a player, when you tell me, “This was all your own carelessness; it does not even count as a bug,” I cannot accept that.
<strong>So, this mod came to be.</strong>
</pre>
</details>
# Configuration
- If you have version isolation enabled, it should be located at `.minecraft\versions\<version name>\config\serveraddressspacefix.json5`
- If you do not have version isolation enabled, it should be at `.minecraft\config\serveraddressspacefix.json5`
This is just a regular JSON text file, except that JSON5 supports extra features such as comments.
All properties are annotated, including their purpose, range of values, type, default value, and so on.
You can open it in a plain text editor and modify it.
You can edit the config file while the game is running; once saved, the changes will be applied in-game automatically.
# Known issues
All known issues have been fixed; if you find any new ones, please let me know.
If anyone is willing to help me fix the bug, please submit a PR ╰(*°▽°*)╯
# Licence
I use the more radical WTFPL rather than MIT.
+48 -40
View File
@@ -1,93 +1,101 @@
plugins {
id("fabric-loom") version "1.8-SNAPSHOT"
id("maven-publish")
id("net.fabricmc.fabric-loom")
`maven-publish`
}
version = project.extra["mod_version"] as String
group = project.extra["maven_group"] as String
base {
archivesName.set(project.extra["archives_base_name"] as String)
}
version = providers.gradleProperty("mod_version").get()
group = providers.gradleProperty("maven_group").get()
repositories {
// Add repositories to retrieve artifacts from in here.
// You should only use this when depending on other mods because
// Loom adds the essential maven repositories to download Minecraft and libraries from automatically.
// See https://docs.gradle.org/current/userguide/declaring_repositories.html
// for more information about repositories.
maven {
name = "Xander Maven"
url = uri("https://maven.isxander.dev/releases")}
url = uri("https://maven.isxander.dev/releases") }
maven {
name = "Terraformers"
url = uri("https://maven.terraformersmc.com/releases")}
}
url = uri("https://maven.terraformersmc.com/releases") }
maven {
name = "Nucleoid"
url = uri("https://maven.nucleoid.xyz/") } }
loom {
splitEnvironmentSourceSets()
mods {
create("twd-sasf") {
sourceSet(sourceSets["main"])
sourceSet(sourceSets["client"])
register("serveraddressspacefix") {
sourceSet(sourceSets.main.get())
sourceSet(sourceSets.getByName("client"))
}
}
accessWidenerPath = file("src/main/resources/serveraddressspacefix.classtweaker")
}
dependencies {
// To change the versions see the gradle.properties file
minecraft("com.mojang:minecraft:${project.extra["minecraft_version"]}")
mappings("net.fabricmc:yarn:${project.extra["yarn_mappings"]}:v2")
modImplementation("net.fabricmc:fabric-loader:${project.extra["loader_version"]}")
minecraft("com.mojang:minecraft:${providers.gradleProperty("minecraft_version").get()}")
implementation("net.fabricmc:fabric-loader:${providers.gradleProperty("loader_version").get()}")
// Fabric API. This is technically optional, but you probably want it anyway.
modImplementation("net.fabricmc.fabric-api:fabric-api:${project.extra["fabric_version"]}")
implementation("net.fabricmc.fabric-api:fabric-api:${providers.gradleProperty("fabric_api_version").get()}")
// Uncomment the following line to enable the deprecated Fabric API modules.
// These are included in the Fabric API production distribution and allow you to update your mod to the latest modules at a later more convenient time.
// "modImplementation"("net.fabricmc.fabric-api:fabric-api-deprecated:${project.extra["fabric_version"]}")
modImplementation("com.terraformersmc:modmenu:${project.extra["modmenu_version"]}")
modImplementation("dev.isxander:yet-another-config-lib:${project.extra["yacl_version"]}")
implementation("com.terraformersmc:modmenu:${providers.gradleProperty("modmenu_version").get()}")
implementation("dev.isxander:yet-another-config-lib:${providers.gradleProperty("yacl_version").get()}")
}
tasks.processResources {
inputs.property("version", project.version)
val version = version
inputs.property("version", version)
filesMatching("fabric.mod.json") {
expand("version" to project.version)
expand("version" to version)
}
}
tasks.withType<JavaCompile> {
options.release.set(21)
tasks.withType<JavaCompile>().configureEach {
options.release = 25
}
java {
// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
// if it is present.
// If you remove this line, sources will not be generated.
withSourcesJar()
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
sourceCompatibility = JavaVersion.VERSION_25
targetCompatibility = JavaVersion.VERSION_25
}
tasks.jar {
val projectName = project.name
inputs.property("projectName", projectName)
from("LICENSE") {
rename { "${it}_${project.base.archivesName.get()}" }
rename { "${it}_$projectName" }
}
archiveFileName = "${providers.gradleProperty("archives_base_name").get()}-${project.version} mc${providers.gradleProperty("compatible_with").get()}.jar"
}
tasks.remapJar{
// https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html#org.gradle.api.tasks.bundling.Jar:archiveFileName
// 用这个属性设置jar包的文件名格式
// 别用上面那个Jar任务的配置,会被remapJar覆盖掉
archiveFileName = "${project.base.archivesName.get()}-${project.version} mc${project.extra["compatible_with"]}.jar"}
tasks.remapSourcesJar{
archiveFileName = "${project.base.archivesName.get()}-${project.version} mc${project.extra["compatible_with"]}-sources.jar"}
tasks.named<Jar>("sourcesJar") {
archiveFileName = "${providers.gradleProperty("archives_base_name").get()}-${project.version} mc${providers.gradleProperty("compatible_with").get()}-sources.jar"}
// configure the maven publication
publishing {
publications {
create<MavenPublication>("mavenJava") {
register<MavenPublication>("mavenJava") {
from(components["java"])
}
}
// See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing.
repositories {
// Add repositories to publish to here.
// Notice: This block does NOT have the same function as the block in the top level.
// The repositories here will be used for publishing your artifact, not for retrieving dependencies.
// The repositories here will be used for publishing your artifact, not for
// retrieving dependencies.
}
}
+12 -13
View File
@@ -4,28 +4,27 @@ org.gradle.parallel=true
# Fabric Properties
# check these on https://fabricmc.net/develop
minecraft_version=1.21.3
yarn_mappings=1.21.3+build.2
loader_version=0.16.7
minecraft_version=26.1
loader_version=0.18.4
loom_version=1.16-SNAPSHOT
# Mod Properties
mod_version=0.2.1
mod_version=0.2.3
maven_group=xyz.thewhitedog9487
archives_base_name=ServerAddressSpaceFix
# Dependencies
fabric_version=0.106.1+1.21.3
fabric_api_version=0.144.0+26.1
compatible_with=26.x
compatible_with=1.21+1.21.x
# https://modrinth.com/mod/modmenu/versions
# https://maven.terraformersmc.com/releases/com/terraformersmc/modmenu
modmenu_version=12.0.0-beta.1
modmenu_version=18.0.0-alpha.7
# https://modrinth.com/mod/yacl/versions
# https://maven.isxander.dev/#/releases/dev/isxander/yet-another-config-lib
yacl_version=3.6.1+1.21.2-fabric
yacl_version=3.9.0+26.1-fabric
# loom_libraries_base=https://bmclapi2.bangbang93.com/maven/
# loom_resources_base=https://bmclapi2.bangbang93.com/assets/
# loom_version_manifests=https://bmclapi2.bangbang93.com/mc/game/version_manifest.json
# loom_experimental_versions=https://maven.fabricmc.net/net/minecraft/experimental_versions.json
# loom_fabric_repository=https://repository.hanbings.io/proxy/
# https://mvnrepository.com/artifact/org.projectlombok/lombok
lombok_version=1.18.46
Binary file not shown.
+3 -1
View File
@@ -1,7 +1,9 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-bin.zip
networkTimeout=10000
retries=0
retryBackOffMs=500
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored
+13 -14
View File
@@ -1,7 +1,7 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
# Copyright © 2015 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.
@@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
@@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/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/.
@@ -84,7 +86,7 @@ done
# 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
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -112,7 +114,6 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
@@ -145,7 +146,7 @@ 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=SC3045
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
@@ -153,7 +154,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@@ -170,7 +171,6 @@ fi
# 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" )
@@ -202,16 +202,15 @@ 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, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
# Collect all arguments for the java command:
# * DEFAULT_JVM_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 \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.
Vendored
+22 -32
View File
@@ -13,6 +13,8 @@
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@@ -21,8 +23,8 @@
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Set local scope for the variables, and ensure extensions are enabled
setlocal EnableExtensions
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@@ -43,13 +45,13 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
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
"%COMSPEC%" /c exit 1
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
@@ -57,36 +59,24 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
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
"%COMSPEC%" /c exit 1
: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 %*
@rem endlocal doesn't take effect until after the line is parsed and variables are expanded
@rem which allows us to clear the local environment before executing the java command
endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel
: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
:exitWithErrorLevel
@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
"%COMSPEC%" /c exit %ERRORLEVEL%
+6 -3
View File
@@ -4,6 +4,9 @@ pluginManagement {
name = "Fabric"
url = uri("https://maven.fabricmc.net/")}
mavenCentral()
gradlePluginPortal()
}
}
gradlePluginPortal() }
plugins {
id("net.fabricmc.fabric-loom") version providers.gradleProperty("loom_version") } }
// Should match your modid
rootProject.name = "serveraddressspacefix"
@@ -0,0 +1,18 @@
package xyz.thewhitedog9487.Event;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
import xyz.thewhitedog9487.Settings;
import static xyz.thewhitedog9487.ServerAddressSpaceFixClient.ModLogger;
import static xyz.thewhitedog9487.Settings.SettingsInstance;
public class ClientLifecycleEventsRegister {
public static void Register(){
ClientLifecycleEvents.CLIENT_STARTED.register(ClientInstance -> {
ModLogger.info("正在从" + Settings.ConfigFilePath + "加载设置...");
Settings.SettingsHandler.load();
SettingsInstance = Settings.SettingsHandler.instance();
Settings.RegisterConfigFileReloadWatcher(); } );
ClientLifecycleEvents.CLIENT_STOPPING.register(ClientInstance -> {
ModLogger.info("正在保存设置到" + Settings.ConfigFilePath + "...");
Settings.SettingsHandler.save(); } ); } }
@@ -7,20 +7,25 @@ import dev.isxander.yacl3.api.Option;
import dev.isxander.yacl3.api.OptionDescription;
import dev.isxander.yacl3.api.YetAnotherConfigLib;
import dev.isxander.yacl3.api.controller.TickBoxControllerBuilder;
import net.minecraft.text.Text;
import net.minecraft.network.chat.Component;
import static xyz.thewhitedog9487.Settings.SettingsInstance;
public class ModMenu implements ModMenuApi {
@Override
public ConfigScreenFactory<?> getModConfigScreenFactory() {
return parent -> YetAnotherConfigLib.createBuilder()
.title(Text.translatable("title.twd-sasf.config"))
.title(Component.translatable("title.serveraddressspacefix.config"))
.category(ConfigCategory.createBuilder()
.name(Text.translatable("config.twd-sasf.category.general"))
.name(Component.translatable("config.serveraddressspacefix.category.general"))
.option(Option.<Boolean>createBuilder()
.name(Text.translatable("option.twd-sasf.ModEnabled"))
.binding(true, () -> Settings.ModEnabled, newVal -> Settings.ModEnabled = newVal)
.description(OptionDescription.of(Text.translatable("option.twd-sasf.ModEnabled.description")))
.name(Component.translatable("option.serveraddressspacefix.ModEnabled"))
.binding(true, () -> SettingsInstance.ModEnabled, newVal -> {
SettingsInstance.ModEnabled = newVal;
Settings.SettingsHandler.save(); } )
.description(OptionDescription.of(Component.translatable("option.serveraddressspacefix.ModEnabled.description")))
.controller(TickBoxControllerBuilder::create)
.build())
.build())
.build().generateScreen(parent);}}
.build()
.generateScreen(parent); } }
@@ -3,12 +3,15 @@ package xyz.thewhitedog9487;
import net.fabricmc.api.ClientModInitializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import xyz.thewhitedog9487.Event.ClientLifecycleEventsRegister;
public class ServerAddressSpaceFixClient implements ClientModInitializer {
public static final Logger LOGGER = LoggerFactory.getLogger("twd-sasf");
public static final String ModID = "serveraddressspacefix";
public static final Logger ModLogger = LoggerFactory.getLogger(ModID);
@Override
public void onInitializeClient() {
// This entrypoint is suitable for setting up client-specific logic, such as rendering.
LOGGER.info("ServerAddressSpaceFix Loading!");
ClientLifecycleEventsRegister.Register();
}
}
@@ -1,5 +1,77 @@
package xyz.thewhitedog9487;
import com.google.gson.GsonBuilder;
import dev.isxander.yacl3.config.v2.api.ConfigClassHandler;
import dev.isxander.yacl3.config.v2.api.SerialEntry;
import dev.isxander.yacl3.config.v2.api.serializer.GsonConfigSerializerBuilder;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.resources.Identifier;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
import static xyz.thewhitedog9487.ServerAddressSpaceFixClient.ModID;
import static xyz.thewhitedog9487.ServerAddressSpaceFixClient.ModLogger;
public class Settings {
public static boolean ModEnabled = true;
public static Path ConfigFilePath = FabricLoader.getInstance()
.getConfigDir()
.resolve(ModID + ".json5")
.toAbsolutePath();
public static ConfigClassHandler<Settings> SettingsHandler = ConfigClassHandler.createBuilder(Settings.class)
.id(Identifier.fromNamespaceAndPath(ModID, "settings_confighandler"))
.serializer(SettingsConfigClassHandler -> GsonConfigSerializerBuilder.create(SettingsConfigClassHandler)
.setPath(ConfigFilePath)
.appendGsonBuilder(GsonBuilder::setPrettyPrinting)
.setJson5(true)
.build() )
.build();
public static volatile Settings SettingsInstance;
// region 配置文件变更监听器
public static void RegisterConfigFileReloadWatcher(){
// 由GPT 5.4 Mini生成
Thread.startVirtualThread(new Thread(() -> {
Path ConfigDirectory = ConfigFilePath.getParent();
try (WatchService WatchService = FileSystems.getDefault().newWatchService()) {
ConfigDirectory.register(WatchService, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);
while (!Thread.currentThread().isInterrupted()) {
WatchKey WatchKey = WatchService.take();
boolean ShouldReload = false;
for (WatchEvent<?> WatchEvent : WatchKey.pollEvents()) {
WatchEvent.Kind<?> EventKind = WatchEvent.kind();
if (EventKind == StandardWatchEventKinds.OVERFLOW) {
continue; }
Path ChangedPath = (Path) WatchEvent.context();
if (ChangedPath != null && ChangedPath.equals(ConfigFilePath.getFileName())) {
ShouldReload = true; } }
if (ShouldReload) {
SettingsHandler.load();
SettingsInstance = SettingsHandler.instance();
ModLogger.info("检测到配置文件变更,已重新加载设置。");}
if (!WatchKey.reset()) {
break;}}
} catch (InterruptedException InterruptedException) {
Thread.currentThread().interrupt();
} catch (IOException e) {
throw new RuntimeException(e); } } ) ); }
// endregion
@SerialEntry(comment =
"""
是否启用本模组的功能
类型:布尔值
取值范围:true false
默认值:true
""")
public boolean ModEnabled = true;
}
@@ -0,0 +1,41 @@
package xyz.thewhitedog9487.mixin.client;
import net.minecraft.client.gui.components.EditBox;
import net.minecraft.client.gui.screens.DirectJoinServerScreen;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import static xyz.thewhitedog9487.ServerAddressSpaceFixClient.ModLogger;
import static xyz.thewhitedog9487.Settings.SettingsInstance;
@Mixin(DirectJoinServerScreen.class)
public class DirectJoinServerScreenMixin {
@Shadow private EditBox ipEdit;
@Inject(method = "init", at = @At("TAIL"))
private void onInit(CallbackInfo ci) {
RemoveSpaceCharacter(); }
@Inject(method = "onSelect", at = @At("HEAD"))
private void onSelect(CallbackInfo ci) {
RemoveSpaceCharacter(); }
@Inject(method = "removed", at = @At("HEAD"))
private void onRemoved(CallbackInfo ci) {
RemoveSpaceCharacter(); }
@Unique
private void RemoveSpaceCharacter() {
var RawValue = ipEdit.getValue();
if (SettingsInstance.ModEnabled == false ||
RawValue.contains(" ") == false) { return; }
var NewValue = RawValue.replace(" ", "");
this.ipEdit.setValue(NewValue);
ModLogger.info("已完成移除空格替换!" + "\n" +
"原始值:" + RawValue + "\n" +
"新值:" + NewValue); } }
@@ -0,0 +1,31 @@
package xyz.thewhitedog9487.mixin.client;
import net.minecraft.client.gui.components.EditBox;
import net.minecraft.client.gui.screens.ManageServerScreen;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import static xyz.thewhitedog9487.ServerAddressSpaceFixClient.ModLogger;
import static xyz.thewhitedog9487.Settings.SettingsInstance;
@Mixin(ManageServerScreen.class)
public class ManageServerScreenMixin {
@Redirect(
method = "onAdd",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/client/gui/components/EditBox;getValue()Ljava/lang/String;",
ordinal = 1 ) )
private String RedirectIpEditGetValue(EditBox instance) {
var RawValue = instance.getValue();
if ( SettingsInstance.ModEnabled == false ||
RawValue.contains(" ") == false) {
return RawValue; }
else {
var NewValue = RawValue.replace(" ", "");
ModLogger.info("已完成移除空格替换!" + "\n" +
"原始值:" + RawValue + "\n" +
"新值:" + NewValue);
return NewValue; } } }
@@ -1,15 +0,0 @@
package xyz.thewhitedog9487.mixin.client;
import net.minecraft.client.gui.screen.multiplayer.AddServerScreen;
import net.minecraft.client.gui.widget.TextFieldWidget;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import xyz.thewhitedog9487.Settings;
@Mixin(AddServerScreen.class)
public class MixinAddServerScreen {
@Redirect(method = "addAndClose", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/widget/TextFieldWidget;getText()Ljava/lang/String;", ordinal = 1))
private String trimGetText(TextFieldWidget instance) {
if( Settings.ModEnabled == false ) { return instance.getText(); }
else return instance.getText().replace(" ", "");}}
@@ -1,23 +0,0 @@
package xyz.thewhitedog9487.mixin.client;
import net.minecraft.client.gui.screen.multiplayer.DirectConnectScreen;
import net.minecraft.client.gui.widget.TextFieldWidget;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import xyz.thewhitedog9487.Settings;
@Mixin(DirectConnectScreen.class)
interface DirectConnectScreenAccessor {
@Accessor("addressField")
TextFieldWidget Mixin_GetAddressField();}
@Mixin(DirectConnectScreen.class)
public class MixinDirectConnectScreen {
@Redirect(method = "saveAndClose", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/widget/TextFieldWidget;getText()Ljava/lang/String;"))
private String trimGetText(TextFieldWidget instance) {
if( Settings.ModEnabled == false ) { return instance.getText(); }
String trimmedText = instance.getText().replace(" ", "");
((DirectConnectScreenAccessor) this).Mixin_GetAddressField().setText(trimmedText);
return trimmedText;}}
@@ -1,19 +0,0 @@
package xyz.thewhitedog9487.mixin.client;
import net.minecraft.client.network.ServerInfo;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import xyz.thewhitedog9487.Settings;
import java.nio.file.Paths;
@Mixin(ServerInfo.class)
public class MixinServerInfo {
@Redirect(method = "<init>", at = @At(value = "FIELD", opcode = Opcodes.PUTFIELD, target = "Lnet/minecraft/client/network/ServerInfo;address:Ljava/lang/String;"))
private void RemoveSpace_Write(ServerInfo original, String value) {
if( Settings.ModEnabled == false ) {
original.address = value;
return; }
else { original.address = value.replace(" ", ""); } }}
@@ -0,0 +1,30 @@
package xyz.thewhitedog9487.mixin.client;
import net.minecraft.client.multiplayer.ServerData;
import org.jetbrains.annotations.Nullable;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import static xyz.thewhitedog9487.ServerAddressSpaceFixClient.ModLogger;
import static xyz.thewhitedog9487.Settings.SettingsInstance;
@Mixin(ServerData.class)
public class ServerDataMixin {
/**
* 用于处理服务器列表内已经存在的服务器项的IP存在空格的问题
*/
@Redirect(method = "<init>", at = @At(value = "FIELD", opcode = Opcodes.PUTFIELD, target = "Lnet/minecraft/client/multiplayer/ServerData;ip:Ljava/lang/String;"))
private void RemoveSpace_Write(ServerData original, String value) {
@Nullable String RawValue = value;
if (SettingsInstance.ModEnabled == false ||
RawValue.contains(" ") == false) {
original.ip = value;
return; }
else {
var NewValue = RawValue.replace(" ", "");
original.ip = NewValue;
ModLogger.info("已完成移除空格替换!" + "\n" +
"原始值:" + RawValue + "\n" +
"新值:" + NewValue); } } }
@@ -0,0 +1,15 @@
{
"required": true,
"package": "xyz.thewhitedog9487.mixin.client",
"compatibilityLevel": "JAVA_25",
"client": [
"DirectJoinServerScreenMixin",
"ManageServerScreenMixin",
"ServerDataMixin"
],
"injectors": {
"defaultRequire": 1
},
"mixins": [
]
}
@@ -1,14 +0,0 @@
{
"required": true,
"package": "xyz.thewhitedog9487.mixin.client",
"compatibilityLevel": "JAVA_21",
"client": [
"DirectConnectScreenAccessor",
"MixinAddServerScreen",
"MixinDirectConnectScreen",
"MixinServerInfo"
],
"injectors": {
"defaultRequire": 1
}
}
@@ -2,15 +2,16 @@ package xyz.thewhitedog9487;
import net.fabricmc.api.ModInitializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 所有的代码都在client源集内
* @deprecated 目前没有任何用途
* @see ServerAddressSpaceFixClient
*/
@Deprecated
public class ServerAddressSpaceFix implements ModInitializer {
// This logger is used to write text to the console and the log file.
// It is considered best practice to use your mod id as the logger's name.
// That way, it's clear which mod wrote info, warnings, and errors.
public static final Logger LOGGER = LoggerFactory.getLogger("twd-sasf");
@Override
public void onInitialize() {
@@ -18,6 +19,5 @@ public class ServerAddressSpaceFix implements ModInitializer {
// However, some things (like resources) may still be uninitialized.
// Proceed with mild caution.
LOGGER.info("Hello Fabric world!");
}
}

Before

Width:  |  Height:  |  Size: 453 B

After

Width:  |  Height:  |  Size: 453 B

@@ -0,0 +1,10 @@
{
"modmenu.nameTranslation.serveraddressspacefix": "ServerAddressSpaceFix",
"modmenu.descriptionTranslation.serveraddressspacefix": "Fixed the \"unknown host\" bug caused by the server address containing spaces at the beginning and end of the server address when adding a server or connecting directly\nTip: If you need to use the configuration menu, install the YetAnotherConfigLib mod",
"serveraddressspacefix.bilibili": "Bilibili",
"serveraddressspacefix.blog": "Blog",
"title.serveraddressspacefix.config": "ServerAddressSpaceFix",
"config.serveraddressspacefix.category.general": "General",
"option.serveraddressspacefix.ModEnabled": "Whether To Enable The Mod Feature",
"option.serveraddressspacefix.ModEnabled.description": "There is no need to restart the game, and the modification will take effect immediately after it is saved"
}
@@ -0,0 +1,10 @@
{
"modmenu.nameTranslation.serveraddressspacefix": "服务器地址首尾空格修复",
"modmenu.descriptionTranslation.serveraddressspacefix": "修复添加服务器或直接连接时,服务器地址首尾包含空格导致的“未知的主机”Bug\n提示:如果需要使用配置菜单,请安装YetAnotherConfigLib模组",
"serveraddressspacefix.bilibili": "哔哩哔哩主页",
"serveraddressspacefix.blog": "TheWhiteDog9487的博客",
"title.serveraddressspacefix.config": "服务器地址首尾空格修复",
"config.serveraddressspacefix.category.general": "通用",
"option.serveraddressspacefix.ModEnabled": "是否启用模组功能",
"option.serveraddressspacefix.ModEnabled.description": "无需重启游戏,修改并保存后立刻生效"
}
@@ -1,10 +0,0 @@
{
"modmenu.nameTranslation.twd-sasf": "ServerAddressSpaceFix",
"modmenu.descriptionTranslation.twd-sasf": "Fixed the \"unknown host\" bug caused by the server address containing spaces at the beginning and end of the server address when adding a server or connecting directly\nTip: If you need to use the configuration menu, install the YetAnotherConfigLib mod",
"twd-sasf.bilibili": "Bilibili",
"twd-sasf.blog": "Blog",
"title.twd-sasf.config": "ServerAddressSpaceFix",
"config.twd-sasf.category.general": "General",
"option.twd-sasf.ModEnabled": "Whether To Enable The Mod Feature",
"option.twd-sasf.ModEnabled.description": "There is no need to restart the game, and the modification will take effect immediately after it is saved"
}
@@ -1,10 +0,0 @@
{
"modmenu.nameTranslation.twd-sasf": "服务器地址首尾空格修复",
"modmenu.descriptionTranslation.twd-sasf": "修复添加服务器或直接连接时,服务器地址首尾包含空格导致的“未知的主机”Bug\n提示:如果需要使用配置菜单,请安装YetAnotherConfigLib模组",
"twd-sasf.bilibili": "哔哩哔哩主页",
"twd-sasf.blog": "TheWhiteDog9487的博客",
"title.twd-sasf.config": "服务器地址首尾空格修复",
"config.twd-sasf.category.general": "通用",
"option.twd-sasf.ModEnabled": "是否启用模组功能",
"option.twd-sasf.ModEnabled.description": "无需重启游戏,修改并保存后立刻生效"
}
+17 -11
View File
@@ -1,6 +1,6 @@
{
"schemaVersion": 1,
"id": "twd-sasf",
"id": "serveraddressspacefix",
"version": "${version}",
"name": "ServerAddressSpaceFix",
"description": "服务器地址首尾空格修复",
@@ -13,31 +13,37 @@
"issues": "https://github.com/TheWhiteDog9487/ServerAddressSpaceFix/issues"
},
"license": "WTFPL",
"icon": "assets/twd-sasf/icon.png",
"icon": "assets/serveraddressspacefix/icon.png",
"environment": "client",
"entrypoints": {
"client": [ "xyz.thewhitedog9487.ServerAddressSpaceFixClient" ],
"modmenu": [ "xyz.thewhitedog9487.ModMenu" ]
},
"mixins": [
"twd-sasf.mixins.json",
"serveraddressspacefix.mixins.json",
{
"config": "twd-sasf.client.mixins.json",
"config": "serveraddressspacefix.client.mixins.json",
"environment": "client"
}
],
"depends": {
"fabricloader": ">=0.15.11",
"minecraft": ["1.21","1.21.*"],
"java": ">=21"
"fabricloader": ">=0.18.4",
"fabric-api": ">=0.144.0",
"minecraft": "26.*",
"java": ">=25"
},
"suggests": {
"another-mod": "*"
"modmenu": "*",
"yet_another_config_lib_v3": "*"
},
"custom": {
"modmenu": {
"links": {
"twd-sasf.bilibili": "https://space.bilibili.com/401746666",
"twd-sasf.blog": "www.thewhitedog9487.xyz"},
"update_checker": true}}
"serveraddressspacefix.bilibili": "https://space.bilibili.com/401746666",
"serveraddressspacefix.blog": "https://www.thewhitedog9487.xyz"},
"update_checker": true } },
"contributors": [
"JustAlittleWolf"
],
"accessWidener": "serveraddressspacefix.classtweaker"
}
@@ -0,0 +1,2 @@
classTweaker v1 official
accessible field net/minecraft/client/gui/screens/multiplayer/JoinMultiplayerScreen editingServer Lnet/minecraft/client/multiplayer/ServerData;
@@ -1,7 +1,7 @@
{
"required": true,
"package": "xyz.thewhitedog9487.mixin",
"compatibilityLevel": "JAVA_21",
"compatibilityLevel": "JAVA_25",
"mixins": [],
"injectors": {
"defaultRequire": 1
+24
View File
@@ -1,3 +1,27 @@
# 0.2.3
时间:2026 06 04
1. 使用YACL Config API自动将功能开关状态保存到文件内
2. 解决直接连接窗口内输入框里的服务器IP没有被移除空格处理的问题
3. 在移除空格之后打印日志
4. 更改ModID
# 0.2.2
时间:2026 03 26
1. 新到Minecraft 26.1
2. 由于源码变更,更换了一个新的实现方案,稍微测试了一下没太大毛病,应该还行
# 0.2.1
时间:2024 10 30
主要内容:
加大剂量,以前不会对服务器列表中已经保存的服务器地址进行处理,现在只要模组处于启用状态会直接对所有地址进行消除空格替换,确保不留死角。
说人话:未安装或未启用本模组时添加的服务器地址如果有空格,在启用本模组之后会在打开“多人游戏”窗口时被处理掉。
# 0.2.0
时间:2024 06 14
主要内容:
1. 更新适配的游戏版本到1.21/1.21.x
2. 翻译键移除thewhitedog9487的命名空间
# 0.1.4
时间:2024 01 15
主要内容: