From 20171830ea1f76a2270d9a918a4fe9b3f3c29871 Mon Sep 17 00:00:00 2001 From: h7x4 Date: Fri, 12 Jun 2026 02:22:26 +0900 Subject: [PATCH] minecraft: query and parse player positions --- src/main.c | 37 ++++++++++++----- src/minecraft.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++ src/minecraft.h | 15 +++++++ src/rcon.c | 6 +++ 4 files changed, 152 insertions(+), 9 deletions(-) create mode 100644 src/minecraft.c create mode 100644 src/minecraft.h diff --git a/src/main.c b/src/main.c index 7a1557a..8aa7072 100644 --- a/src/main.c +++ b/src/main.c @@ -1,8 +1,13 @@ +/* + * main.c - Main entry point for the wamf executable. + */ + #include #include #include #include "rcon.h" +#include "minecraft.h" const char *LOCALHOST = "127.0.0.1"; @@ -42,17 +47,31 @@ int main(void) { exit(EXIT_FAILURE); } - const char *get_player_positions_cmd = "execute as @a run data get entity @s Pos"; - printf("\nexecuting command: %s\n", get_player_positions_cmd); - char *response = rcon_command_multipacket(sockfd, get_player_positions_cmd); - - if (response) { - printf("\n=== player positions ===\n"); - printf("%s\n", response); - } else { - fprintf(stderr, "failed to get player positions\n"); + player_entity_t* entities = malloc(sizeof(player_entity_t) * 20); + if (!entities) { + perror("malloc failed"); + close(sockfd); + exit(EXIT_FAILURE); } + if (query_player_entities(sockfd, 20, entities) < 0) { + fprintf(stderr, "failed to query player entities\n"); + free(entities); + close(sockfd); + exit(EXIT_FAILURE); + } + + printf("\n=== player positions ===\n"); + for (size_t i = 0; i < 20 && entities[i].name != NULL; i++) { + printf("%s: (%.2f, %.2f, %.2f)\n", entities[i].name, entities[i].x, entities[i].y, entities[i].z); + } + + for (size_t i = 0; i < 20; i++) { + free_player_entity(&entities[i]); + } + + free(entities); + close(sockfd); return 0; } diff --git a/src/minecraft.c b/src/minecraft.c new file mode 100644 index 0000000..3cbd5dc --- /dev/null +++ b/src/minecraft.c @@ -0,0 +1,103 @@ +/* + * minecraft.c - Minecraft specific RCON client implementation + * + * This file implements some Minecraft-specific RCON commands and utilities, + * such as parsing player position data. + */ + +#include +#include +#include + +#include "minecraft.h" +#include "rcon.h" + +void free_player_entity(player_entity_t *entity) { + if (entity->name) { + free(entity->name); + entity->name = NULL; + } +} + +// Parses lines of of the format: +// has the following entity data: [-1234.1234567891234d, 71.0d, +// 113.0000000000001d] +int parse_player_entity(const char *response, player_entity_t *entity) { + const char *prefix = " has the following entity data: ["; + char *prefix_pos = strstr(response, prefix); + if (!prefix_pos) { + fprintf(stderr, "invalid response format: missing prefix\n"); + return -1; + } + + size_t name_len = prefix_pos - response; + entity->name = malloc(name_len + 1); + if (!entity->name) { + perror("malloc failed"); + return -1; + } + strncpy(entity->name, response, name_len); + entity->name[name_len] = '\0'; + + const char *coords_start = prefix_pos + strlen(prefix); + const char *coords_end = strchr(coords_start, ']'); + if (!coords_end) { + fprintf(stderr, "invalid response format: missing closing bracket\n"); + free(entity->name); + return -1; + } + + char coords_str[256]; + size_t coords_len = coords_end - coords_start; + if (coords_len >= sizeof(coords_str)) { + fprintf(stderr, "coordinates string too long\n"); + free(entity->name); + return -1; + } + strncpy(coords_str, coords_start, coords_len); + coords_str[coords_len] = '\0'; + + if (sscanf(coords_str, "%lfd, %lfd, %lfd", &entity->x, &entity->y, + &entity->z) != 3) { + fprintf(stderr, "invalid coordinates format\n"); + free(entity->name); + return -1; + } + + return 0; +} + +int query_player_entities(int sockfd, size_t max_entities, + player_entity_t *entities) { + char *response = rcon_command_multipacket( + sockfd, "execute as @a run data get entity @s Pos"); + if (!response) { + fprintf(stderr, "failed to execute command\n"); + return -1; + } + + size_t entity_count = 0; + for (size_t i = 0; i < max_entities; i++) { + char *line = strtok(response, "\n"); + if (!line) + break; + printf("parsing line: '%s'\n", line); + + if (parse_player_entity(line, &entities[entity_count]) == 0) { + entity_count++; + } else { + fprintf(stderr, "failed to parse player entity from line: %s\n", line); + } + }; + + if (entity_count == max_entities) { + fprintf( + stderr, + "warning: more player entities in response than provided space for\n"); + } + + // WARNING: response is not malloced :skull: - don't free it! + // free(response); + + return (int)entity_count; +} diff --git a/src/minecraft.h b/src/minecraft.h new file mode 100644 index 0000000..a790f30 --- /dev/null +++ b/src/minecraft.h @@ -0,0 +1,15 @@ +#include + +typedef struct { + char *name; + double x; + double y; + double z; +} player_entity_t; + +void free_player_entity(player_entity_t *entity); + +int parse_player_entity(const char *response, player_entity_t *entity); + +int query_player_entities(int sockfd, size_t entities_size, + player_entity_t *entities); diff --git a/src/rcon.c b/src/rcon.c index bcbf64e..28ce64a 100644 --- a/src/rcon.c +++ b/src/rcon.c @@ -1,3 +1,9 @@ +/* + * rcon.c - RCON client implementation + * + * This file implements some basic functions for communicating with arbitrary RCON servers. + */ + #include #include #include