Skip to content

Commit 7a74427

Browse files
authored
Merge pull request #1 from BIGGASSS/config-support
Add support for in-game-command configuration
2 parents c6acba9 + ec89df7 commit 7a74427

7 files changed

Lines changed: 523 additions & 13 deletions

File tree

AGENTS.md

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
# AGENTS.md - NeteaseMusicDisplay
2+
3+
A Minecraft Fabric mod that displays the current playing song from Netease Music on Windows.
4+
5+
## Tech Stack
6+
7+
- **Build System**: Gradle with Fabric Loom
8+
- **Primary Language**: Kotlin (2.3.0)
9+
- **Secondary Language**: Java (for Mixin classes)
10+
- **Java Version**: 21
11+
- **Minecraft Version**: 1.21.10
12+
- **Mod Loader**: Fabric
13+
14+
## Build Commands
15+
16+
```bash
17+
# Build the mod JAR
18+
./gradlew build
19+
20+
# Run all tests
21+
./gradlew test
22+
23+
# Run a specific test class
24+
./gradlew test --tests "com.as9929.display.netease.YourTestClass"
25+
26+
# Run a specific test method
27+
./gradlew test --tests "com.as9929.display.netease.YourTestClass.testMethod"
28+
29+
# Run all checks (includes tests, ABI checks)
30+
./gradlew check
31+
32+
# Clean build directory
33+
./gradlew clean
34+
35+
# Full clean build
36+
./gradlew clean build
37+
38+
# Generate sources JAR
39+
./gradlew sourcesJar
40+
```
41+
42+
## Project Structure
43+
44+
```
45+
src/
46+
├── main/
47+
│ ├── java/com/as9929/display/netease/mixin/ # Java Mixin classes
48+
│ ├── kotlin/com/as9929/display/netease/ # Kotlin source
49+
│ └── resources/ # Mod metadata, assets
50+
│ ├── fabric.mod.json
51+
│ └── netease-music-display.mixins.json
52+
├── test/ # Test sources (if any)
53+
build.gradle # Build configuration
54+
gradle.properties # Version constants
55+
settings.gradle # Project settings
56+
```
57+
58+
## Code Style Guidelines
59+
60+
### Kotlin
61+
62+
- **Indentation**: 4 spaces (no tabs)
63+
- **Line endings**: LF
64+
- **Package**: `com.as9929.display.netease`
65+
- Use `object` for singletons (e.g., `object TextRender`)
66+
- Use `by lazy` for expensive initialization
67+
- Prefer `val` over `var`
68+
- Use nullable types (`String?`) over exceptions for optional returns
69+
- Comments: Use `// --- Description ---` for section headers
70+
71+
Example:
72+
```kotlin
73+
package com.as9929.display.netease
74+
75+
import net.fabricmc.api.ModInitializer
76+
77+
object NeteaseMusicDisplay : ModInitializer {
78+
const val MOD_ID = "netease-music-display"
79+
80+
override fun onInitialize() {
81+
// Initialization code
82+
}
83+
}
84+
```
85+
86+
### Java
87+
88+
- **Indentation**: 4 spaces
89+
- **Package**: `com.as9929.display.netease.mixin` for Mixin classes
90+
- Use Mixin pattern for Minecraft injection points
91+
- Annotations on separate lines for complex injections
92+
93+
Example:
94+
```java
95+
package com.as9929.display.netease.mixin;
96+
97+
import org.spongepowered.asm.mixin.Mixin;
98+
99+
@Mixin(InGameHud.class)
100+
public class InGameHudMixin {
101+
@Inject(
102+
method = "render",
103+
at = @At(value = "INVOKE", target = "...")
104+
)
105+
private void onRender(...) { }
106+
}
107+
```
108+
109+
### Naming Conventions
110+
111+
- **Classes**: PascalCase (e.g., `CloudMusicHelper`, `TextRender`)
112+
- **Objects**: PascalCase (e.g., `TextRender.INSTANCE`)
113+
- **Methods/Functions**: camelCase (e.g., `getCloudMusicTitle()`)
114+
- **Constants**: UPPER_SNAKE_CASE (e.g., `MOD_ID`, `PROCESS_QUERY_INFORMATION`)
115+
- **Packages**: lowercase (e.g., `com.as9929.display.netease`)
116+
117+
### Imports
118+
119+
- Group imports by source:
120+
1. Kotlin/Java standard library
121+
2. Minecraft/Fabric libraries
122+
3. Project imports
123+
4. Third-party libraries (JNA, etc.)
124+
- Use wildcard imports sparingly
125+
126+
### Error Handling
127+
128+
- Use nullable return types instead of throwing exceptions for expected failures
129+
- Wrap platform-specific code (Windows API) with OS checks first
130+
- Use try-finally for resource cleanup (handles, processes)
131+
- Log errors using SLF4J: `LoggerFactory.getLogger(MOD_ID)`
132+
133+
### Threading
134+
135+
- Offload heavy work to background threads using `Executors`
136+
- Mark shared mutable state with `@Volatile`
137+
- Use daemon threads: `t.isDaemon = true`
138+
- Always access Minecraft client on main render thread only
139+
140+
### Mixin Guidelines
141+
142+
- Place all Mixin classes in `mixin` package
143+
- Use `@Inject` for adding behavior
144+
- Use proper `@At` targets (e.g., `INVOKE`, `HEAD`, `TAIL`)
145+
- Include shift when needed: `shift = At.Shift.BEFORE`
146+
147+
## Key Dependencies
148+
149+
- Fabric Loader: `0.18.4`
150+
- Fabric API: `0.138.4+1.21.10`
151+
- Fabric Language Kotlin: `1.13.8+kotlin.2.3.0`
152+
- JNA: For Windows API access (cloudmusic.exe integration)
153+
154+
## Testing
155+
156+
Currently no tests exist. When adding tests:
157+
1. Place in `src/test/kotlin/` mirroring main package structure
158+
2. Use JUnit 5 (included via Gradle)
159+
3. Run with `./gradlew test`
160+
161+
## CI/CD
162+
163+
GitHub Actions workflow at `.github/workflows/build.yml`
164+
165+
## Resources
166+
167+
- Fabric Wiki: https://fabricmc.net/wiki
168+
- Mixin Wiki: https://github.com/SpongePowered/Mixin/wiki

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
11
# NeteaseMusicDisplay
22

