package housing import ( "encoding/binary" "fmt" "math" "time" ) // HousingPacketBuilder handles building packets for housing client communication type HousingPacketBuilder struct { clientVersion int } // NewHousingPacketBuilder creates a new packet builder func NewHousingPacketBuilder(clientVersion int) *HousingPacketBuilder { return &HousingPacketBuilder{ clientVersion: clientVersion, } } // BuildHousePurchasePacket builds the house purchase interface packet func (hpb *HousingPacketBuilder) BuildHousePurchasePacket(data *HousePurchasePacketData) ([]byte, error) { if data == nil { return nil, fmt.Errorf("house purchase data is nil") } // Start with base packet structure packet := make([]byte, 0, 512) // Packet type identifier packet = append(packet, 0x01) // House Purchase packet type // House ID (4 bytes) houseIDBytes := make([]byte, 4) binary.LittleEndian.PutUint32(houseIDBytes, uint32(data.HouseID)) packet = append(packet, houseIDBytes...) // House name length and data nameBytes := []byte(data.Name) packet = append(packet, byte(len(nameBytes))) packet = append(packet, nameBytes...) // Cost in coins (8 bytes) costCoinBytes := make([]byte, 8) binary.LittleEndian.PutUint64(costCoinBytes, uint64(data.CostCoin)) packet = append(packet, costCoinBytes...) // Cost in status (8 bytes) costStatusBytes := make([]byte, 8) binary.LittleEndian.PutUint64(costStatusBytes, uint64(data.CostStatus)) packet = append(packet, costStatusBytes...) // Upkeep in coins (8 bytes) upkeepCoinBytes := make([]byte, 8) binary.LittleEndian.PutUint64(upkeepCoinBytes, uint64(data.UpkeepCoin)) packet = append(packet, upkeepCoinBytes...) // Upkeep in status (8 bytes) upkeepStatusBytes := make([]byte, 8) binary.LittleEndian.PutUint64(upkeepStatusBytes, uint64(data.UpkeepStatus)) packet = append(packet, upkeepStatusBytes...) // Alignment requirement (1 byte) packet = append(packet, byte(data.Alignment)) // Guild level requirement (1 byte) packet = append(packet, byte(data.GuildLevel)) // Vault slots (4 bytes) vaultSlotsBytes := make([]byte, 4) binary.LittleEndian.PutUint32(vaultSlotsBytes, uint32(data.VaultSlots)) packet = append(packet, vaultSlotsBytes...) // Description length and data descBytes := []byte(data.Description) descLen := make([]byte, 2) binary.LittleEndian.PutUint16(descLen, uint16(len(descBytes))) packet = append(packet, descLen...) packet = append(packet, descBytes...) return packet, nil } // BuildHousingListPacket builds the player housing list packet func (hpb *HousingPacketBuilder) BuildHousingListPacket(data *HouseListPacketData) ([]byte, error) { if data == nil { return nil, fmt.Errorf("house list data is nil") } packet := make([]byte, 0, 1024) // Packet type identifier packet = append(packet, 0x02) // Housing List packet type // Number of houses (4 bytes) houseCountBytes := make([]byte, 4) binary.LittleEndian.PutUint32(houseCountBytes, uint32(len(data.Houses))) packet = append(packet, houseCountBytes...) // House entries for _, house := range data.Houses { // Unique ID (8 bytes) uniqueIDBytes := make([]byte, 8) binary.LittleEndian.PutUint64(uniqueIDBytes, uint64(house.UniqueID)) packet = append(packet, uniqueIDBytes...) // House name length and data nameBytes := []byte(house.Name) packet = append(packet, byte(len(nameBytes))) packet = append(packet, nameBytes...) // House type length and data typeBytes := []byte(house.HouseType) packet = append(packet, byte(len(typeBytes))) packet = append(packet, typeBytes...) // Upkeep due timestamp (8 bytes) upkeepDueBytes := make([]byte, 8) binary.LittleEndian.PutUint64(upkeepDueBytes, uint64(house.UpkeepDue.Unix())) packet = append(packet, upkeepDueBytes...) // Escrow coins (8 bytes) escrowCoinsBytes := make([]byte, 8) binary.LittleEndian.PutUint64(escrowCoinsBytes, uint64(house.EscrowCoins)) packet = append(packet, escrowCoinsBytes...) // Escrow status (8 bytes) escrowStatusBytes := make([]byte, 8) binary.LittleEndian.PutUint64(escrowStatusBytes, uint64(house.EscrowStatus)) packet = append(packet, escrowStatusBytes...) // House status (1 byte) packet = append(packet, byte(house.Status)) // Can enter flag (1 byte) if house.CanEnter { packet = append(packet, 0x01) } else { packet = append(packet, 0x00) } } return packet, nil } // BuildBaseHouseWindowPacket builds the main house management interface packet func (hpb *HousingPacketBuilder) BuildBaseHouseWindowPacket(data *BaseHouseWindowPacketData) ([]byte, error) { if data == nil { return nil, fmt.Errorf("base house window data is nil") } packet := make([]byte, 0, 2048) // Packet type identifier packet = append(packet, 0x03) // Base House Window packet type // House info uniqueIDBytes := make([]byte, 8) binary.LittleEndian.PutUint64(uniqueIDBytes, uint64(data.HouseInfo.UniqueID)) packet = append(packet, uniqueIDBytes...) // House name nameBytes := []byte(data.HouseInfo.Name) packet = append(packet, byte(len(nameBytes))) packet = append(packet, nameBytes...) // House type typeBytes := []byte(data.HouseInfo.HouseType) packet = append(packet, byte(len(typeBytes))) packet = append(packet, typeBytes...) // Upkeep due upkeepDueBytes := make([]byte, 8) binary.LittleEndian.PutUint64(upkeepDueBytes, uint64(data.HouseInfo.UpkeepDue.Unix())) packet = append(packet, upkeepDueBytes...) // Escrow balances escrowCoinsBytes := make([]byte, 8) binary.LittleEndian.PutUint64(escrowCoinsBytes, uint64(data.HouseInfo.EscrowCoins)) packet = append(packet, escrowCoinsBytes...) escrowStatusBytes := make([]byte, 8) binary.LittleEndian.PutUint64(escrowStatusBytes, uint64(data.HouseInfo.EscrowStatus)) packet = append(packet, escrowStatusBytes...) // Recent deposits count and data depositCountBytes := make([]byte, 4) binary.LittleEndian.PutUint32(depositCountBytes, uint32(len(data.RecentDeposits))) packet = append(packet, depositCountBytes...) for _, deposit := range data.RecentDeposits { // Timestamp (8 bytes) timestampBytes := make([]byte, 8) binary.LittleEndian.PutUint64(timestampBytes, uint64(deposit.Timestamp.Unix())) packet = append(packet, timestampBytes...) // Amount (8 bytes) amountBytes := make([]byte, 8) binary.LittleEndian.PutUint64(amountBytes, uint64(deposit.Amount)) packet = append(packet, amountBytes...) // Status (8 bytes) statusBytes := make([]byte, 8) binary.LittleEndian.PutUint64(statusBytes, uint64(deposit.Status)) packet = append(packet, statusBytes...) // Player name nameBytes := []byte(deposit.Name) packet = append(packet, byte(len(nameBytes))) packet = append(packet, nameBytes...) } // Recent history count and data historyCountBytes := make([]byte, 4) binary.LittleEndian.PutUint32(historyCountBytes, uint32(len(data.RecentHistory))) packet = append(packet, historyCountBytes...) for _, history := range data.RecentHistory { // Timestamp (8 bytes) timestampBytes := make([]byte, 8) binary.LittleEndian.PutUint64(timestampBytes, uint64(history.Timestamp.Unix())) packet = append(packet, timestampBytes...) // Amount (8 bytes) amountBytes := make([]byte, 8) binary.LittleEndian.PutUint64(amountBytes, uint64(history.Amount)) packet = append(packet, amountBytes...) // Status (8 bytes) statusBytes := make([]byte, 8) binary.LittleEndian.PutUint64(statusBytes, uint64(history.Status)) packet = append(packet, statusBytes...) // Positive flag (1 byte) packet = append(packet, byte(history.PosFlag)) // Transaction type (4 bytes) typeBytes := make([]byte, 4) binary.LittleEndian.PutUint32(typeBytes, uint32(history.Type)) packet = append(packet, typeBytes...) // Reason length and data reasonBytes := []byte(history.Reason) packet = append(packet, byte(len(reasonBytes))) packet = append(packet, reasonBytes...) // Player name nameBytes := []byte(history.Name) packet = append(packet, byte(len(nameBytes))) packet = append(packet, nameBytes...) } // Amenities count and data amenityCountBytes := make([]byte, 4) binary.LittleEndian.PutUint32(amenityCountBytes, uint32(len(data.Amenities))) packet = append(packet, amenityCountBytes...) for _, amenity := range data.Amenities { // Amenity ID (4 bytes) idBytes := make([]byte, 4) binary.LittleEndian.PutUint32(idBytes, uint32(amenity.ID)) packet = append(packet, idBytes...) // Type (4 bytes) typeBytes := make([]byte, 4) binary.LittleEndian.PutUint32(typeBytes, uint32(amenity.Type)) packet = append(packet, typeBytes...) // Name nameBytes := []byte(amenity.Name) packet = append(packet, byte(len(nameBytes))) packet = append(packet, nameBytes...) // Position (12 bytes - 3 floats) xBytes := make([]byte, 4) binary.LittleEndian.PutUint32(xBytes, math.Float32bits(amenity.X)) packet = append(packet, xBytes...) yBytes := make([]byte, 4) binary.LittleEndian.PutUint32(yBytes, math.Float32bits(amenity.Y)) packet = append(packet, yBytes...) zBytes := make([]byte, 4) binary.LittleEndian.PutUint32(zBytes, math.Float32bits(amenity.Z)) packet = append(packet, zBytes...) // Heading (4 bytes) headingBytes := make([]byte, 4) binary.LittleEndian.PutUint32(headingBytes, math.Float32bits(amenity.Heading)) packet = append(packet, headingBytes...) // Is active flag (1 byte) if amenity.IsActive { packet = append(packet, 0x01) } else { packet = append(packet, 0x00) } } // House settings packet = hpb.appendHouseSettings(packet, data.Settings) // Can manage flag (1 byte) if data.CanManage { packet = append(packet, 0x01) } else { packet = append(packet, 0x00) } return packet, nil } // BuildHouseVisitPacket builds the house visit interface packet func (hpb *HousingPacketBuilder) BuildHouseVisitPacket(data *HouseVisitPacketData) ([]byte, error) { if data == nil { return nil, fmt.Errorf("house visit data is nil") } packet := make([]byte, 0, 1024) // Packet type identifier packet = append(packet, 0x04) // House Visit packet type // Number of available houses (4 bytes) houseCountBytes := make([]byte, 4) binary.LittleEndian.PutUint32(houseCountBytes, uint32(len(data.AvailableHouses))) packet = append(packet, houseCountBytes...) // House entries for _, house := range data.AvailableHouses { // Unique ID (8 bytes) uniqueIDBytes := make([]byte, 8) binary.LittleEndian.PutUint64(uniqueIDBytes, uint64(house.UniqueID)) packet = append(packet, uniqueIDBytes...) // Owner name ownerBytes := []byte(house.OwnerName) packet = append(packet, byte(len(ownerBytes))) packet = append(packet, ownerBytes...) // House name nameBytes := []byte(house.HouseName) packet = append(packet, byte(len(nameBytes))) packet = append(packet, nameBytes...) // House type typeBytes := []byte(house.HouseType) packet = append(packet, byte(len(typeBytes))) packet = append(packet, typeBytes...) // Public note noteBytes := []byte(house.PublicNote) noteLen := make([]byte, 2) binary.LittleEndian.PutUint16(noteLen, uint16(len(noteBytes))) packet = append(packet, noteLen...) packet = append(packet, noteBytes...) // Can visit flag (1 byte) if house.CanVisit { packet = append(packet, 0x01) } else { packet = append(packet, 0x00) } // Requires approval flag (1 byte) if house.RequiresApproval { packet = append(packet, 0x01) } else { packet = append(packet, 0x00) } } return packet, nil } // BuildHouseUpdatePacket builds a house status update packet func (hpb *HousingPacketBuilder) BuildHouseUpdatePacket(house *PlayerHouse) ([]byte, error) { if house == nil { return nil, fmt.Errorf("player house is nil") } packet := make([]byte, 0, 256) // Packet type identifier packet = append(packet, 0x05) // House Update packet type // Unique ID (8 bytes) uniqueIDBytes := make([]byte, 8) binary.LittleEndian.PutUint64(uniqueIDBytes, uint64(house.UniqueID)) packet = append(packet, uniqueIDBytes...) // Status (1 byte) packet = append(packet, byte(house.Status)) // Upkeep due (8 bytes) upkeepDueBytes := make([]byte, 8) binary.LittleEndian.PutUint64(upkeepDueBytes, uint64(house.UpkeepDue.Unix())) packet = append(packet, upkeepDueBytes...) // Escrow balances (16 bytes total) escrowCoinsBytes := make([]byte, 8) binary.LittleEndian.PutUint64(escrowCoinsBytes, uint64(house.EscrowCoins)) packet = append(packet, escrowCoinsBytes...) escrowStatusBytes := make([]byte, 8) binary.LittleEndian.PutUint64(escrowStatusBytes, uint64(house.EscrowStatus)) packet = append(packet, escrowStatusBytes...) return packet, nil } // BuildHouseErrorPacket builds an error notification packet func (hpb *HousingPacketBuilder) BuildHouseErrorPacket(errorCode int, message string) ([]byte, error) { packet := make([]byte, 0, 256) // Packet type identifier packet = append(packet, 0x06) // House Error packet type // Error code (4 bytes) errorBytes := make([]byte, 4) binary.LittleEndian.PutUint32(errorBytes, uint32(errorCode)) packet = append(packet, errorBytes...) // Error message length and data messageBytes := []byte(message) msgLen := make([]byte, 2) binary.LittleEndian.PutUint16(msgLen, uint16(len(messageBytes))) packet = append(packet, msgLen...) packet = append(packet, messageBytes...) return packet, nil } // BuildHouseDepositPacket builds a deposit confirmation packet func (hpb *HousingPacketBuilder) BuildHouseDepositPacket(houseID int64, amount int64, status int64, newBalance int64, newStatusBalance int64) ([]byte, error) { packet := make([]byte, 0, 64) // Packet type identifier packet = append(packet, 0x07) // House Deposit packet type // House ID (8 bytes) houseIDBytes := make([]byte, 8) binary.LittleEndian.PutUint64(houseIDBytes, uint64(houseID)) packet = append(packet, houseIDBytes...) // Deposit amount (8 bytes) amountBytes := make([]byte, 8) binary.LittleEndian.PutUint64(amountBytes, uint64(amount)) packet = append(packet, amountBytes...) // Status deposit (8 bytes) statusBytes := make([]byte, 8) binary.LittleEndian.PutUint64(statusBytes, uint64(status)) packet = append(packet, statusBytes...) // New coin balance (8 bytes) newBalanceBytes := make([]byte, 8) binary.LittleEndian.PutUint64(newBalanceBytes, uint64(newBalance)) packet = append(packet, newBalanceBytes...) // New status balance (8 bytes) newStatusBalanceBytes := make([]byte, 8) binary.LittleEndian.PutUint64(newStatusBalanceBytes, uint64(newStatusBalance)) packet = append(packet, newStatusBalanceBytes...) return packet, nil } // BuildHouseWithdrawalPacket builds a withdrawal confirmation packet func (hpb *HousingPacketBuilder) BuildHouseWithdrawalPacket(houseID int64, amount int64, status int64, newBalance int64, newStatusBalance int64) ([]byte, error) { packet := make([]byte, 0, 64) // Packet type identifier packet = append(packet, 0x08) // House Withdrawal packet type // House ID (8 bytes) houseIDBytes := make([]byte, 8) binary.LittleEndian.PutUint64(houseIDBytes, uint64(houseID)) packet = append(packet, houseIDBytes...) // Withdrawal amount (8 bytes) amountBytes := make([]byte, 8) binary.LittleEndian.PutUint64(amountBytes, uint64(amount)) packet = append(packet, amountBytes...) // Status withdrawal (8 bytes) statusBytes := make([]byte, 8) binary.LittleEndian.PutUint64(statusBytes, uint64(status)) packet = append(packet, statusBytes...) // New coin balance (8 bytes) newBalanceBytes := make([]byte, 8) binary.LittleEndian.PutUint64(newBalanceBytes, uint64(newBalance)) packet = append(packet, newBalanceBytes...) // New status balance (8 bytes) newStatusBalanceBytes := make([]byte, 8) binary.LittleEndian.PutUint64(newStatusBalanceBytes, uint64(newStatusBalance)) packet = append(packet, newStatusBalanceBytes...) return packet, nil } // BuildItemPlacementPacket builds an item placement response packet func (hpb *HousingPacketBuilder) BuildItemPlacementPacket(item *HouseItem, success bool) ([]byte, error) { if item == nil { return nil, fmt.Errorf("house item is nil") } packet := make([]byte, 0, 128) // Packet type identifier packet = append(packet, 0x09) // Item Placement packet type // Item ID (8 bytes) itemIDBytes := make([]byte, 8) binary.LittleEndian.PutUint64(itemIDBytes, uint64(item.ID)) packet = append(packet, itemIDBytes...) // Success flag (1 byte) if success { packet = append(packet, 0x01) } else { packet = append(packet, 0x00) } if success { // Position (12 bytes - 3 floats) xBytes := make([]byte, 4) binary.LittleEndian.PutUint32(xBytes, math.Float32bits(item.X)) packet = append(packet, xBytes...) yBytes := make([]byte, 4) binary.LittleEndian.PutUint32(yBytes, math.Float32bits(item.Y)) packet = append(packet, yBytes...) zBytes := make([]byte, 4) binary.LittleEndian.PutUint32(zBytes, math.Float32bits(item.Z)) packet = append(packet, zBytes...) // Heading (4 bytes) headingBytes := make([]byte, 4) binary.LittleEndian.PutUint32(headingBytes, math.Float32bits(item.Heading)) packet = append(packet, headingBytes...) // Pitch/Roll (16 bytes - 4 floats) pitchXBytes := make([]byte, 4) binary.LittleEndian.PutUint32(pitchXBytes, math.Float32bits(item.PitchX)) packet = append(packet, pitchXBytes...) pitchYBytes := make([]byte, 4) binary.LittleEndian.PutUint32(pitchYBytes, math.Float32bits(item.PitchY)) packet = append(packet, pitchYBytes...) rollXBytes := make([]byte, 4) binary.LittleEndian.PutUint32(rollXBytes, math.Float32bits(item.RollX)) packet = append(packet, rollXBytes...) rollYBytes := make([]byte, 4) binary.LittleEndian.PutUint32(rollYBytes, math.Float32bits(item.RollY)) packet = append(packet, rollYBytes...) } return packet, nil } // BuildAccessUpdatePacket builds an access permission update packet func (hpb *HousingPacketBuilder) BuildAccessUpdatePacket(houseID int64, access []HouseAccess) ([]byte, error) { packet := make([]byte, 0, 1024) // Packet type identifier packet = append(packet, 0x0A) // Access Update packet type // House ID (8 bytes) houseIDBytes := make([]byte, 8) binary.LittleEndian.PutUint64(houseIDBytes, uint64(houseID)) packet = append(packet, houseIDBytes...) // Access entry count (4 bytes) countBytes := make([]byte, 4) binary.LittleEndian.PutUint32(countBytes, uint32(len(access))) packet = append(packet, countBytes...) // Access entries for _, entry := range access { // Character ID (4 bytes) charIDBytes := make([]byte, 4) binary.LittleEndian.PutUint32(charIDBytes, uint32(entry.CharacterID)) packet = append(packet, charIDBytes...) // Player name nameBytes := []byte(entry.PlayerName) packet = append(packet, byte(len(nameBytes))) packet = append(packet, nameBytes...) // Access level (1 byte) packet = append(packet, byte(entry.AccessLevel)) // Permissions (4 bytes) permBytes := make([]byte, 4) binary.LittleEndian.PutUint32(permBytes, uint32(entry.Permissions)) packet = append(packet, permBytes...) // Granted by (4 bytes) grantedByBytes := make([]byte, 4) binary.LittleEndian.PutUint32(grantedByBytes, uint32(entry.GrantedBy)) packet = append(packet, grantedByBytes...) // Granted date (8 bytes) grantedDateBytes := make([]byte, 8) binary.LittleEndian.PutUint64(grantedDateBytes, uint64(entry.GrantedDate.Unix())) packet = append(packet, grantedDateBytes...) // Expires date (8 bytes) expiresDateBytes := make([]byte, 8) binary.LittleEndian.PutUint64(expiresDateBytes, uint64(entry.ExpiresDate.Unix())) packet = append(packet, expiresDateBytes...) // Notes notesBytes := []byte(entry.Notes) notesLen := make([]byte, 2) binary.LittleEndian.PutUint16(notesLen, uint16(len(notesBytes))) packet = append(packet, notesLen...) packet = append(packet, notesBytes...) } return packet, nil } // Helper methods // appendHouseSettings appends house settings to a packet func (hpb *HousingPacketBuilder) appendHouseSettings(packet []byte, settings HouseSettings) []byte { // House name nameBytes := []byte(settings.HouseName) packet = append(packet, byte(len(nameBytes))) packet = append(packet, nameBytes...) // Visit permission (1 byte) packet = append(packet, byte(settings.VisitPermission)) // Public note publicNoteBytes := []byte(settings.PublicNote) publicNoteLen := make([]byte, 2) binary.LittleEndian.PutUint16(publicNoteLen, uint16(len(publicNoteBytes))) packet = append(packet, publicNoteLen...) packet = append(packet, publicNoteBytes...) // Private note privateNoteBytes := []byte(settings.PrivateNote) privateNoteLen := make([]byte, 2) binary.LittleEndian.PutUint16(privateNoteLen, uint16(len(privateNoteBytes))) packet = append(packet, privateNoteLen...) packet = append(packet, privateNoteBytes...) // Boolean flags (6 bytes) flags := []bool{ settings.AllowFriends, settings.AllowGuild, settings.RequireApproval, settings.ShowOnDirectory, settings.AllowDecoration, settings.TaxExempt, } for _, flag := range flags { if flag { packet = append(packet, 0x01) } else { packet = append(packet, 0x00) } } return packet } // ValidatePacketSize checks if packet size is within acceptable limits func (hpb *HousingPacketBuilder) ValidatePacketSize(packet []byte) error { maxSize := hpb.getMaxPacketSize() if len(packet) > maxSize { return fmt.Errorf("packet size %d exceeds maximum %d", len(packet), maxSize) } return nil } // getMaxPacketSize returns the maximum packet size for the client version func (hpb *HousingPacketBuilder) getMaxPacketSize() int { if hpb.clientVersion >= 564 { return 4096 // Newer clients support larger packets } return 2048 // Older clients have smaller limits } // GetPacketTypeDescription returns human-readable packet type description func (hpb *HousingPacketBuilder) GetPacketTypeDescription(packetType byte) string { switch packetType { case 0x01: return "House Purchase" case 0x02: return "Housing List" case 0x03: return "Base House Window" case 0x04: return "House Visit" case 0x05: return "House Update" case 0x06: return "House Error" case 0x07: return "House Deposit" case 0x08: return "House Withdrawal" case 0x09: return "Item Placement" case 0x0A: return "Access Update" default: return "Unknown" } } // Client version specific methods // IsVersionSupported checks if client version supports specific features func (hpb *HousingPacketBuilder) IsVersionSupported(feature string) bool { switch feature { case "extended_access": return hpb.clientVersion >= 546 case "amenity_management": return hpb.clientVersion >= 564 case "item_rotation": return hpb.clientVersion >= 572 case "house_search": return hpb.clientVersion >= 580 default: return true // Basic features supported in all versions } } // Error codes for housing system const ( HouseErrorNone = iota HouseErrorInsufficientFunds HouseErrorInsufficientStatus HouseErrorAccessDenied HouseErrorHouseNotFound HouseErrorAlignmentRestriction HouseErrorGuildLevelRestriction HouseErrorUpkeepOverdue HouseErrorMaxHousesReached HouseErrorInvalidPlacement HouseErrorItemNotFound HouseErrorSystemDisabled ) // getHousingErrorMessage returns human-readable error message for error code func getHousingErrorMessage(errorCode int) string { switch errorCode { case HouseErrorNone: return "No error" case HouseErrorInsufficientFunds: return "Insufficient funds" case HouseErrorInsufficientStatus: return "Insufficient status points" case HouseErrorAccessDenied: return "Access denied" case HouseErrorHouseNotFound: return "House not found" case HouseErrorAlignmentRestriction: return "Alignment requirement not met" case HouseErrorGuildLevelRestriction: return "Guild level requirement not met" case HouseErrorUpkeepOverdue: return "House upkeep is overdue" case HouseErrorMaxHousesReached: return "Maximum number of houses reached" case HouseErrorInvalidPlacement: return "Invalid item placement" case HouseErrorItemNotFound: return "Item not found" case HouseErrorSystemDisabled: return "Housing system is disabled" default: return "Unknown error" } } // Packet parsing helper methods for incoming packets // ParseBuyHousePacket parses an incoming buy house request func (hpb *HousingPacketBuilder) ParseBuyHousePacket(data []byte) (int32, error) { if len(data) < 4 { return 0, fmt.Errorf("packet too short for buy house request") } // Extract house ID (4 bytes) houseID := int32(binary.LittleEndian.Uint32(data[0:4])) return houseID, nil } // ParseEnterHousePacket parses an incoming enter house request func (hpb *HousingPacketBuilder) ParseEnterHousePacket(data []byte) (int64, error) { if len(data) < 8 { return 0, fmt.Errorf("packet too short for enter house request") } // Extract unique house ID (8 bytes) uniqueID := int64(binary.LittleEndian.Uint64(data[0:8])) return uniqueID, nil } // ParseDepositPacket parses an incoming deposit request func (hpb *HousingPacketBuilder) ParseDepositPacket(data []byte) (int64, int64, int64, error) { if len(data) < 24 { return 0, 0, 0, fmt.Errorf("packet too short for deposit request") } // Extract house ID (8 bytes) houseID := int64(binary.LittleEndian.Uint64(data[0:8])) // Extract coin amount (8 bytes) coinAmount := int64(binary.LittleEndian.Uint64(data[8:16])) // Extract status amount (8 bytes) statusAmount := int64(binary.LittleEndian.Uint64(data[16:24])) return houseID, coinAmount, statusAmount, nil } // Time formatting helpers for display // FormatUpkeepDue formats upkeep due date for display func FormatUpkeepDue(upkeepDue time.Time) string { now := time.Now() if upkeepDue.Before(now) { duration := now.Sub(upkeepDue) days := int(duration.Hours() / 24) if days == 0 { return "Overdue (today)" } return fmt.Sprintf("Overdue (%d days)", days) } else { duration := upkeepDue.Sub(now) days := int(duration.Hours() / 24) if days == 0 { return "Due today" } return fmt.Sprintf("Due in %d days", days) } } // FormatCurrency formats currency amounts for display func FormatCurrency(amount int64) string { if amount < 0 { return fmt.Sprintf("-%s", FormatCurrency(-amount)) } if amount >= 10000 { // 1 gold = 10000 copper gold := amount / 10000 remainder := amount % 10000 if remainder == 0 { return fmt.Sprintf("%dg", gold) } else { silver := remainder / 100 copper := remainder % 100 if copper == 0 { return fmt.Sprintf("%dg %ds", gold, silver) } else { return fmt.Sprintf("%dg %ds %dc", gold, silver, copper) } } } else if amount >= 100 { silver := amount / 100 copper := amount % 100 if copper == 0 { return fmt.Sprintf("%ds", silver) } else { return fmt.Sprintf("%ds %dc", silver, copper) } } else { return fmt.Sprintf("%dc", amount) } }