diff --git a/include/globals.h b/include/globals.h index 06f8368..c5942b9 100644 --- a/include/globals.h +++ b/include/globals.h @@ -248,6 +248,19 @@ typedef struct { #pragma pack(pop) +union EntityDataValue { + uint8_t byte; + int pose; +}; + +typedef struct { + uint8_t index; + // 0 - Byte + // 21 - Pose + int type; + union EntityDataValue value; +} EntityData; + extern BlockChange block_changes[MAX_BLOCK_CHANGES]; extern int block_changes_count; diff --git a/include/packets.h b/include/packets.h index 4d3b36c..ae832ac 100644 --- a/include/packets.h +++ b/include/packets.h @@ -48,6 +48,7 @@ int sc_acknowledgeBlockChange (int client_fd, int sequence); int sc_playerInfoUpdateAddPlayer (int client_fd, PlayerData player); int sc_spawnEntity (int client_fd, int id, uint8_t *uuid, int type, double x, double y, double z, uint8_t yaw, uint8_t pitch); int sc_spawnEntityPlayer (int client_fd, PlayerData player); +int sc_setEntityMetadata (int client_fd, int id, EntityData *metadata, size_t length); int sc_entityAnimation (int client_fd, int id, uint8_t animation); int sc_teleportEntity (int client_fd, int id, double x, double y, double z, float yaw, float pitch); int sc_setHeadRotation (int client_fd, int id, uint8_t yaw); diff --git a/include/procedures.h b/include/procedures.h index 1dde793..62d09f0 100644 --- a/include/procedures.h +++ b/include/procedures.h @@ -20,6 +20,8 @@ void disconnectClient (int *client_fd, int cause); int givePlayerItem (PlayerData *player, uint16_t item, uint8_t count); void spawnPlayer (PlayerData *player); +void broadcastPlayerMetadata (PlayerData *player); + uint8_t serverSlotToClientSlot (int window_id, uint8_t slot); uint8_t clientSlotToServerSlot (int window_id, uint8_t slot); @@ -47,4 +49,9 @@ void handleServerTick (int64_t time_since_last_tick); void broadcastChestUpdate (int origin_fd, uint8_t *storage_ptr, uint16_t item, uint8_t count, uint8_t slot); +ssize_t writeEntityData (int client_fd, EntityData *data); + +int sizeEntityData (EntityData *data); +int sizeEntityMetadata (EntityData *metadata, size_t length); + #endif diff --git a/src/packets.c b/src/packets.c index 5cc1a51..8b67b5f 100644 --- a/src/packets.c +++ b/src/packets.c @@ -764,6 +764,10 @@ int cs_setPlayerMovementFlags (int client_fd, uint8_t *on_ground) { *on_ground = readByte(client_fd) & 0x01; + PlayerData *player; + if (!getPlayerData(client_fd, &player)) + broadcastPlayerMetadata(player); + return 0; } @@ -910,6 +914,26 @@ int sc_spawnEntity ( return 0; } +// S->C Set Entity Metadata +int sc_setEntityMetadata (int client_fd, int id, EntityData *metadata, size_t length) { + int entity_metadata_size = sizeEntityMetadata(metadata, length); + if (entity_metadata_size == -1) return 1; + + writeVarInt(client_fd, 2 + sizeVarInt(id) + entity_metadata_size); + writeByte(client_fd, 0x5C); + + writeVarInt(client_fd, id); // Entity ID + + for (size_t i = 0; i < length; i ++) { + EntityData *data = &metadata[i]; + writeEntityData(client_fd, data); + } + + writeByte(client_fd, 0xFF); // End + + return 0; +} + // S->C Spawn Entity (from PlayerData) int sc_spawnEntityPlayer (int client_fd, PlayerData player) { return sc_spawnEntity( @@ -1198,6 +1222,8 @@ int cs_playerInput (int client_fd) { if (flags & 0x20) player->flags |= 0x04; else player->flags &= ~0x04; + broadcastPlayerMetadata(player); + return 0; } @@ -1215,6 +1241,8 @@ int cs_playerCommand (int client_fd) { if (action == 1) player->flags |= 0x08; else if (action == 2) player->flags &= ~0x08; + broadcastPlayerMetadata(player); + return 0; } diff --git a/src/procedures.c b/src/procedures.c index 464e7a4..7ba75da 100644 --- a/src/procedures.c +++ b/src/procedures.c @@ -368,6 +368,43 @@ void spawnPlayer (PlayerData *player) { } +// Broadcasts a player's entity metadata (sneak/sprint state) to other players +void broadcastPlayerMetadata (PlayerData *player) { + uint8_t sneaking = (player->flags & 0x04) != 0; + uint8_t sprinting = (player->flags & 0x08) != 0; + + uint8_t player_bit_mask = 0; + if (sneaking) player_bit_mask |= 0x02; + if (sprinting) player_bit_mask |= 0x08; + + int pose = 0; + if (sneaking) pose = 5; + + EntityData metadata[] = { + { + 0, // Index (Bit Mask) + 0, // Type (Byte) + player_bit_mask, // Value + }, + { + 6, // Index (Pose), + 21, // Type (Pose), + pose, // Value (Standing) + } + }; + + for (int i = 0; i < MAX_PLAYERS; i ++) { + PlayerData* other_player = &player_data[i]; + int client_fd = other_player->client_fd; + + if (client_fd == -1) continue; + if (client_fd == player->client_fd) continue; + if (other_player->flags & 0x20) continue; + + sc_setEntityMetadata(client_fd, player->client_fd, metadata, 2); + } +} + uint8_t getBlockChange (short x, uint8_t y, short z) { for (int i = 0; i < block_changes_count; i ++) { if (block_changes[i].block == 0xFF) continue; @@ -1784,3 +1821,47 @@ void broadcastChestUpdate (int origin_fd, uint8_t *storage_ptr, uint16_t item, u } #endif + +ssize_t writeEntityData (int client_fd, EntityData *data) { + writeByte(client_fd, data->index); + writeVarInt(client_fd, data->type); + + switch (data->type) { + case 0: // Byte + return writeByte(client_fd, data->value.byte); + case 21: // Pose + writeVarInt(client_fd, data->value.pose); + return 0; + + default: return -1; + } +} + +// Returns the networked size of an EntityData entry +int sizeEntityData (EntityData *data) { + int value_size; + + switch (data->type) { + case 0: // Byte + value_size = 1; + break; + case 21: // Pose + value_size = sizeVarInt(data->value.pose); + break; + + default: return -1; + } + + return 1 + sizeVarInt(data->type) + value_size; +} + +// Returns the networked size of an array of EntityData entries +int sizeEntityMetadata (EntityData *metadata, size_t length) { + int total_size = 0; + for (size_t i = 0; i < length; i ++) { + int size = sizeEntityData(&metadata[i]); + if (size == -1) return -1; + total_size += size; + } + return total_size; +} diff --git a/src/tools.c b/src/tools.c index fabb6f0..0b7050f 100644 --- a/src/tools.c +++ b/src/tools.c @@ -120,7 +120,7 @@ ssize_t send_all (int client_fd, const void *buf, ssize_t len) { if (err == WSAEWOULDBLOCK || err == WSAEINTR) { #else if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) { - #endif + #endif // handle network timeout if (get_program_time() - last_update_time > NETWORK_TIMEOUT_TIME) { disconnectClient(&client_fd, -2);