Skip to content

Commit 903982f

Browse files
committed
refactor: replace HTTP metrics with UDP, improve network architecture
- Replace HttpMetricsClient (TCP) with UdpMetricsClient (UDP) for metrics transmission - Add HttpServerManager for /api/deviceinfo HTTP endpoint (TCP) - Rename networkUrl to networkHost, remove http:// prefix requirement - Update ConfigAccess with new validation and defaults - Add network config options to PtrackerCommand - Update ClothConfigScreen with new network settings - Add test_udp_server.py for UDP testing - Update localization files (en_us, zh_cn, zh_tw, lzh) - Update README with new Network API documentation - Remove obsolete HttpService, UrlUtils, and related tests
1 parent 4d3a1e0 commit 903982f

20 files changed

Lines changed: 785 additions & 1178 deletions

File tree

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,8 @@ replay_*.log
4444
logs/
4545
*.log
4646
*.log.gz
47+
48+
# Python
49+
50+
*.pyc
51+
__pycache__/

README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ A Minecraft Fabric mod for tracking game performance metrics.
99

1010
- **In-game Performance Display**: Shows real-time FPS, TPS, MSPT, heap memory, and CPU usage.
1111
- **Metrics Export**: Exports performance data to CSV, JSON, or YAML formats.
12-
- **HTTP API**: Exposes metrics via a local HTTP server for integration with monitoring tools.
12+
- **Network API**: Exposes device info via HTTP (TCP) and sends metrics via UDP for integration with monitoring tools.
1313
- **Configurable Collection**: Toggle which metrics to collect and export.
1414

1515
## Commands
@@ -37,22 +37,23 @@ Contributions are welcome. Please feel free to submit issues or pull requests on
3737

3838
1. **Local Data Export**: When enabled, performance metrics are written to files in a configurable directory.
3939

40-
2. **HTTP Reporting**: When the network feature is enabled, this mod sends performance metrics to a remote HTTP endpoint. The transmitted data includes:
40+
2. **Network Transmission**: When the network feature is enabled, this mod transmits performance metrics via UDP and exposes system information via HTTP:
4141
- Session identifier
4242
- Timestamp
4343
- All enabled metrics (FPS, TPS, heap, CPU, etc.)
44-
- Complete system information as listed above
44+
- Complete system information
4545

46-
3. **No Authentication**: The HTTP endpoint has no built-in authentication or encryption. Any party with network access to the endpoint can receive and store your data.
46+
3. **No Authentication**: The HTTP endpoint and UDP receiver have no built-in authentication or encryption. Any party with network access can receive and store your data.
4747

4848
4. **Data Retention**: Any remote server you send data to will retain that data according to their own policies. This mod has no control over how your data is stored or used by third parties.
4949

5050
**Recommendations:**
51-
- Only enable the HTTP reporting feature if you trust the endpoint
52-
- Use localhost (`127.0.0.1`) or a trusted local server as the endpoint when testing
51+
- Only enable the network feature if you trust the endpoint
52+
- Use localhost (`127.0.0.1`) or a trusted local server when testing
5353
- Be aware of what data is being collected and transmitted
5454
- Review the data before sharing any exported files
5555
- Consider the implications of exposing system information to network endpoints
56+
- Firewall rules should be configured appropriately for your use case
5657

5758
## License
5859

