890 lines
26 KiB
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)
|
|
}
|
|
}
|