| /* Minecraft Interprocess Management Client |
| * Copyright 2015-2020 Rivoreo |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License as published by the |
| * Free Software Foundation, either version 3 of the License, or (at your |
| * option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| * more details. |
| */ |
| |
| #include "packet.h" |
| #include <unistd.h> |
| #include "syncrw.h" |
| #include <string.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <errno.h> |
| |
| int send_packet_get_version(int fd) { |
| uint32_t length = 1; |
| struct mcipm_packet *packet = malloc(4 + length); |
| if(!packet) { |
| fputs("send_packet_get_version: out of memory\n", stderr); |
| return -1; |
| } |
| packet->length = length; |
| packet->type = MCIPM_GET_VERSION; |
| if(sync_write(fd, packet, 4 + length) < 0) { |
| perror("send_packet_get_version: write"); |
| return -1; |
| } |
| return 0; |
| } |
| |
| int send_packet_get_entities(int fd, const void *selectors, size_t selectors_len, unsigned int selector_count) { |
| uint32_t length = 1 + 2 + selectors_len; |
| struct mcipm_packet *packet = malloc(4 + length); |
| if(!packet) { |
| fputs("send_packet_get_entities: out of memory\n", stderr); |
| return -1; |
| } |
| packet->length = length; |
| packet->type = MCIPM_GET_ENTITIES; |
| *(uint16_t *)packet->data = selector_count; |
| memcpy(packet->data + 2, selectors, selectors_len); |
| if(sync_write(fd, packet, 4 + length) < 0) { |
| perror("send_packet_get_entities: write"); |
| return -1; |
| } |
| return 0; |
| } |
| |
| int send_packet_kill_entity(int fd, const char *world_id_name, long int entity_id) { |
| size_t world_id_name_len = strlen(world_id_name); |
| if(world_id_name_len > 255) { |
| fprintf(stderr, "send_packet_kill_entity: world_id_name too long (%zu>255)\n", world_id_name_len); |
| return -1; |
| } |
| uint32_t length = 1 + 1 + world_id_name_len + 1 + 4; |
| struct mcipm_packet *packet = malloc(4 + length); |
| if(!packet) { |
| fputs("send_packet_kill_entity: out of memory\n", stderr); |
| return -1; |
| } |
| packet->length = length; |
| packet->type = MCIPM_KILL_ENTITY; |
| uint8_t *p = packet->data; |
| *p++ = world_id_name_len; |
| memcpy(p, world_id_name, world_id_name_len); |
| p += world_id_name_len; |
| *p++ = MCIPM_KILL_ENTITY_BY_ID; |
| *(uint32_t *)p = entity_id; |
| if(sync_write(fd, packet, 4 + length) < 0) { |
| perror("send_packet_kill_entity: write"); |
| return -1; |
| |
| } |
| return 0; |
| } |
| |
| int receive_packet(int fd, struct mcipm_packet **packet, int blocking_read) { |
| uint32_t length; |
| int s; |
| if(blocking_read) s = sync_read(fd, &length, sizeof length); |
| else do { |
| s = read(fd, &length, sizeof length); |
| } while(s < 0 && errno == EINTR); |
| if(s < 0) return GET_PACKET_ERROR; |
| if(!s) return GET_PACKET_EOF; |
| if(s < sizeof length) return blocking_read ? GET_PACKET_EOF : GET_PACKET_SHORT_READ; |
| if(length < 1) return GET_PACKET_TOO_SMALL; |
| if(length > MCIPM_PACKET_MAX_LENGTH) return GET_PACKET_TOO_LARGE; |
| *packet = malloc(sizeof length + length); |
| if(!*packet) return GET_PACKET_OUT_OF_MEMORY; |
| (*packet)->length = length; |
| if(blocking_read) s = sync_read(fd, (char *)*packet + sizeof length, length); |
| else do { |
| s = read(fd, (char *)*packet + sizeof length, length); |
| } while(s < 0 && errno == EINTR); |
| int r = 0; |
| //syslog(LOG_DEBUG, "*length = %u, s = %d", (unsigned int)*length, s); |
| if(s < 0) r = GET_PACKET_ERROR; |
| else if(!s) r = GET_PACKET_EOF; |
| else if(s < length) r = blocking_read ? GET_PACKET_EOF : GET_PACKET_SHORT_READ; |
| if(r) free(*packet); |
| return r; |
| } |
| |