From ba86dfd927b5e2432be797e12095642dc4091fe1 Mon Sep 17 00:00:00 2001 From: SDFTDusername <85052868+SDFTDusername@users.noreply.github.com> Date: Tue, 16 Sep 2025 12:36:42 +0200 Subject: [PATCH] implement sheep shearing * Added shearing sheeps * Added empty line * Changed comment in MobData to say what the middle 1 bit is used for * Replaced if statements with a switch statement and early returns * Send mob metadata to players that join * Fixed mob metadata getting freed when exiting the switch statement * remove currently unnecessary check * use bitwise and in place of modulo * style nitpicks --------- Co-authored-by: p2r3 --- include/globals.h | 2 +- include/procedures.h | 2 + src/main.c | 1 + src/packets.c | 4 +- src/procedures.c | 100 ++++++++++++++++++++++++++++++++++++++++--- 5 files changed, 102 insertions(+), 7 deletions(-) diff --git a/include/globals.h b/include/globals.h index c5942b9..4f07a3e 100644 --- a/include/globals.h +++ b/include/globals.h @@ -241,7 +241,7 @@ typedef struct { uint8_t y; short z; // Lower 5 bits: health - // Middle 1 bit: reserved for future use + // Middle 1 bit: sheep sheared, unused for other mobs // Upper 2 bits: panic timer uint8_t data; } MobData; diff --git a/include/procedures.h b/include/procedures.h index 62d09f0..f3aa331 100644 --- a/include/procedures.h +++ b/include/procedures.h @@ -21,6 +21,7 @@ int givePlayerItem (PlayerData *player, uint16_t item, uint8_t count); void spawnPlayer (PlayerData *player); void broadcastPlayerMetadata (PlayerData *player); +void broadcastMobMetadata (int client_fd, int entity_id); uint8_t serverSlotToClientSlot (int window_id, uint8_t slot); uint8_t clientSlotToServerSlot (int window_id, uint8_t slot); @@ -44,6 +45,7 @@ void handlePlayerUseItem (PlayerData *player, short x, short y, short z, uint8_t void checkFluidUpdate (short x, uint8_t y, short z, uint8_t block); void spawnMob (uint8_t type, short x, uint8_t y, short z, uint8_t health); +void interactEntity (int entity_id, int interactor_id); void hurtEntity (int entity_id, int attacker_id, uint8_t damage_type, uint8_t damage); void handleServerTick (int64_t time_since_last_tick); diff --git a/src/main.c b/src/main.c index dd065d9..1d8bddb 100644 --- a/src/main.c +++ b/src/main.c @@ -156,6 +156,7 @@ void handlePacket (int client_fd, int length, int packet_id, int state) { mob_data[i].type, mob_data[i].x, mob_data[i].y, mob_data[i].z, 0, 0 ); + broadcastMobMetadata(client_fd, -2 - i); } } diff --git a/src/packets.c b/src/packets.c index 8b67b5f..5cec9d5 100644 --- a/src/packets.c +++ b/src/packets.c @@ -1179,7 +1179,9 @@ int cs_interact (int client_fd) { // Ignore sneaking flag recv_all(client_fd, recv_buffer, 1, false); - if (type == 1) { + if (type == 0) { // Interact + interactEntity(entity_id, client_fd); + } else if (type == 1) { // Attack hurtEntity(entity_id, client_fd, D_generic, 1); } diff --git a/src/procedures.c b/src/procedures.c index 7ba75da..4048bba 100644 --- a/src/procedures.c +++ b/src/procedures.c @@ -373,18 +373,18 @@ 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; + uint8_t entity_bit_mask = 0; + if (sneaking) entity_bit_mask |= 0x02; + if (sprinting) entity_bit_mask |= 0x08; int pose = 0; if (sneaking) pose = 5; EntityData metadata[] = { { - 0, // Index (Bit Mask) + 0, // Index (Entity Bit Mask) 0, // Type (Byte) - player_bit_mask, // Value + entity_bit_mask, // Value }, { 6, // Index (Pose), @@ -405,6 +405,50 @@ void broadcastPlayerMetadata (PlayerData *player) { } } +// Sends a mob's entity metadata to the given player. +// If client_fd is -1, broadcasts to all player +void broadcastMobMetadata (int client_fd, int entity_id) { + + MobData *mob = &mob_data[-entity_id - 2]; + + EntityData *metadata; + size_t length; + + switch (mob->type) { + case 106: // Sheep + if (!((mob->data >> 5) & 1)) // Don't send metadata if sheep isn't sheared + return; + + metadata = malloc(sizeof *metadata); + metadata[0] = (EntityData){ + 17, // Index (Sheep Bit Mask), + 0, // Type (Byte), + (uint8_t)0x10, // Value + }; + length = 1; + + break; + + default: return; + } + + if (client_fd == -1) { + for (int i = 0; i < MAX_PLAYERS; i ++) { + PlayerData* player = &player_data[i]; + client_fd = player->client_fd; + + if (client_fd == -1) continue; + if (player->flags & 0x20) continue; + + sc_setEntityMetadata(client_fd, entity_id, metadata, length); + } + } else { + sc_setEntityMetadata(client_fd, entity_id, metadata, length); + } + + free(metadata); +} + 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; @@ -1344,11 +1388,57 @@ void spawnMob (uint8_t type, short x, uint8_t y, short z, uint8_t health) { ); } + // Freshly spawned mobs currently don't need metadata updates. + // If this changes, uncomment this line. + // broadcastMobMetadata(-1, i); + break; } } +void interactEntity (int entity_id, int interactor_id) { + + PlayerData *player; + if (getPlayerData(interactor_id, &player)) return; + + MobData *mob = &mob_data[-entity_id - 2]; + + switch (mob->type) { + case 106: // Sheep + if (player->inventory_items[player->hotbar] != I_shears) + return; + + if ((mob->data >> 5) & 1) // Check if sheep has already been sheared + return; + + mob->data |= 1 << 5; // Set sheared to true + + bumpToolDurability(player); + + #ifdef ENABLE_PICKUP_ANIMATION + playPickupAnimation(player, I_white_wool, mob->x, mob->y, mob->z); + #endif + + uint8_t item_count = 1 + (fast_rand() & 1); // 1-2 + givePlayerItem(player, I_white_wool, item_count); + + for (int i = 0; i < MAX_PLAYERS; i ++) { + PlayerData* player = &player_data[i]; + int client_fd = player->client_fd; + + if (client_fd == -1) continue; + if (player->flags & 0x20) continue; + + sc_entityAnimation(client_fd, interactor_id, 0); + } + + broadcastMobMetadata(-1, entity_id); + + break; + } +} + void hurtEntity (int entity_id, int attacker_id, uint8_t damage_type, uint8_t damage) { if (attacker_id > 0) { // Attacker is a player