33import com .sun .net .httpserver .HttpExchange ;
44import com .sun .net .httpserver .HttpServer ;
55import net .fabricmc .api .ClientModInitializer ;
6+ import net .fabricmc .fabric .api .client .event .lifecycle .v1 .ClientLifecycleEvents ;
67import net .fabricmc .fabric .api .client .event .lifecycle .v1 .ClientTickEvents ;
8+ import net .fabricmc .fabric .api .client .event .lifecycle .v1 .ClientWorldEvents ;
9+ import net .fabricmc .fabric .api .client .networking .v1 .ClientPlayConnectionEvents ;
710import net .minecraft .client .MinecraftClient ;
811import net .minecraft .client .world .ClientWorld ;
912import net .minecraft .entity .player .PlayerEntity ;
1922public class PlayerCoordsAPIClient implements ClientModInitializer {
2023 private HttpServer server ;
2124 private boolean serverStarted = false ;
25+ private volatile PlayerSnapshot latestSnapshot ;
2226 // Hardcoded port value - no longer in config
2327 private static final int PORT = 25565 ;
2428
@@ -31,6 +35,7 @@ public void onInitializeClient() {
3135
3236 // Register tick event to constantly check config status
3337 ClientTickEvents .END_CLIENT_TICK .register (client -> {
38+ updateSnapshot (client );
3439 boolean configEnabled = PlayerCoordsAPI .getConfig ().enabled ;
3540
3641 // If enabled and server not started, start server
@@ -44,9 +49,47 @@ public void onInitializeClient() {
4449 }
4550 });
4651
52+ ClientWorldEvents .AFTER_CLIENT_WORLD_CHANGE .register ((client , world ) -> updateSnapshot (client ));
53+ ClientPlayConnectionEvents .DISCONNECT .register ((handler , client ) -> clearSnapshot ());
54+ ClientLifecycleEvents .CLIENT_STOPPING .register (client -> {
55+ clearSnapshot ();
56+ stopServer ();
57+ });
58+
4759 PlayerCoordsAPI .LOGGER .info ("Registered config monitor" );
4860 }
4961
62+ private void updateSnapshot (MinecraftClient client ) {
63+ PlayerEntity player = client .player ;
64+ ClientWorld worldObj = client .world ;
65+
66+ if (player == null || worldObj == null ) {
67+ latestSnapshot = null ;
68+ return ;
69+ }
70+
71+ RegistryEntry <Biome > biomeEntry = worldObj .getBiome (player .getBlockPos ());
72+ String biome = biomeEntry .getKey ()
73+ .map (key -> key .getValue ().toString ())
74+ .orElse ("unknown" );
75+
76+ latestSnapshot = new PlayerSnapshot (
77+ player .getX (),
78+ player .getY (),
79+ player .getZ (),
80+ player .getYaw (),
81+ player .getPitch (),
82+ worldObj .getRegistryKey ().getValue ().toString (),
83+ biome ,
84+ player .getUuid ().toString (),
85+ player .getName ().getString ()
86+ );
87+ }
88+
89+ private void clearSnapshot () {
90+ latestSnapshot = null ;
91+ }
92+
5093 private void startServer () {
5194 if (serverStarted ) return ;
5295
@@ -96,36 +139,9 @@ private void handleCoordsRequest(HttpExchange exchange) throws IOException {
96139 return ;
97140 }
98141
99- // Get player coordinates
100- MinecraftClient client = MinecraftClient .getInstance ();
101- PlayerEntity player = client .player ;
102- ClientWorld worldObj = client .world ;
103-
104- String responseText ;
105- if (player != null && worldObj != null ) {
106- double x = player .getX ();
107- double y = player .getY ();
108- double z = player .getZ ();
109- String world = worldObj .getRegistryKey ().getValue ().toString ();
110-
111- // Get biome information
112- RegistryEntry <Biome > biomeEntry = worldObj .getBiome (player .getBlockPos ());
113- String biome = biomeEntry .getKey ().orElseThrow ().getValue ().toString ();
114-
115- // Get player UUID and username
116- String uuid = player .getUuid ().toString ();
117- String username = player .getName ().getString ();
118-
119- // Fetch pitch/yaw, note they are floats internally
120- float yaw = player .getYaw ();
121- float pitch = player .getPitch ();
122-
123- // Format as JSON using US locale to ensure dots instead of commas
124- responseText = String .format (Locale .US ,
125- "{\" x\" : %.2f, \" y\" : %.2f, \" z\" : %.2f, \" yaw\" : %.2f, \" pitch\" : %.2f, \" world\" : \" %s\" , \" biome\" : \" %s\" , \" uuid\" : \" %s\" , \" username\" : \" %s\" }" ,
126- x , y , z , yaw , pitch , world , biome , uuid , username
127- );
128- sendResponse (exchange , 200 , responseText );
142+ PlayerSnapshot snapshot = latestSnapshot ;
143+ if (snapshot != null ) {
144+ sendResponse (exchange , 200 , snapshot .toJson ());
129145 } else {
130146 sendResponse (exchange , 404 , "{\" error\" : \" Player not in world\" }" );
131147 }
@@ -148,4 +164,23 @@ private void sendResponse(HttpExchange exchange, int statusCode, String response
148164 exchange .sendResponseHeaders (statusCode , -1 ); // No response body
149165 }
150166 }
167+
168+ private record PlayerSnapshot (
169+ double x ,
170+ double y ,
171+ double z ,
172+ float yaw ,
173+ float pitch ,
174+ String world ,
175+ String biome ,
176+ String uuid ,
177+ String username
178+ ) {
179+ private String toJson () {
180+ return String .format (Locale .US ,
181+ "{\" x\" : %.2f, \" y\" : %.2f, \" z\" : %.2f, \" yaw\" : %.2f, \" pitch\" : %.2f, \" world\" : \" %s\" , \" biome\" : \" %s\" , \" uuid\" : \" %s\" , \" username\" : \" %s\" }" ,
182+ x , y , z , yaw , pitch , world , biome , uuid , username
183+ );
184+ }
185+ }
151186}
0 commit comments