package cps import ( "fmt" "sort" ) // Compiler compiles packet definitions into optimized structures type Compiler struct { packets map[string]*PacketDef } // NewCompiler creates a compiler with parsed packet definitions func NewCompiler(packets map[string]*PacketDef) *Compiler { return &Compiler{packets: packets} } // Compile compiles a packet structure for a specific client version func (c *Compiler) Compile(packetName string, clientVersion int) (*CompiledStruct, error) { packet, exists := c.packets[packetName] if !exists { return nil, fmt.Errorf("packet %s not found", packetName) } // Find best matching version (highest <= clientVersion) var bestVersion int = -1 for version := range packet.Versions { if version <= clientVersion && version > bestVersion { bestVersion = version } } if bestVersion == -1 { return nil, fmt.Errorf("no compatible version found for %s (client version %d)", packetName, clientVersion) } // Build cascaded field list fields, fieldOrder, err := c.buildCascadedFields(packet, bestVersion) if err != nil { return nil, err } // Resolve positions and create final structure compiledFields, totalSize := c.resolvePositions(fields, fieldOrder) return &CompiledStruct{ Name: packetName, Version: bestVersion, Fields: compiledFields, Size: totalSize, }, nil } // buildCascadedFields builds the final field list by cascading from v1 to target version func (c *Compiler) buildCascadedFields(packet *PacketDef, targetVersion int) (map[string]*FieldDef, []string, error) { result := make(map[string]*FieldDef) var allFieldOrder []string // Get all versions up to target, sorted var versions []int for version := range packet.Versions { if version <= targetVersion { versions = append(versions, version) } } sort.Ints(versions) // Apply each version in order for _, version := range versions { versionDef := packet.Versions[version] // Add new fields in their original order for _, fieldName := range versionDef.FieldOrder { fieldDef := versionDef.Fields[fieldName] if fieldDef.Remove { delete(result, fieldName) // Remove from order list for i, name := range allFieldOrder { if name == fieldName { allFieldOrder = append(allFieldOrder[:i], allFieldOrder[i+1:]...) break } } } else { // Create copy to avoid modifying original newField := *fieldDef // If field exists, preserve position if not explicitly set if existing, exists := result[fieldName]; exists { if newField.Position == -1 && existing.Position >= 0 { newField.Position = existing.Position } if newField.After == "" && existing.After != "" { newField.After = existing.After } if newField.Before == "" && existing.Before != "" { newField.Before = existing.Before } } else { // New field - add to order allFieldOrder = append(allFieldOrder, fieldName) } result[fieldName] = &newField } } } return result, allFieldOrder, nil } // resolvePositions resolves field positions and creates ordered field list func (c *Compiler) resolvePositions(fields map[string]*FieldDef, originalOrder []string) ([]*CompiledField, int) { var compiledFields []*CompiledField var fieldOrder []string // Handle explicitly positioned fields first positioned := make(map[int]string) var maxPos int for name, field := range fields { if field.Position >= 0 { positioned[field.Position] = name if field.Position > maxPos { maxPos = field.Position } } } // Build initial order from positioned fields for i := 0; i <= maxPos; i++ { if name, exists := positioned[i]; exists { fieldOrder = append(fieldOrder, name) } } // Handle @after/@before positioning for _, name := range originalOrder { field := fields[name] if field.Position >= 0 { continue // Already handled } if field.After != "" { // Insert after referenced field for i, existing := range fieldOrder { if existing == field.After { fieldOrder = append(fieldOrder[:i+1], append([]string{name}, fieldOrder[i+1:]...)...) break } } } else if field.Before != "" { // Insert before referenced field for i, existing := range fieldOrder { if existing == field.Before { fieldOrder = append(fieldOrder[:i], append([]string{name}, fieldOrder[i:]...)...) break } } } else { // Append in original order fieldOrder = append(fieldOrder, name) } } // Create compiled fields with calculated offsets offset := 0 dynamic := false for _, name := range fieldOrder { field := fields[name] compiledField := &CompiledField{ Name: name, Type: field.Type, Size: field.Size, Offset: offset, Dynamic: field.Size == -1, } compiledFields = append(compiledFields, compiledField) // Calculate offset for next field if field.Size == -1 { dynamic = true } else if !dynamic { fieldSize := c.getTypeSize(field.Type) if field.Size > 0 { fieldSize *= field.Size } offset += fieldSize } } totalSize := offset if dynamic { totalSize = -1 } return compiledFields, totalSize } // getTypeSize returns the byte size of a data type func (c *Compiler) getTypeSize(t EQ2Type) int { switch t { case TypeInt8: return 1 case TypeInt16: return 2 case TypeInt32, TypeFloat32, TypeColor: return 4 case TypeInt64: return 8 default: return -1 // Variable size } }