From a796dc88ff627ec2492e7cf4add0b3139eec280b Mon Sep 17 00:00:00 2001 From: Plasma Date: Wed, 5 Jun 2024 05:39:45 -0400 Subject: [PATCH] Implement interfaces of various GUI windows (double chests, anvil, etc) and fix NBT/missing packet crashes (#196) * Implement double chests * Implement Generic_9x4 and Generic_9x5 * Implement generic_9x1 * Fix generic_9x1's positioning and implement generic_9x2 * Refactor names, fix some formatting. * Remove forgotten comment * Fix NBT related crash * Add missing packet * Prepare for other interfaces * Implement anvil ui * Implement dropper & beacon * Implement furnace, blast furnace, smoker * Implement beacon --------- Co-authored-by: stackotter --- .../Sources/Datatypes/NBT/NBTCompound.swift | 2 +- Sources/Core/Sources/GUI/GUISprite.swift | 46 ++- Sources/Core/Sources/GUI/WindowType.swift | 380 +++++++++++++++++- .../Network/Protocol/PacketRegistry.swift | 3 +- .../Resources/GUI/GUITextureSlice.swift | 80 ++++ .../Sources/World/Dimension/Dimension.swift | 18 +- 6 files changed, 508 insertions(+), 21 deletions(-) diff --git a/Sources/Core/Sources/Datatypes/NBT/NBTCompound.swift b/Sources/Core/Sources/Datatypes/NBT/NBTCompound.swift index da9b9c9e..346c5dab 100644 --- a/Sources/Core/Sources/Datatypes/NBT/NBTCompound.swift +++ b/Sources/Core/Sources/Datatypes/NBT/NBTCompound.swift @@ -138,7 +138,7 @@ extension NBT { case .end: break case .byte: - value = try buffer.readByte() + value = try buffer.readSignedByte() case .short: value = try buffer.readSignedShort(endianness: .big) case .int: diff --git a/Sources/Core/Sources/GUI/GUISprite.swift b/Sources/Core/Sources/GUI/GUISprite.swift index b7877a2e..0ecceb31 100644 --- a/Sources/Core/Sources/GUI/GUISprite.swift +++ b/Sources/Core/Sources/GUI/GUISprite.swift @@ -16,11 +16,23 @@ public enum GUISprite { case xpBarForeground case inventory case craftingTable - /// If positioned directly above ``GUISprite/singleChestBottomHalf`` it forms + case furnace + case blastFurnace + case smoker + case anvil + case dispenser + case beacon + + /// If positioned directly above ``GUISprite/genericInventory`` it forms /// the background for a single chest window. The way the texture is made forces /// these to be separate sprites. - case singleChestTopHalf - case singleChestBottomHalf + case genericInventory // Inventory for most interfaces, its a part of the sprite + case generic9x1 + case generic9x2 + case generic9x3 + case generic9x4 + case generic9x5 + case generic9x6 case pinkBossBarBackground case pinkBossBarForeground @@ -81,10 +93,32 @@ public enum GUISprite { return GUISpriteDescriptor(slice: .inventory, position: [0, 0], size: [176, 166]) case .craftingTable: return GUISpriteDescriptor(slice: .craftingTable, position: [0, 0], size: [176, 166]) - case .singleChestTopHalf: - return GUISpriteDescriptor(slice: .genericContainer, position: [0, 0], size: [176, 71]) - case .singleChestBottomHalf: + case .furnace: + return GUISpriteDescriptor(slice: .furnace, position: [0, 0], size: [176, 166]) + case .blastFurnace: + return GUISpriteDescriptor(slice: .blastFurnace, position: [0, 0], size: [176, 166]) + case .smoker: + return GUISpriteDescriptor(slice: .smoker, position: [0, 0], size: [176, 166]) + case .anvil: + return GUISpriteDescriptor(slice: .anvil, position: [0, 0], size: [176, 166]) + case .dispenser: + return GUISpriteDescriptor(slice: .dispenser, position: [0, 0], size: [176, 166]) + case .beacon: + return GUISpriteDescriptor(slice: .beacon, position: [0,0], size: [229, 218]) + case .genericInventory: return GUISpriteDescriptor(slice: .genericContainer, position: [0, 125], size: [176, 97]) + case .generic9x1: + return GUISpriteDescriptor(slice: .genericContainer, position: [0, 0], size: [176, 35]) + case .generic9x2: + return GUISpriteDescriptor(slice: .genericContainer, position: [0, 0], size: [176, 53]) + case .generic9x3: + return GUISpriteDescriptor(slice: .genericContainer, position: [0, 0], size: [176, 71]) + case .generic9x4: + return GUISpriteDescriptor(slice: .genericContainer, position: [0, 0], size: [176, 89]) + case .generic9x5: + return GUISpriteDescriptor(slice: .genericContainer, position: [0, 0], size: [176, 107]) + case .generic9x6: + return GUISpriteDescriptor(slice: .genericContainer, position: [0, 0], size: [176, 222]) case .pinkBossBarBackground: return GUISpriteDescriptor(slice: .bars, position: [0, 0], size: [182, 5]) case .pinkBossBarForeground: diff --git a/Sources/Core/Sources/GUI/WindowType.swift b/Sources/Core/Sources/GUI/WindowType.swift index 8781cf67..58cc71b9 100644 --- a/Sources/Core/Sources/GUI/WindowType.swift +++ b/Sources/Core/Sources/GUI/WindowType.swift @@ -61,12 +61,284 @@ public struct WindowType { ] ) - public static let chest = WindowType( + public static let anvil = WindowType( + id: .vanilla(7), + identifier: Identifier(namespace: "minecraft", name: "crafting"), + background: .sprite(.anvil), + slotCount: 39, + areas:[ + WindowArea( + startIndex: 0, + width: 1, + height: 1, + position: Vec2i(27, 47) + ), + WindowArea( + startIndex: 1, + width: 1, + height: 1, + position: Vec2i(76, 47) + ), + WindowArea( + startIndex: 2, + width: 1, + height: 1, + position: Vec2i(134, 47) + ), + WindowArea( + startIndex: 3, + width: 9, + height: 3, + position: Vec2i(8, 84) + ), + WindowArea( + startIndex: 30, + width: 9, + height: 1, + position: Vec2i(8, 142) + ) + ] + ) + + public static let furnace = WindowType( + id: .vanilla(13), + identifier: Identifier(namespace: "minecraft", name: "furnace"), + background: .sprite(.furnace), + slotCount: 39, + areas: [ + WindowArea( + startIndex: 0, + width: 1, + height: 1, + position: Vec2i(56, 17) + ), + WindowArea( + startIndex: 1, + width: 1, + height: 1, + position: Vec2i(56, 53) + ), + WindowArea( + startIndex: 2, + width: 1, + height: 1, + position: Vec2i(112, 31) + ), + WindowArea( + startIndex: 3, + width: 9, + height: 3, + position: Vec2i(8, 84) + ), + WindowArea( + startIndex: 30, + width: 9, + height: 1, + position: Vec2i(8, 142) + ) + ] + ) + + public static let blastFurnace = WindowType( + id: .vanilla(9), + identifier: Identifier(namespace: "minecraft", name: "blast_furnace"), + background: .sprite(.blastFurnace), + slotCount: 39, + areas: [ + WindowArea( + startIndex: 0, + width: 1, + height: 1, + position: Vec2i(56, 17) + ), + WindowArea( + startIndex: 1, + width: 1, + height: 1, + position: Vec2i(56, 53) + ), + WindowArea( + startIndex: 2, + width: 1, + height: 1, + position: Vec2i(112, 31) + ), + WindowArea( + startIndex: 3, + width: 9, + height: 3, + position: Vec2i(8, 84) + ), + WindowArea( + startIndex: 30, + width: 9, + height: 1, + position: Vec2i(8, 142) + ) + ] + ) + + public static let smoker = WindowType( + id: .vanilla(21), + identifier: Identifier(namespace: "minecraft", name: "smoker"), + background: .sprite(.smoker), + slotCount: 39, + areas: [ + WindowArea( + startIndex: 0, + width: 1, + height: 1, + position: Vec2i(56, 17) + ), + WindowArea( + startIndex: 1, + width: 1, + height: 1, + position: Vec2i(56, 53) + ), + WindowArea( + startIndex: 2, + width: 1, + height: 1, + position: Vec2i(112, 31) + ), + WindowArea( + startIndex: 3, + width: 9, + height: 3, + position: Vec2i(8, 84) + ), + WindowArea( + startIndex: 30, + width: 9, + height: 1, + position: Vec2i(8, 142) + ) + ] + ) + + public static let beacon = WindowType( + id: .vanilla(8), + identifier: Identifier(namespace: "minecraft", name: "beacon"), + background: .sprite(.beacon), + slotCount: 37, + areas: [ + WindowArea( + startIndex: 0, + width: 1, + height: 1, + position: Vec2i(136, 110) + ), + WindowArea( + startIndex: 1, + width: 9, + height: 3, + position: Vec2i(36, 137) + ), + WindowArea( + startIndex: 28, + width: 9, + height: 1, + position: Vec2i(36, 196) + ) + ] + ) + + // Dispenser & dropper + public static let generic3x3 = WindowType( + id: .vanilla(6), + identifier: Identifier(namespace: "minecraft", name: "generic_3x3"), + background: .sprite(.dispenser), + slotCount: 45, + areas: [ + WindowArea( + startIndex: 0, + width: 3, + height: 3, + position: Vec2i(62, 17) + ), + WindowArea( + startIndex: 9, + width: 9, + height: 3, + position: Vec2i(8, 84) + ), + WindowArea( + startIndex: 36, + width: 9, + height: 1, + position: Vec2i(8, 142) + ) + ] + ) + + // Generic window types + public static let generic9x1 = WindowType( + id: .vanilla(0), + identifier: Identifier(namespace: "minecraft", name: "generic_9x1"), + background: GUIElement.list(spacing: 0) { + GUIElement.sprite(.generic9x1) + GUIElement.sprite(.genericInventory) + }, + slotCount: 45, + areas: [ + WindowArea( + startIndex: 0, + width: 9, + height: 1, + position: Vec2i(8, 18) + ), + WindowArea( + startIndex: 9, + width: 9, + height: 3, + position: Vec2i(8, 50) + ), + WindowArea( + startIndex: 36, + width: 9, + height: 1, + position: Vec2i(8, 108) + ) + ] + ) + + public static let generic9x2 = WindowType( + id: .vanilla(1), + identifier: Identifier(namespace: "minecraft", name: "generic_9x2"), + background: GUIElement.list(spacing: 0) { + GUIElement.sprite(.generic9x2) + GUIElement.sprite(.genericInventory) + }, + slotCount: 54, + areas: [ + WindowArea( + startIndex: 0, + width: 9, + height: 2, + position: Vec2i(8, 18) + ), + WindowArea( + startIndex: 18, + width: 9, + height: 3, + position: Vec2i(8, 68) + ), + WindowArea( + startIndex: 45, + width: 9, + height: 1, + position: Vec2i(8, 126) + ) + ] + ) + + public static let generic9x3 = WindowType( id: .vanilla(2), identifier: Identifier(namespace: "minecraft", name: "generic_9x3"), background: GUIElement.list(spacing: 0) { - GUIElement.sprite(.singleChestTopHalf) - GUIElement.sprite(.singleChestBottomHalf) + GUIElement.sprite(.generic9x3) + GUIElement.sprite(.genericInventory) }, slotCount: 63, areas: [ @@ -91,12 +363,112 @@ public struct WindowType { ] ) + public static let generic9x4 = WindowType( + id: .vanilla(3), + identifier: Identifier(namespace: "minecraft", name: "generic_9x4"), + background: GUIElement.list(spacing: 0) { + GUIElement.sprite(.generic9x4) + GUIElement.sprite(.genericInventory) + }, + slotCount: 72, + areas: [ + WindowArea( + startIndex: 0, + width: 9, + height: 4, + position: Vec2i(8, 18) + ), + WindowArea( + startIndex: 36, + width: 9, + height: 3, + position: Vec2i(8, 104) + ), + WindowArea( + startIndex: 63, + width: 9, + height: 1, + position: Vec2i(8, 162) + ) + ] + ) + + public static let generic9x5 = WindowType( + id: .vanilla(4), + identifier: Identifier(namespace: "minecraft", name:"generic_9x5"), + background: GUIElement.list(spacing: 0) { + GUIElement.sprite(.generic9x5) + GUIElement.sprite(.genericInventory) + }, + slotCount: 81, + areas: [ + WindowArea( + startIndex: 0, + width: 9, + height: 5, + position: Vec2i(8, 18) + ), + WindowArea( + startIndex: 45, + width: 9, + height: 3, + position: Vec2i(8, 122) + ), + WindowArea( + startIndex: 72, + width: 9, + height: 1, + position: Vec2i(8, 180) + ) + ] + ) + + public static let generic9x6 = WindowType( + id: .vanilla(5), + identifier: Identifier(namespace: "minecraft", name: "generic_9x6"), + background: GUIElement.list(spacing: 0) { + GUIElement.sprite(.generic9x6) + }, + slotCount: 90, + areas: [ + WindowArea( + startIndex: 0, + width: 9, + height: 6, + position: Vec2i(8, 18) + ), + WindowArea( + startIndex: 54, + width: 9, + height: 3, + position: Vec2i(8, 140) + ), + WindowArea( + startIndex: 81, + width: 9, + height: 1, + position: Vec2i(8, 198) + ), + ] + ) + /// The window types understood by vanilla. public static let types = [Id: Self]( values: [ inventory, craftingTable, - chest + anvil, + furnace, + blastFurnace, + smoker, + beacon, + generic9x1, + generic9x2, + generic9x3, + generic9x4, + generic9x5, + generic9x6, + generic3x3 ], keyedBy: \.id ) diff --git a/Sources/Core/Sources/Network/Protocol/PacketRegistry.swift b/Sources/Core/Sources/Network/Protocol/PacketRegistry.swift index 3e59046d..f8ed63cd 100644 --- a/Sources/Core/Sources/Network/Protocol/PacketRegistry.swift +++ b/Sources/Core/Sources/Network/Protocol/PacketRegistry.swift @@ -126,7 +126,8 @@ public struct PacketRegistry { EntityAttributesPacket.self, EntityEffectPacket.self, DeclareRecipesPacket.self, - TagsPacket.self + TagsPacket.self, + WindowPropertyPacket.self ], toState: .play) return registry } diff --git a/Sources/Core/Sources/Resources/GUI/GUITextureSlice.swift b/Sources/Core/Sources/Resources/GUI/GUITextureSlice.swift index 028c209b..f8a1bfc7 100644 --- a/Sources/Core/Sources/Resources/GUI/GUITextureSlice.swift +++ b/Sources/Core/Sources/Resources/GUI/GUITextureSlice.swift @@ -6,6 +6,22 @@ public enum GUITextureSlice: Int, CaseIterable { case inventory case craftingTable case genericContainer + case dispenser // also covers dropper + case anvil + case beacon + case blastFurnace + case brewingStand + case enchantingTable + case furnace + case grindstone + case hopper + case loom + case merchant // Villagers & wandering traders + case shulkerBox + case smithingTable + case smoker + case cartographyTable + case stonecutter /// The path of the slice's underlying texture in a resource pack's textures directory. public var path: String { @@ -22,6 +38,38 @@ public enum GUITextureSlice: Int, CaseIterable { return "container/crafting_table.png" case .genericContainer: return "container/generic_54.png" + case .dispenser: + return "container/dispenser.png" + case .anvil: + return "container/anvil.png" + case .beacon: + return "container/beacon.png" + case .blastFurnace: + return "container/blast_furnace.png" + case .brewingStand: + return "container/brewing_stand.png" + case .enchantingTable: + return "container/enchanting_table.png" + case .furnace: + return "container/furnace.png" + case .grindstone: + return "container/grindstone.png" + case .hopper: + return "container/hopper.png" + case .loom: + return "container/loom.png" + case .merchant: + return "container/villager2.png" + case .shulkerBox: + return "container/shulker_box.png" + case .smithingTable: + return "container/smithing.png" + case .smoker: + return "container/smoker.png" + case .cartographyTable: + return "container/cartography_table.png" + case .stonecutter: + return "container/stonecutter.png" } } @@ -40,6 +88,38 @@ public enum GUITextureSlice: Int, CaseIterable { return Identifier(name: "gui/container/crafting_table") case .genericContainer: return Identifier(name: "gui/container/generic_54") + case .dispenser: + return Identifier(name: "gui/container/dispenser") + case .anvil: + return Identifier(name: "gui/container/anvil") + case .beacon: + return Identifier(name: "gui/container/beacon") + case .blastFurnace: + return Identifier(name: "gui/container/blast_furnace") + case .brewingStand: + return Identifier(name: "gui/container/brewing_stand") + case .enchantingTable: + return Identifier(name: "gui/container/enchanting_table") + case .furnace: + return Identifier(name: "gui/container/furnace") + case .grindstone: + return Identifier(name: "gui/container/grindstone") + case .hopper: + return Identifier(name: "gui/container/hopper") + case .loom: + return Identifier(name: "gui/container/loom") + case .merchant: + return Identifier(name: "gui/container/villager2") + case .shulkerBox: + return Identifier(name: "gui/container/shulker_box") + case .smithingTable: + return Identifier(name: "gui/container/smithing") + case .smoker: + return Identifier(name: "gui/container/smoker") + case .cartographyTable: + return Identifier(name: "gui/container/cartography_table") + case .stonecutter: + return Identifier(name: "gui/container/stonecutter") } } } diff --git a/Sources/Core/Sources/World/Dimension/Dimension.swift b/Sources/Core/Sources/World/Dimension/Dimension.swift index 03adc8ba..89a61ede 100644 --- a/Sources/Core/Sources/World/Dimension/Dimension.swift +++ b/Sources/Core/Sources/World/Dimension/Dimension.swift @@ -89,23 +89,23 @@ public struct Dimension { let fixedTime: Int64? = try? compound.get("fixed_time") self.fixedTime = fixedTime.map(Int.init) - let isNatural: UInt8 = try compound.get("natural") + let isNatural: Int8 = try compound.get("natural") self.isNatural = isNatural == 1 - let hasCeiling: UInt8 = try compound.get("has_ceiling") + let hasCeiling: Int8 = try compound.get("has_ceiling") self.hasCeiling = hasCeiling == 1 - let hasSkyLight: UInt8 = try compound.get("has_skylight") + let hasSkyLight: Int8 = try compound.get("has_skylight") self.hasSkyLight = hasSkyLight == 1 - let shrunk: UInt8 = try compound.get("shrunk") + let shrunk: Int8 = try compound.get("shrunk") self.shrunk = shrunk == 1 - let ultrawarm: UInt8 = try compound.get("ultrawarm") + let ultrawarm: Int8 = try compound.get("ultrawarm") self.ultrawarm = ultrawarm == 1 - let hasRaids: UInt8 = try compound.get("has_raids") + let hasRaids: Int8 = try compound.get("has_raids") self.hasRaids = hasRaids == 1 - let respawnAnchorWorks: UInt8 = try compound.get("respawn_anchor_works") + let respawnAnchorWorks: Int8 = try compound.get("respawn_anchor_works") self.respawnAnchorWorks = respawnAnchorWorks == 1 - let bedWorks: UInt8 = try compound.get("bed_works") + let bedWorks: Int8 = try compound.get("bed_works") self.bedWorks = bedWorks == 1 - let piglinSafe: UInt8 = try compound.get("piglin_safe") + let piglinSafe: Int8 = try compound.get("piglin_safe") self.piglinSafe = piglinSafe == 1 let logicalHeight: Int32 = try compound.get("logical_height") self.logicalHeight = Int(logicalHeight)