prevent players from dropping and losing items

This commit is contained in:
p2r3
2025-08-24 20:41:48 +03:00
parent ab9ed43a9b
commit 549745516f
6 changed files with 80 additions and 12 deletions

View File

@@ -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

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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) |