Add SCUM log sessions and trajectory projections

This commit is contained in:
npc0-hue
2026-08-27 12:34:07 +08:00
parent 0940780058
commit 316efbe780
38 changed files with 1549 additions and 84 deletions
@@ -0,0 +1,87 @@
package service
import (
"fmt"
"strings"
"time"
"browser.local/platform/domain"
)
func (svc *CoreService) projectPluginLifecycleState(instance domain.ServerInstance, plugin domain.GamePlugin, capability string, result domain.JobExecutionResult, stamp time.Time) error {
if len(plugin.GameClientBridge.LifecycleProjections) == 0 {
return nil
}
for _, projection := range plugin.GameClientBridge.LifecycleProjections {
if !containsString(projection.Capabilities, capability) || len(projection.Target.FixedValues) == 0 {
continue
}
if len(projection.ProcessStates) > 0 && !containsString(projection.ProcessStates, strings.TrimSpace(result.ProcessState)) {
continue
}
if err := svc.applyPluginBulkProjection(instance, plugin, projection.Target, stamp); err != nil {
return err
}
}
return nil
}
func (svc *CoreService) applyPluginBulkProjection(instance domain.ServerInstance, plugin domain.GamePlugin, target domain.GameClientBridgeBulkProjectionTargetDeclaration, stamp time.Time) error {
rows, err := svc.store.PluginDataRecords().List(domain.PluginDataFilter{PluginID: plugin.ID, ServerInstanceID: instance.ID, Collection: target.Collection})
if err != nil {
return err
}
mutations := make([]domain.PluginDataMutation, 0, len(rows))
activityMutations := []domain.PluginDataMutation{}
for _, row := range rows {
if strings.TrimSpace(fmt.Sprint(row.Value[target.MatchField])) != target.MatchValue {
continue
}
value := mergePluginDataValues(row.Value, pluginBulkProjectionValues(target.FixedValues, stamp, row.Value, target.ObservedAtField))
mutations = append(mutations, domain.PluginDataMutation{Operation: domain.PluginDataMutationPut, Key: row.Key, Value: value})
if target.ActivityTarget != nil {
activity := pluginBulkActivityValue(*target.ActivityTarget, row.Value, stamp)
activityKey, keyErr := pluginDataRowKey(activity, target.ActivityTarget.UpsertKeys)
if keyErr != nil {
return keyErr
}
activityMutations = append(activityMutations, domain.PluginDataMutation{Operation: domain.PluginDataMutationPut, Key: activityKey, Value: activity})
}
}
if len(mutations) > 0 {
if _, err := svc.applyPluginDataTransaction(domain.PluginDataTransaction{PluginID: plugin.ID, ServerInstanceID: instance.ID, Collection: target.Collection, Mutations: mutations}); err != nil {
return err
}
}
if len(activityMutations) > 0 && target.ActivityTarget != nil {
if _, err := svc.applyPluginDataTransaction(domain.PluginDataTransaction{PluginID: plugin.ID, ServerInstanceID: instance.ID, Collection: target.ActivityTarget.Collection, Mutations: activityMutations}); err != nil {
return err
}
}
return nil
}
func pluginBulkProjectionValues(fixedValues map[string]string, stamp time.Time, row map[string]any, observedAtField string) map[string]any {
value := make(map[string]any, len(fixedValues)+1)
for key, fixed := range fixedValues {
value[key] = renderQueryProjectionTemplate(fixed, row)
}
if observedAtField != "" {
value[observedAtField] = stamp.UTC().Format(time.RFC3339Nano)
}
return value
}
func pluginBulkActivityValue(target domain.GameClientBridgeBulkActivityTargetDeclaration, row map[string]any, stamp time.Time) map[string]any {
value := make(map[string]any, len(target.RowMappings)+len(target.FixedValues)+1)
for destination, source := range target.RowMappings {
value[destination] = row[source]
}
for key, fixed := range target.FixedValues {
value[key] = renderQueryProjectionTemplate(fixed, row)
}
if target.ObservedAtField != "" {
value[target.ObservedAtField] = stamp.UTC().Format(time.RFC3339Nano)
}
return value
}