implement comma separated rules for packet parser
This commit is contained in:
parent
fcb6a82135
commit
35fc2b667b
@ -68,6 +68,37 @@ StatTypes []uint8 `eq2:"int8,len=5"`
|
|||||||
StatValues []any `eq2:"int32,type2=float,type2criteria=stat_type_%i!=6"`
|
StatValues []any `eq2:"int32,type2=float,type2criteria=stat_type_%i!=6"`
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Comma-separated conditions
|
||||||
|
Multiple variables can be checked in a single condition using comma-separated lists:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Parse only if NONE of the listed variables are set
|
||||||
|
NumEffects uint8 `eq2:"int8,ifvariablenotset=header_info_header_unknown_0_0,header_unknown_0"`
|
||||||
|
|
||||||
|
// Parse if ANY of the listed variables are set
|
||||||
|
BonusData []byte `eq2:"char,len=20,ifvariableset=has_bonus,has_special,has_extra"`
|
||||||
|
|
||||||
|
// Parse if ANY of the listed flags are set
|
||||||
|
OptionalField uint32 `eq2:"int32,ifflag=debug_mode,test_mode,dev_mode"`
|
||||||
|
|
||||||
|
// Parse if ALL of the listed flags are NOT set
|
||||||
|
ProductionData []byte `eq2:"char,len=50,ifflagnotset=debug_mode,test_mode"`
|
||||||
|
|
||||||
|
// Multiple equals conditions (ANY must be true)
|
||||||
|
TypeData any `eq2:"int32,ifequals=type=1,category=special"`
|
||||||
|
|
||||||
|
// Multiple not-equals conditions (ALL must be true)
|
||||||
|
Value uint16 `eq2:"int16,ifnotequals=status=disabled,flag=hidden"`
|
||||||
|
```
|
||||||
|
|
||||||
|
**Comma-separated logic rules:**
|
||||||
|
- `ifvariableset` - TRUE if ANY variable is set
|
||||||
|
- `ifvariablenotset` - TRUE if ALL variables are NOT set
|
||||||
|
- `ifflag` - TRUE if ANY flag is set
|
||||||
|
- `ifflagnotset` - TRUE if ALL flags are NOT set
|
||||||
|
- `ifequals` - TRUE if ANY condition matches
|
||||||
|
- `ifnotequals` - TRUE if ALL conditions are true (none match)
|
||||||
|
|
||||||
### Type switching (when EQ2 reuses the same bytes for different things)
|
### Type switching (when EQ2 reuses the same bytes for different things)
|
||||||
```go
|
```go
|
||||||
// Normally parse as int32, but if StatType != 6, parse as float instead
|
// Normally parse as int32, but if StatType != 6, parse as float instead
|
||||||
@ -135,6 +166,12 @@ type CharacterData struct {
|
|||||||
Buffs []BuffData `eq2:"array,arraysize=BuffCount"`
|
Buffs []BuffData `eq2:"array,arraysize=BuffCount"`
|
||||||
// Only parse extended data if first buff exists
|
// Only parse extended data if first buff exists
|
||||||
ExtendedBuffData []byte `eq2:"char,len=20,ifvariableset=buffs_0"`
|
ExtendedBuffData []byte `eq2:"char,len=20,ifvariableset=buffs_0"`
|
||||||
|
|
||||||
|
// Comma-separated conditions example
|
||||||
|
HeaderFlags uint8 `eq2:"int8"`
|
||||||
|
// Parse effects only if neither unknown field is set
|
||||||
|
NumEffects uint8 `eq2:"int8,ifvariablenotset=header_info_header_unknown_0_0,header_unknown_0"`
|
||||||
|
Effects []EffectData `eq2:"array,arraysize=NumEffects"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type InventoryItem struct {
|
type InventoryItem struct {
|
||||||
@ -157,6 +194,11 @@ type BuffData struct {
|
|||||||
BuffID uint32 `eq2:"int32"`
|
BuffID uint32 `eq2:"int32"`
|
||||||
Duration uint16 `eq2:"int16"`
|
Duration uint16 `eq2:"int16"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type EffectData struct {
|
||||||
|
Effect common.EQ2String16 `eq2:"string16"`
|
||||||
|
Percentage uint8 `eq2:"int8"`
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Advanced conditional patterns
|
## Advanced conditional patterns
|
||||||
@ -179,6 +221,14 @@ type ComplexPacket struct {
|
|||||||
PlayerName string `eq2:"string16"`
|
PlayerName string `eq2:"string16"`
|
||||||
ShortName string `eq2:"string8,if=player_name!<=8"`
|
ShortName string `eq2:"string8,if=player_name!<=8"`
|
||||||
LongDesc string `eq2:"string32,if=player_name!>15"`
|
LongDesc string `eq2:"string32,if=player_name!>15"`
|
||||||
|
|
||||||
|
// Comma-separated multi-condition examples
|
||||||
|
DebugInfo []byte `eq2:"char,len=100,ifflag=debug_mode,test_mode,dev_mode"`
|
||||||
|
ProdData []byte `eq2:"char,len=50,ifflagnotset=debug_mode,test_mode,dev_mode"`
|
||||||
|
|
||||||
|
// Multiple variable checks
|
||||||
|
OptionalData []byte `eq2:"char,len=20,ifvariableset=has_optional,has_extended"`
|
||||||
|
CleanupData []byte `eq2:"char,len=10,ifvariablenotset=dirty_flag,temp_flag,cache_flag"`
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -191,6 +241,8 @@ If you've got EQ2's XML packet definitions, the conversion is pretty straightfor
|
|||||||
| `Type="int32"` | `eq2:"int32"` |
|
| `Type="int32"` | `eq2:"int32"` |
|
||||||
| `ArraySizeVariable="count"` | `arraysize=Count` |
|
| `ArraySizeVariable="count"` | `arraysize=Count` |
|
||||||
| `IfVariableSet="flag"` | `ifvariableset=Flag` |
|
| `IfVariableSet="flag"` | `ifvariableset=Flag` |
|
||||||
|
| `IfVariableNotSet="var1,var2"` | `ifvariablenotset=var1,var2` |
|
||||||
|
| `IfFlag="flag1,flag2"` | `ifflag=flag1,flag2` |
|
||||||
| `Size="5"` | `len=5` |
|
| `Size="5"` | `len=5` |
|
||||||
| `Type2Criteria="field!=value"` | `type2criteria=Field!=value` |
|
| `Type2Criteria="field!=value"` | `type2criteria=Field!=value` |
|
||||||
| `Type2Criteria="name!>10"` | `type2criteria=name!>10` |
|
| `Type2Criteria="name!>10"` | `type2criteria=name!>10` |
|
||||||
|
@ -12,32 +12,30 @@ func (p *Parser) evaluateAllConditions(fieldTag *FieldTag, _ reflect.Value) bool
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if fieldTag.IfVariableSet != "" && !p.isVariableSet(fieldTag.IfVariableSet) {
|
if fieldTag.IfVariableSet != "" && !p.evaluateVariableSetCondition(fieldTag.IfVariableSet) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if fieldTag.IfVariableNotSet != "" && p.isVariableSet(fieldTag.IfVariableNotSet) {
|
if fieldTag.IfVariableNotSet != "" && !p.evaluateVariableNotSetCondition(fieldTag.IfVariableNotSet) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if fieldTag.IfFlag != "" && !p.flags[fieldTag.IfFlag] {
|
if fieldTag.IfFlag != "" && !p.evaluateFlagCondition(fieldTag.IfFlag) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if fieldTag.IfFlagNotSet != "" && p.flags[fieldTag.IfFlagNotSet] {
|
if fieldTag.IfFlagNotSet != "" && !p.evaluateFlagNotSetCondition(fieldTag.IfFlagNotSet) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if fieldTag.IfEquals != "" {
|
if fieldTag.IfEquals != "" {
|
||||||
parts := strings.Split(fieldTag.IfEquals, "=")
|
if !p.evaluateEqualsConditions(fieldTag.IfEquals) {
|
||||||
if len(parts) == 2 && !p.evaluateEqualsCondition(parts[0], parts[1]) {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if fieldTag.IfNotEquals != "" {
|
if fieldTag.IfNotEquals != "" {
|
||||||
parts := strings.Split(fieldTag.IfNotEquals, "=")
|
if !p.evaluateNotEqualsConditions(fieldTag.IfNotEquals) {
|
||||||
if len(parts) == 2 && p.evaluateEqualsCondition(parts[0], parts[1]) {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -45,6 +43,96 @@ func (p *Parser) evaluateAllConditions(fieldTag *FieldTag, _ reflect.Value) bool
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// evaluateVariableSetCondition handles comma-separated variable set conditions
|
||||||
|
func (p *Parser) evaluateVariableSetCondition(variables string) bool {
|
||||||
|
varList := p.splitCommaSeparated(variables)
|
||||||
|
// Return true if ANY variable is set
|
||||||
|
for _, variable := range varList {
|
||||||
|
if p.isVariableSet(variable) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// evaluateVariableNotSetCondition handles comma-separated variable not set conditions
|
||||||
|
func (p *Parser) evaluateVariableNotSetCondition(variables string) bool {
|
||||||
|
varList := p.splitCommaSeparated(variables)
|
||||||
|
// Return true if ALL variables are not set
|
||||||
|
for _, variable := range varList {
|
||||||
|
if p.isVariableSet(variable) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// evaluateFlagCondition handles comma-separated flag conditions
|
||||||
|
func (p *Parser) evaluateFlagCondition(flags string) bool {
|
||||||
|
flagList := p.splitCommaSeparated(flags)
|
||||||
|
// Return true if ANY flag is set
|
||||||
|
for _, flag := range flagList {
|
||||||
|
if p.flags[flag] {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// evaluateFlagNotSetCondition handles comma-separated flag not set conditions
|
||||||
|
func (p *Parser) evaluateFlagNotSetCondition(flags string) bool {
|
||||||
|
flagList := p.splitCommaSeparated(flags)
|
||||||
|
// Return true if ALL flags are not set
|
||||||
|
for _, flag := range flagList {
|
||||||
|
if p.flags[flag] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// evaluateEqualsConditions handles comma-separated equals conditions
|
||||||
|
func (p *Parser) evaluateEqualsConditions(conditions string) bool {
|
||||||
|
conditionList := p.splitCommaSeparated(conditions)
|
||||||
|
// Return true if ANY condition is true
|
||||||
|
for _, condition := range conditionList {
|
||||||
|
parts := strings.Split(condition, "=")
|
||||||
|
if len(parts) == 2 && p.evaluateEqualsCondition(strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1])) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// evaluateNotEqualsConditions handles comma-separated not equals conditions
|
||||||
|
func (p *Parser) evaluateNotEqualsConditions(conditions string) bool {
|
||||||
|
conditionList := p.splitCommaSeparated(conditions)
|
||||||
|
// Return true if ALL conditions are true (all not equal)
|
||||||
|
for _, condition := range conditionList {
|
||||||
|
parts := strings.Split(condition, "=")
|
||||||
|
if len(parts) == 2 {
|
||||||
|
if p.evaluateEqualsCondition(strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1])) {
|
||||||
|
return false // This condition is false (they are equal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// splitCommaSeparated splits a comma-separated string and trims whitespace
|
||||||
|
func (p *Parser) splitCommaSeparated(input string) []string {
|
||||||
|
if input == "" {
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.Split(input, ",")
|
||||||
|
result := make([]string, len(parts))
|
||||||
|
for i, part := range parts {
|
||||||
|
result[i] = strings.TrimSpace(part)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
// evaluateCondition evaluates conditions for conditional fields
|
// evaluateCondition evaluates conditions for conditional fields
|
||||||
func (p *Parser) evaluateCondition(condition *FieldCondition) bool {
|
func (p *Parser) evaluateCondition(condition *FieldCondition) bool {
|
||||||
if condition.Type == "simple" {
|
if condition.Type == "simple" {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user