Commit Diff


commit - 244c98552f79a6e0810b5e50b3df391c3eec3807
commit + ba86dfd927b5e2432be797e12095642dc4091fe1
blob - c5942b9ea0c23362acaca9e0cc6e065a5d6fdc4b
blob + 4f07a3ef3f1b6dfc6da80ebc11996b99bbe90669
--- include/globals.h
+++ 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;
blob - 62d09f06244a60ebd35fe543924216f2eb7fb58d
blob + f3aa33133e0b13d3946824c2a4ebe4271a82ca63
--- include/procedures.h
+++ include/procedures.h
@@ -21,6 +21,7 @@ int givePlayerItem (PlayerData *player, uint16_t item,
 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,
 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);
 
blob - dd065d9e1204851ed64c77cb04111a56295c0615
blob + 1d8bddbc91a0d2ff70831ce91a13c193ba40743c
--- src/main.c
+++ src/main.c
@@ -156,6 +156,7 @@ void handlePacket (int client_fd, int length, int pack
             mob_data[i].type, mob_data[i].x, mob_data[i].y, mob_data[i].z,
             0, 0
           );
+          broadcastMobMetadata(client_fd, -2 - i);
         }
 
       }
blob - 8b67b5f0f1717df494922a2071a74a58629b77e0
blob + 5cec9d5f195917440195a5cd8a89e4ab4da15f79
--- src/packets.c
+++ 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);
   }
 
blob - 7ba75da40d5d01df149e3fe5caa2ec4459485a32
blob + 4048bba6d0431bab6dc4aa1f20f317ff3e957ab6
--- src/procedures.c
+++ 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
       );
     }
 
+    // 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