minecraft: query and parse player positions
This commit is contained in:
+28
-9
@@ -1,8 +1,13 @@
|
||||
/*
|
||||
* main.c - Main entry point for the wamf executable.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
+103
@@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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:
|
||||
// <user> 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;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#include <stddef.h>
|
||||
|
||||
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);
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* rcon.c - RCON client implementation
|
||||
*
|
||||
* This file implements some basic functions for communicating with arbitrary RCON servers.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
Reference in New Issue
Block a user