33
A Minecraft Fabric mod that displays the current playing song in Netease Music on Windows.
4+
5+
## Available Commands
6+
7+
- `/nmd color <hex|name>` - Set color (e.g., `#FF5733`, `red`, `yellow`)
8+
- `/nmd pos <x> <y>` - Set position (use `-1` for X to auto right-align)
9+
- `/nmd width <50-500>` - Set max box width
10+
- `/nmd scale <0.5-3.0>` - Set text scale
11+
- `/nmd toggle` - Enable/disable display
12+
- `/nmd reset` - Reset to defaults
13+
- `/nmd status` - Show current settings

build.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ plugins {
22
id 'net.fabricmc.fabric-loom-remap' version "${loom_version}"
33
id 'maven-publish'
44
id "org.jetbrains.kotlin.jvm" version "2.3.0"
5+
id "org.jetbrains.kotlin.plugin.serialization" version "2.3.0"
56
}
67

78
version = project.mod_version
@@ -33,6 +34,9 @@ dependencies {
3334
// Fabric API. This is technically optional, but you probably want it anyway.
3435
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_api_version}"
3536
modImplementation "net.fabricmc:fabric-language-kotlin:${project.fabric_kotlin_version}"
37+
38+
// Kotlinx Serialization for JSON config
39+
modImplementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3"
3640
}
3741

3842
processResources {
Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.as9929.display.netease
22

3+
import com.as9929.display.netease.command.ConfigCommand
4+
import com.as9929.display.netease.config.ConfigManager
35
import net.fabricmc.api.ModInitializer
46
import org.slf4j.LoggerFactory
57

@@ -8,9 +10,14 @@ object NeteaseMusicDisplay : ModInitializer {
810
private val logger = LoggerFactory.getLogger(MOD_ID)
911

1012
override fun onInitialize() {
11-
// This code runs as soon as Minecraft is in a mod-load-ready state.
12-
// However, some things (like resources) may still be uninitialized.
13-
// Proceed with mild caution.
13+
// Load configuration
14+
ConfigManager.loadConfig()
15+
logger.info("$MOD_ID config loaded.")
16+
17+
// Register commands
18+
ConfigCommand.register()
19+
logger.info("$MOD_ID commands registered.")
20+
1421
logger.info("$MOD_ID loaded.")
1522
}
1623
}

src/main/kotlin/com/as9929/display/netease/TextRender.kt

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
11
package com.as9929.display.netease
22

3+
import com.as9929.display.netease.config.ConfigManager
34
import net.minecraft.client.MinecraftClient
45
import net.minecraft.client.gui.DrawContext
56
import net.minecraft.text.Text
6-
import java.awt.Color
77
import java.util.concurrent.Executors
88
import java.util.concurrent.TimeUnit
99

1010
object TextRender {
11-
private var scale = 1.0f
12-
private val color = Color(255, 255, 0, 255).rgb // White
13-
1411
@Volatile
1512
private var cachedTitle: String? = null
1613

@@ -25,7 +22,7 @@ object TextRender {
2522
// Schedule the query to run every 1 second (initial delay 0)
2623
executor.scheduleAtFixedRate({
2724
try {
28-
// This heavy work now happens off the main thread
25+
// This heavy work happens off the main thread
2926
val musicTitle = CloudMusicHelper.getCloudMusicTitle()
3027
cachedTitle = musicTitle
3128
} catch (e: Exception) {
@@ -35,25 +32,38 @@ object TextRender {
3532
}
3633

3734
fun onRender(context: DrawContext) {
35+
val config = ConfigManager.config
36+
37+
// 0. Check if enabled
38+
if (!config.enabled) return
39+
3840
// 1. Instant check: If title is null (e.g. not Windows or not playing), stop rendering.
39-
// We read the variable directly; no heavy calculation here.
4041
val titleToRender = cachedTitle ?: return
4142

4243
val client = MinecraftClient.getInstance()
4344
val textRenderer = client.textRenderer
4445

45-
// 2. Define your text
46+
// 2. Define the text
4647
val text = Text.literal("Playing: $titleToRender")
4748

4849
// 3. Calculate Dimensions (Standard Rendering Logic)
50+
val scale = config.scale
4951
val screenWidth = client.window.scaledWidth / scale
50-
val maxBoxWidth = 200
52+
val maxBoxWidth = config.maxBoxWidth
5153
val padding = 10
5254
val fullTextWidth = textRenderer.getWidth(text)
5355
val currentBoxWidth = fullTextWidth.coerceAtMost(maxBoxWidth)
5456

55-
val x = (screenWidth - currentBoxWidth - padding).toInt()
56-
val y = 3
57+
// Calculate X position: use config if not -1, otherwise auto right-align
58+
val x = if (config.x == -1) {
59+
(screenWidth - currentBoxWidth - padding).toInt()
60+
} else {
61+
config.x
62+
}
63+
val y = config.y
64+
65+
// Get color from config
66+
val color = ConfigManager.getColorInt()
5767

5868
// 4. Render
5969
context.matrices.pushMatrix()

0 commit comments

Comments
 (0)