src/client/java/org/damon233/performtrackermod/config/ClothConfigScreen.java

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -128,34 +128,67 @@ public static Screen createConfigScreen(Screen parent) {
128128
.setTooltip(Text.translatable("performtracker.config.collect_cpu.tooltip"))
129129
.setSaveConsumer(ConfigAccess::setCollectCpu)
130130
.build());
131-
131+
132132
ConfigCategory network = builder.getOrCreateCategory(Text.translatable("performtracker.config.category.network"));
133-
133+
134+
network.addEntry(entryBuilder.startTextDescription(
135+
Text.translatable("performtracker.config.network_warning")
136+
)
137+
.build());
138+
134139
network.addEntry(entryBuilder.startBooleanToggle(
135-
Text.translatable("performtracker.config.network_enabled"),
136-
ConfigAccess.isNetworkEnabled())
140+
Text.translatable("performtracker.config.network_enabled"),
141+
ConfigAccess.isNetworkEnabled())
137142
.setDefaultValue(false)
138143
.setTooltip(Text.translatable("performtracker.config.network_enabled.tooltip"))
139144
.setSaveConsumer(ConfigAccess::setNetworkEnabled)
140145
.build());
141-
146+
142147
network.addEntry(entryBuilder.startTextField(
143-
Text.translatable("performtracker.config.network_endpoint"),
144-
ConfigAccess.getNetworkEndpoint())
145-
.setDefaultValue("http://localhost:31415")
146-
.setTooltip(Text.translatable("performtracker.config.network_endpoint.tooltip"))
148+
Text.translatable("performtracker.config.network_host"),
149+
ConfigAccess.getNetworkUrl())
150+
.setDefaultValue("localhost")
151+
.setTooltip(Text.translatable("performtracker.config.network_host.tooltip"))
147152
.setSaveConsumer(newValue -> {
148-
if (ConfigAccess.isValidNetworkEndpoint(newValue)) {
149-
ConfigAccess.setNetworkEndpoint(newValue);
153+
if (ConfigAccess.isValidNetworkUrl(newValue)) {
154+
ConfigAccess.setNetworkUrl(newValue);
150155
}
151156
})
152157
.setErrorSupplier(newValue -> {
153-
if (!ConfigAccess.isValidNetworkEndpoint(newValue)) {
154-
return Optional.of(Text.translatable("performtracker.config.network_endpoint.error"));
158+
if (!ConfigAccess.isValidNetworkUrl(newValue)) {
159+
return Optional.of(Text.translatable("performtracker.config.network_host.error"));
155160
}
156161
return Optional.empty();
157162
})
158163
.build());
164+
165+
network.addEntry(entryBuilder.startIntField(
166+
Text.translatable("performtracker.config.receiver_port"),
167+
ConfigAccess.getReceiverPort())
168+
.setDefaultValue(31415)
169+
.setMin(1)
170+
.setMax(65535)
171+
.setTooltip(Text.translatable("performtracker.config.receiver_port.tooltip"))
172+
.setSaveConsumer(port -> {
173+
if (port >= 1 && port <= 65535) {
174+
ConfigAccess.setReceiverPort(port);
175+
}
176+
})
177+
.build());
178+
179+
network.addEntry(entryBuilder.startIntField(
180+
Text.translatable("performtracker.config.sender_port"),
181+
ConfigAccess.getSenderPort())
182+
.setDefaultValue(31416)
183+
.setMin(1)
184+
.setMax(65535)
185+
.setTooltip(Text.translatable("performtracker.config.sender_port.tooltip"))
186+
.setSaveConsumer(port -> {
187+
if (port >= 1 && port <= 65535) {
188+
ConfigAccess.setSenderPort(port);
189+
}
190+
})
191+
.build());
159192

160193
return builder.build();
161194
}

src/main/java/org/damon233/performtrackermod/PerformTracker.java

Lines changed: 12 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -27,78 +27,44 @@
2727
import org.damon233.performtrackermod.config.ConfigAccess;
2828
import org.damon233.performtrackermod.controller.TrackerController;
2929
import org.damon233.performtrackermod.command.PtrackerCommand;
30-
import org.damon233.performtrackermod.network.HttpService;
30+
import org.damon233.performtrackermod.network.HttpServerManager;
31+
import org.damon233.performtrackermod.network.UdpMetricsClient;
3132

