Skip to content

Commit

Permalink
Showing 68 changed files with 3,460 additions and 2,858 deletions.
12 changes: 11 additions & 1 deletion proto/api.proto
Original file line number Diff line number Diff line change
@@ -432,12 +432,22 @@ message SpellStats {
bool has_cast_time = 9; // Whether this spell has a cast time or not.
bool is_friendly = 10; // Whether this spell should be cast on player units
}
message APLValidation {
LogLevel log_level = 1;
string validation = 2;
}
message APLActionStats {
repeated string warnings = 1;
repeated string warnings = 1 [deprecated=true];
repeated APLValidation validations = 2;
}
message UUIDValidations {
UUID uuid = 1;
repeated APLValidation validations = 2;
}
message APLStats {
repeated APLActionStats prepull_actions = 1;
repeated APLActionStats priority_list = 2;
repeated UUIDValidations uuid_validations = 3;
}
message UnitMetadata {
string name = 3;
6 changes: 5 additions & 1 deletion proto/apl.proto
Original file line number Diff line number Diff line change
@@ -82,8 +82,10 @@ message APLAction {
}
}

// NextIndex: 86
// NextIndex: 88
message APLValue {
UUID uuid = 87;

oneof value {
// Operators
APLValueConst const = 1;
@@ -120,6 +122,7 @@ message APLValue {
APLValueCurrentSolarEnergy current_solar_energy = 68;
APLValueCurrentLunarEnergy current_lunar_energy = 69;
APLValueCurrentHolyPower current_holy_power = 75;
APLValueMaxRunicPower max_runic_power = 86;

// Unit values
APLValueUnitIsMoving unit_is_moving = 72;
@@ -435,6 +438,7 @@ message APLValueCurrentRunicPower {}
message APLValueCurrentSolarEnergy {}
message APLValueCurrentLunarEnergy {}
message APLValueCurrentHolyPower {}
message APLValueMaxRunicPower {}

enum APLValueRuneType {
RuneUnknown = 0;
13 changes: 13 additions & 0 deletions proto/common.proto
Original file line number Diff line number Diff line change
@@ -1052,3 +1052,16 @@ enum RotationType {
Aoe = 3;
Custom = 2;
}

message UUID {
string value = 1;
}

// Values are expected to be in increasing order of severity
enum LogLevel {
Information = 0;
Warning = 1;
Error = 2;

Undefined = -1;
}
74 changes: 52 additions & 22 deletions sim/core/apl.go
Original file line number Diff line number Diff line change
@@ -34,29 +34,43 @@ type APLRotation struct {

// Validation warnings that occur during proto parsing.
// We return these back to the user for display in the UI.
curWarnings []string
prepullWarnings [][]string
priorityListWarnings [][]string
curValidations []*proto.APLValidation
prepullValidations [][]*proto.APLValidation
priorityListValidations [][]*proto.APLValidation
uuidValidations map[*proto.UUID][]*proto.APLValidation

// Maps indices in filtered sim lists to indices in configs.
prepullIdxMap []int
priorityListIdxMap []int
}

func (rot *APLRotation) ValidationWarning(message string, vals ...interface{}) {
warning := fmt.Sprintf(message, vals...)
rot.curWarnings = append(rot.curWarnings, warning)
func (rot *APLRotation) ValidationMessage(log_level proto.LogLevel, message string, vals ...interface{}) {
formatted_message := fmt.Sprintf(message, vals...)
rot.curValidations = append(rot.curValidations, &proto.APLValidation{
LogLevel: log_level,
Validation: formatted_message,
})
}

func (rot *APLRotation) ValidationMessageByUUID(uuid *proto.UUID, log_level proto.LogLevel, message string, vals ...interface{}) {
if uuid != nil {
formatted_message := fmt.Sprintf(message, vals...)
rot.uuidValidations[uuid] = append(rot.uuidValidations[uuid], &proto.APLValidation{
LogLevel: log_level,
Validation: formatted_message,
})
}
}

// Invokes the fn function, and attributes all warnings generated during its invocation
// to the provided warningsList.
func (rot *APLRotation) doAndRecordWarnings(warningsList *[]string, isPrepull bool, fn func()) {
func (rot *APLRotation) doAndRecordWarnings(warningsList *[]*proto.APLValidation, isPrepull bool, fn func()) {
rot.parsingPrepull = isPrepull
fn()
if warningsList != nil {
*warningsList = append(*warningsList, rot.curWarnings...)
*warningsList = append(*warningsList, rot.curValidations...)
}
rot.curWarnings = nil
rot.curValidations = nil
rot.parsingPrepull = false
}

@@ -79,21 +93,22 @@ func (unit *Unit) newAPLRotation(config *proto.APLRotation) *APLRotation {
}

rotation := &APLRotation{
unit: unit,
prepullWarnings: make([][]string, len(config.PrepullActions)),
priorityListWarnings: make([][]string, len(config.PriorityList)),
unit: unit,
prepullValidations: make([][]*proto.APLValidation, len(config.PrepullActions)),
priorityListValidations: make([][]*proto.APLValidation, len(config.PriorityList)),
uuidValidations: make(map[*proto.UUID][]*proto.APLValidation),
}

// Parse prepull actions
for i, prepullItem := range config.PrepullActions {
prepullIdx := i // Save to local variable for correct lambda capture behavior
rotation.doAndRecordWarnings(&rotation.prepullWarnings[prepullIdx], true, func() {
rotation.doAndRecordWarnings(&rotation.prepullValidations[prepullIdx], true, func() {
if !prepullItem.Hide {
doAtVal := rotation.newAPLValue(prepullItem.DoAtValue)
if doAtVal != nil {
doAt := doAtVal.GetDuration(nil)
if doAt > 0 {
rotation.ValidationWarning("Invalid time for 'Do At', ignoring this Prepull Action")
rotation.ValidationMessage(proto.LogLevel_Warning, "Invalid time for 'Do At', ignoring this Prepull Action")
} else {
action := rotation.newAPLAction(prepullItem.Action)
if action != nil {
@@ -102,7 +117,7 @@ func (unit *Unit) newAPLRotation(config *proto.APLRotation) *APLRotation {
unit.RegisterPrepullAction(doAt, func(sim *Simulation) {
// Warnings for prepull cast failure are detected by running a fake prepull,
// so this action.Execute needs to record warnings.
rotation.doAndRecordWarnings(&rotation.prepullWarnings[prepullIdx], true, func() {
rotation.doAndRecordWarnings(&rotation.prepullValidations[prepullIdx], true, func() {
action.Execute(sim)
})
})
@@ -115,7 +130,7 @@ func (unit *Unit) newAPLRotation(config *proto.APLRotation) *APLRotation {

// Parse priority list
for i, aplItem := range config.PriorityList {
rotation.doAndRecordWarnings(&rotation.priorityListWarnings[i], false, func() {
rotation.doAndRecordWarnings(&rotation.priorityListValidations[i], false, func() {
if !aplItem.Hide {
action := rotation.newAPLAction(aplItem.Action)
if action != nil {
@@ -128,12 +143,12 @@ func (unit *Unit) newAPLRotation(config *proto.APLRotation) *APLRotation {

// Finalize
for i, action := range rotation.prepullActions {
rotation.doAndRecordWarnings(&rotation.prepullWarnings[rotation.prepullIdxMap[i]], true, func() {
rotation.doAndRecordWarnings(&rotation.prepullValidations[rotation.prepullIdxMap[i]], true, func() {
action.Finalize(rotation)
})
}
for i, action := range rotation.priorityList {
rotation.doAndRecordWarnings(&rotation.priorityListWarnings[rotation.priorityListIdxMap[i]], false, func() {
rotation.doAndRecordWarnings(&rotation.priorityListValidations[rotation.priorityListIdxMap[i]], false, func() {
action.Finalize(rotation)
})
}
@@ -177,19 +192,34 @@ func (unit *Unit) newAPLRotation(config *proto.APLRotation) *APLRotation {
func (rot *APLRotation) getStats() *proto.APLStats {
// Perform one final round of validation after post-finalize effects.
for i, action := range rot.prepullActions {
rot.doAndRecordWarnings(&rot.prepullWarnings[rot.prepullIdxMap[i]], true, func() {
rot.doAndRecordWarnings(&rot.prepullValidations[rot.prepullIdxMap[i]], true, func() {
action.impl.PostFinalize(rot)
})
}
for i, action := range rot.priorityList {
rot.doAndRecordWarnings(&rot.priorityListWarnings[rot.priorityListIdxMap[i]], false, func() {
rot.doAndRecordWarnings(&rot.priorityListValidations[rot.priorityListIdxMap[i]], false, func() {
action.impl.PostFinalize(rot)
})
}

uuidValidationsArr := make([]*proto.UUIDValidations, len(rot.uuidValidations))
i := 0
for uuid, validations := range rot.uuidValidations {
uuidValidationsArr[i] = &proto.UUIDValidations{
Uuid: uuid,
Validations: validations,
}
i++
}

return &proto.APLStats{
PrepullActions: MapSlice(rot.prepullWarnings, func(warnings []string) *proto.APLActionStats { return &proto.APLActionStats{Warnings: warnings} }),
PriorityList: MapSlice(rot.priorityListWarnings, func(warnings []string) *proto.APLActionStats { return &proto.APLActionStats{Warnings: warnings} }),
PrepullActions: MapSlice(rot.prepullValidations, func(validations []*proto.APLValidation) *proto.APLActionStats {
return &proto.APLActionStats{Validations: validations}
}),
PriorityList: MapSlice(rot.priorityListValidations, func(validations []*proto.APLValidation) *proto.APLActionStats {
return &proto.APLActionStats{Validations: validations}
}),
UuidValidations: uuidValidationsArr,
}
}

16 changes: 8 additions & 8 deletions sim/core/apl_actions_casting.go
Original file line number Diff line number Diff line change
@@ -138,7 +138,7 @@ func (rot *APLRotation) newActionMultidot(config *proto.APLActionMultidot) APLAc

maxOverlap := rot.coerceTo(rot.newAPLValue(config.MaxOverlap), proto.APLValueType_ValueTypeDuration)
if maxOverlap == nil {
maxOverlap = rot.newValueConst(&proto.APLValueConst{Val: "0ms"})
maxOverlap = rot.newValueConst(&proto.APLValueConst{Val: "0ms"}, &proto.UUID{Value: ""})
}

maxDots := config.MaxDots
@@ -147,7 +147,7 @@ func (rot *APLRotation) newActionMultidot(config *proto.APLActionMultidot) APLAc
numTargets = int32(len(unit.Env.Raid.AllPlayerUnits))
}
if numTargets < maxDots {
rot.ValidationWarning("Encounter only has %d targets. Using that for Max Dots instead of %d", numTargets, maxDots)
rot.ValidationMessage(proto.LogLevel_Warning, "Encounter only has %d targets. Using that for Max Dots instead of %d", numTargets, maxDots)
maxDots = numTargets
}

@@ -213,13 +213,13 @@ func (rot *APLRotation) newActionMultishield(config *proto.APLActionMultishield)

maxOverlap := rot.coerceTo(rot.newAPLValue(config.MaxOverlap), proto.APLValueType_ValueTypeDuration)
if maxOverlap == nil {
maxOverlap = rot.newValueConst(&proto.APLValueConst{Val: "0ms"})
maxOverlap = rot.newValueConst(&proto.APLValueConst{Val: "0ms"}, &proto.UUID{Value: ""})
}

maxShields := config.MaxShields
numTargets := int32(len(unit.Env.Raid.AllPlayerUnits))
if numTargets < maxShields {
rot.ValidationWarning("Encounter only has %d targets. Using that for Max Shields instead of %d", numTargets, maxShields)
rot.ValidationMessage(proto.LogLevel_Warning, "Encounter only has %d targets. Using that for Max Shields instead of %d", numTargets, maxShields)
maxShields = numTargets
}

@@ -319,13 +319,13 @@ func (action *APLActionCastAllStatBuffCooldowns) String() string {
}
func (action *APLActionCastAllStatBuffCooldowns) PostFinalize(rot *APLRotation) {
if len(action.allSubactions) == 0 {
rot.ValidationWarning("%s will not cast any spells! There are either no major cooldowns buffing the specified stat type(s), or all of them are manually cast in the APL.", action)
rot.ValidationMessage(proto.LogLevel_Warning, "%s will not cast any spells! There are either no major cooldowns buffing the specified stat type(s), or all of them are manually cast in the APL.", action)
} else {
actionIDs := MapSlice(action.allSubactions, func(subaction *APLActionCastSpell) ActionID {
return subaction.spell.ActionID
})

rot.ValidationWarning("%s will cast the following spells: %s", action, StringFromActionIDs(actionIDs))
rot.ValidationMessage(proto.LogLevel_Information, "%s will cast the following spells: %s", action, StringFromActionIDs(actionIDs))
}
}

@@ -364,12 +364,12 @@ func (action *APLActionAutocastOtherCooldowns) String() string {
}
func (action *APLActionAutocastOtherCooldowns) PostFinalize(rot *APLRotation) {
if len(action.character.initialMajorCooldowns) == 0 {
rot.ValidationWarning("%s will not cast any spells! There are either no major cooldowns configured for this character, or all of them are manually cast in the APL.", action)
rot.ValidationMessage(proto.LogLevel_Warning, "%s will not cast any spells! There are either no major cooldowns configured for this character, or all of them are manually cast in the APL.", action)
} else {
actionIDs := MapSlice(action.character.initialMajorCooldowns, func(mcd MajorCooldown) ActionID {
return mcd.Spell.ActionID
})

rot.ValidationWarning("%s will cast the following spells: %s", action, StringFromActionIDs(actionIDs))
rot.ValidationMessage(proto.LogLevel_Information, "%s will cast the following spells: %s", action, StringFromActionIDs(actionIDs))
}
}
4 changes: 2 additions & 2 deletions sim/core/apl_actions_misc.go
Original file line number Diff line number Diff line change
@@ -128,14 +128,14 @@ type APLActionItemSwap struct {

func (rot *APLRotation) newActionItemSwap(config *proto.APLActionItemSwap) APLActionImpl {
if config.SwapSet == proto.APLActionItemSwap_Unknown {
rot.ValidationWarning("Unknown item swap set")
rot.ValidationMessage(proto.LogLevel_Warning, "Unknown item swap set")
return nil
}

character := rot.unit.Env.Raid.GetPlayerFromUnit(rot.unit).GetCharacter()
if !character.ItemSwap.IsEnabled() {
if config.SwapSet != proto.APLActionItemSwap_Main {
rot.ValidationWarning("No swap set configured in Settings.")
rot.ValidationMessage(proto.LogLevel_Warning, "No swap set configured in Settings.")
}
return nil
}
4 changes: 2 additions & 2 deletions sim/core/apl_actions_sequences.go
Original file line number Diff line number Diff line change
@@ -86,7 +86,7 @@ type APLActionResetSequence struct {

func (rot *APLRotation) newActionResetSequence(config *proto.APLActionResetSequence) APLActionImpl {
if config.SequenceName == "" {
rot.ValidationWarning("Reset Sequence must provide a sequence name")
rot.ValidationMessage(proto.LogLevel_Warning, "Reset Sequence must provide a sequence name")
return nil
}
return &APLActionResetSequence{
@@ -100,7 +100,7 @@ func (action *APLActionResetSequence) Finalize(rot *APLRotation) {
return
}
}
rot.ValidationWarning("No sequence with name: '%s'", action.name)
rot.ValidationMessage(proto.LogLevel_Warning, "No sequence with name: '%s'", action.name)
}
func (action *APLActionResetSequence) IsReady(sim *Simulation) bool {
return action.sequence != nil && action.sequence.curIdx != 0
2 changes: 1 addition & 1 deletion sim/core/apl_actions_timing.go
Original file line number Diff line number Diff line change
@@ -121,7 +121,7 @@ func (rot *APLRotation) newActionSchedule(config *proto.APLActionSchedule) APLAc
if durVal, err := time.ParseDuration(strings.TrimSpace(timingStr)); err == nil {
timings[i] = durVal
} else {
rot.ValidationWarning("Invalid duration value '%s'", strings.TrimSpace(timingStr))
rot.ValidationMessage(proto.LogLevel_Warning, "Invalid duration value '%s'", strings.TrimSpace(timingStr))
valid = false
}
}
Loading

0 comments on commit 5fdd72f

Please sign in to comment.