diff --git a/include/globals.h b/include/globals.h index 27cd61b..dfcae0e 100644 --- a/include/globals.h +++ b/include/globals.h @@ -91,8 +91,10 @@ typedef struct { uint8_t hotbar; uint16_t inventory_items[41]; uint16_t craft_items[9]; + uint16_t cursor_item; uint8_t inventory_count[41]; uint8_t craft_count[9]; + uint8_t cursor_count; // 0x01 - attack cooldown // 0x02 - has not spawned yet // 0x04 - sneaking diff --git a/include/packets.h b/include/packets.h index c2968d8..db0d5a2 100644 --- a/include/packets.h +++ b/include/packets.h @@ -32,6 +32,7 @@ int sc_setCenterChunk (int client_fd, int x, int y); int sc_chunkDataAndUpdateLight (int client_fd, int _x, int _z); int sc_keepAlive (int client_fd); int sc_setContainerSlot (int client_fd, int window_id, uint16_t slot, uint8_t count, uint16_t item); +int sc_setCursorItem (int client_fd, uint16_t item, uint8_t count); int sc_setHeldItem (int client_fd, uint8_t slot); int sc_blockUpdate (int client_fd, int64_t x, int64_t y, int64_t z, uint8_t block); int sc_openScreen (int client_fd, uint8_t window, const char *title, uint16_t length); diff --git a/include/tools.h b/include/tools.h index 65fb1e8..623de35 100644 --- a/include/tools.h +++ b/include/tools.h @@ -26,6 +26,7 @@ ssize_t writeDouble (int client_fd, double num); uint8_t readByte (int client_fd); uint16_t readUint16 (int client_fd); +int16_t readInt16 (int client_fd); uint32_t readUint32 (int client_fd); uint64_t readUint64 (int client_fd); int64_t readInt64 (int client_fd); diff --git a/src/packets.c b/src/packets.c index 0daabc1..86b2f09 100644 --- a/src/packets.c +++ b/src/packets.c @@ -504,15 +504,37 @@ int cs_clickContainer (int client_fd) { int window_id = readVarInt(client_fd); readVarInt(client_fd); // ignore state id - readUint16(client_fd); // ignore clicked slot number - readByte(client_fd); // ignore button - readVarInt(client_fd); // ignore operation mode + + int16_t clicked_slot = readInt16(client_fd); + uint8_t button = readByte(client_fd); + uint8_t mode = readVarInt(client_fd); int changes_count = readVarInt(client_fd); PlayerData *player; if (getPlayerData(client_fd, &player)) return 1; + uint8_t apply_changes = true; + // prevent dropping items + if (mode == 4 && clicked_slot != -999) { + // when using drop button, re-sync the respective slot + uint8_t slot = clientSlotToServerSlot(window_id, clicked_slot); + sc_setContainerSlot(client_fd, window_id, clicked_slot, player->inventory_count[slot], player->inventory_items[slot]); + apply_changes = false; + } else if (mode == 0 && clicked_slot == -999) { + // when clicking outside inventory, return the dropped item to the player + if (button == 0) { + givePlayerItem(player, player->cursor_item, player->cursor_count); + player->cursor_item = 0; + player->cursor_count = 0; + } else { + givePlayerItem(player, player->cursor_item, 1); + player->cursor_count -= 1; + if (player->cursor_count == 0) player->cursor_item = 0; + } + apply_changes = false; + } + uint8_t slot, count, craft = false; uint16_t item; int tmp; @@ -521,10 +543,10 @@ int cs_clickContainer (int client_fd) { slot = clientSlotToServerSlot(window_id, readUint16(client_fd)); // slots outside of the inventory overflow into the crafting buffer - if (slot > 40) craft = true; + if (slot > 40 && apply_changes) craft = true; if (!readByte(client_fd)) { // no item? - if (slot != 255) { + if (slot != 255 && apply_changes) { player->inventory_items[slot] = 0; player->inventory_count[slot] = 0; } @@ -540,7 +562,7 @@ int cs_clickContainer (int client_fd) { tmp = readVarInt(client_fd); recv_all(client_fd, recv_buffer, tmp, false); - if (count > 0) { + if (count > 0 && apply_changes) { player->inventory_items[slot] = item; player->inventory_count[slot] = count; } @@ -558,20 +580,42 @@ int cs_clickContainer (int client_fd) { } } - // read but ignore carried item slot (for now) + // assign cursor-carried item slot if (readByte(client_fd)) { - readVarInt(client_fd); - readVarInt(client_fd); + player->cursor_item = readVarInt(client_fd); + player->cursor_count = readVarInt(client_fd); + // ignore components tmp = readVarInt(client_fd); recv_all(client_fd, recv_buffer, tmp, false); tmp = readVarInt(client_fd); recv_all(client_fd, recv_buffer, tmp, false); + } else { + player->cursor_item = 0; + player->cursor_count = 0; } return 0; } +// S->C Set Cursor Item +int sc_setCursorItem (int client_fd, uint16_t item, uint8_t count) { + + writeVarInt(client_fd, 1 + sizeVarInt(count) + (count != 0 ? sizeVarInt(item) + 2 : 0)); + writeByte(client_fd, 0x59); + + writeVarInt(client_fd, count); + if (count == 0) return 0; + + writeVarInt(client_fd, item); + + // Skip components + writeByte(client_fd, 0); + writeByte(client_fd, 0); + + return 0; +} + // C->S Set Player Position And Rotation int cs_setPlayerPositionAndRotation (int client_fd, double *x, double *y, double *z, float *yaw, float *pitch, uint8_t *on_ground) { @@ -656,6 +700,11 @@ int cs_closeContainer (int client_fd) { if (client_slot != 255) sc_setContainerSlot(player->client_fd, window_id, client_slot, 0, 0); } + givePlayerItem(player, player->cursor_item, player->cursor_count); + sc_setCursorItem(client_fd, 0, 0); + player->cursor_item = 0; + player->cursor_count = 0; + return 0; } diff --git a/src/procedures.c b/src/procedures.c index 51dfe80..1d62040 100644 --- a/src/procedures.c +++ b/src/procedures.c @@ -572,6 +572,20 @@ uint8_t getItemStackSize (uint16_t item) { void handlePlayerAction (PlayerData *player, int action, short x, short y, short z) { + // Re-sync slot when player drops an item + if (action == 3 || action == 4) { + sc_setContainerSlot( + player->client_fd, 0, + serverSlotToClientSlot(0, player->hotbar), + player->inventory_count[player->hotbar], + player->inventory_items[player->hotbar] + ); + return; + } + + // Ignore further actions not pertaining to mining blocks + if (action != 0 && action != 2) return; + // In creative, only the "start mining" action is sent // No additional verification is performed, the block is simply removed if (action == 0 && GAMEMODE == 1) { @@ -579,9 +593,6 @@ void handlePlayerAction (PlayerData *player, int action, short x, short y, short return; } - // Ignore actions not pertaining to mining blocks - if (action != 0 && action != 2) return; - uint8_t block = getBlockAt(x, y, z); // If this is a "start mining" packet, the block must be instamine diff --git a/src/tools.c b/src/tools.c index b44669f..d29c913 100644 --- a/src/tools.c +++ b/src/tools.c @@ -134,6 +134,10 @@ uint16_t readUint16 (int client_fd) { recv_count = recv_all(client_fd, recv_buffer, 2, false); return ((uint16_t)recv_buffer[0] << 8) | recv_buffer[1]; } +int16_t readInt16 (int client_fd) { + recv_count = recv_all(client_fd, recv_buffer, 2, false); + return ((int16_t)recv_buffer[0] << 8) | (int16_t)recv_buffer[1]; +} uint32_t readUint32 (int client_fd) { recv_count = recv_all(client_fd, recv_buffer, 4, false); return ((uint32_t)recv_buffer[0] << 24) |