3233
public class PerformTracker implements ModInitializer {
3334
public static final String MOD_ID = "performtracker";
3435
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
3536

3637
private static TrackerController trackerController;
37-
private static HttpService httpService;
3838
private static IFpsProvider fpsProvider;
3939
private static IGpuProvider gpuProvider;
4040

4141
@Override
4242
public void onInitialize() {
4343
ConfigAccess.init();
44-
44+
4545
LOGGER.info("PerformTracker initialized.");
46-
ServerMetricsCollector serverMetricsCollector = new ServerMetricsCollector();
46+
ServerMetricsCollector serverMetricsCollector = new ServerMetricsCollector();
4747
trackerController = new TrackerController(serverMetricsCollector, null);
4848
PtrackerCommand.register();
49-
49+
5050
if (ConfigAccess.isNetworkEnabled()) {
51-
httpService = new HttpService();
52-
httpService.initialize(ConfigAccess.getNetworkUrl());
53-
httpService.tryStartServer();
51+
String host = ConfigAccess.getNetworkUrl();
52+
int port = ConfigAccess.getReceiverPort();
53+
HttpServerManager.getInstance().start(host, port);
5454
}
55-
56-
// Register callback for network enable/disable changes
57-
ConfigAccess.setOnNetworkEnabledChanged(() -> {
58-
if (ConfigAccess.isNetworkEnabled()) {
59-
// Network was enabled - create and start HttpService if not exists
60-
if (httpService == null) {
61-
httpService = new HttpService();
62-
httpService.initialize(ConfigAccess.getNetworkUrl());
63-
}
64-
httpService.tryStartServer();
65-
} else {
66-
// Network was disabled - stop server and clear HttpService
67-
if (httpService != null) {
68-
httpService.stop();
69-
httpService.stopServer();
70-
httpService = null;
71-
LOGGER.info("HttpService unloaded due to network being disabled.");
72-
}
73-
}
74-
});
75-
76-
// Register callback for network endpoint changes
77-
ConfigAccess.setOnNetworkEndpointChanged(() -> {
78-
if (httpService != null && ConfigAccess.isNetworkEnabled()) {
79-
httpService.reinitialize(ConfigAccess.getNetworkUrl());
80-
LOGGER.info("HttpService reinitialized with new endpoint: {}", ConfigAccess.getNetworkUrl());
81-
}
82-
});
83-
55+
8456
ServerLifecycleEvents.SERVER_STOPPING.register(server -> {
85-
if (httpService != null) {
86-
httpService.stopServer();
87-
LOGGER.info("HttpService server stopped.");
88-
}
57+
HttpServerManager.getInstance().stop();
58+
UdpMetricsClient.getInstance().close();
8959
});
90-
60+
9161
LOGGER.info("PerformTracker server components ready.");
9262
}
9363

9464
public static TrackerController getTrackerController() {
9565
return trackerController;
9666
}
9767

98-
public static HttpService getHttpService() {
99-
return httpService;
100-
}
101-
10268
public static void setFpsProvider(Object fpsProvider) {
10369
IFpsProvider provider = (IFpsProvider) fpsProvider;
10470
PerformTracker.fpsProvider = provider;

src/main/java/org/damon233/performtrackermod/command/PtrackerCommand.java

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -57,18 +57,8 @@ private enum ConfigType {
5757
ConfigAccess::getExportDirectory,
5858
v -> ConfigAccess.setExportDirectory((String) v),
5959
ConfigAccess::getDefaultExportDirectory),
60-
61-
BOOL3("network_enabled", "performtracker.config.network_enabled", false,
62-
ConfigAccess::isNetworkEnabled,
63-
v -> ConfigAccess.setNetworkEnabled((Boolean) v),
64-
ConfigAccess::getDefaultNetworkEnabled),
65-
66-
STRING2("network_endpoint", "performtracker.config.network_endpoint", "http://localhost:31415",
67-
ConfigAccess::getNetworkEndpoint,
68-
v -> ConfigAccess.setNetworkEndpoint((String) v),
69-
ConfigAccess::getDefaultNetworkEndpoint),
7060

71-
STRING3("output_format", "performtracker.config.output_format", "csv",
61+
STRING2("output_format", "performtracker.config.output_format", "csv",
7262
ConfigAccess::getOutputFormat,
7363
v -> ConfigAccess.setOutputFormat((String) v),
7464
ConfigAccess::getDefaultOutputFormat),
@@ -101,7 +91,27 @@ private enum ConfigType {
10191
BOOL10("binary_units", "performtracker.config.binary_units", true,
10292
ConfigAccess::isBinaryUnits,
10393
v -> ConfigAccess.setBinaryUnits((Boolean) v),
104-
ConfigAccess::getDefaultBinaryUnits);
94+
ConfigAccess::getDefaultBinaryUnits),
95+
96+
BOOL11("network_enabled", "performtracker.config.network_enabled", false,
97+
ConfigAccess::isNetworkEnabled,
98+
v -> ConfigAccess.setNetworkEnabled((Boolean) v),
99+
ConfigAccess::getDefaultNetworkEnabled),
100+
101+
STRING4("network_host", "performtracker.config.network_host", "localhost",
102+
ConfigAccess::getNetworkUrl,
103+
v -> ConfigAccess.setNetworkUrl((String) v),
104+
ConfigAccess::getDefaultNetworkUrl),
105+
106+
INT2("receiver_port", "performtracker.config.receiver_port", 31415,
107+
ConfigAccess::getReceiverPort,
108+
v -> ConfigAccess.setReceiverPort((Integer) v),
109+
ConfigAccess::getDefaultReceiverPort),
110+
111+
INT3("sender_port", "performtracker.config.sender_port", 31416,
112+
ConfigAccess::getSenderPort,
113+
v -> ConfigAccess.setSenderPort((Integer) v),
114+
ConfigAccess::getDefaultSenderPort);
105115

106116
final String key;
107117
final String translationKey;
@@ -137,10 +147,8 @@ void sendInfo(ServerCommandSource source) {
137147
}
138148

139149
void sendLine(ServerCommandSource source) {
140-
MutableText current = FormattingService.colorValue(
141-
Boolean.toString(Boolean.TRUE.equals(getter.get())));
142-
MutableText def = FormattingService.colorValue(
143-
Boolean.toString(Boolean.TRUE.equals(defaultGetter.get())));
150+
MutableText current = FormattingService.colorValue(String.valueOf(getter.get()));
151+
MutableText def = FormattingService.colorValue(String.valueOf(defaultGetter.get()));
144152
source.sendFeedback(() -> Text.translatable(translationKey).append(": ").append(current).append(" (default: ").append(def).append(")"), false);
145153
}
146154
}
@@ -209,11 +217,7 @@ public static void register() {
209217
item.then(CommandManager.argument("value", StringArgumentType.string())
210218
.executes(ctx -> {
211219
String val = StringArgumentType.getString(ctx, "value");
212-
if (cfg == ConfigType.STRING2 && !ConfigAccess.isValidNetworkEndpoint(val)) {
213-
ctx.getSource().sendFeedback(() -> Text.translatable("performtracker.config.network_endpoint.error"), false);
214-
return 0;
215-
}
216-
if (cfg == ConfigType.STRING3 && !ConfigAccess.isValidOutputFormat(val)) {
220+
if (cfg == ConfigType.STRING2 && !ConfigAccess.isValidOutputFormat(val)) {
217221
ctx.getSource().sendFeedback(() -> Text.translatable("performtracker.config.output_format.error"), false);
218222
return 0;
219223
}

0 commit comments

Comments
 (0)