eq2go/internal/housing/packets.go

890 lines
26 KiB
Go

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