Declare SCUM Run data targets

This commit is contained in:
npc0-hue
2026-08-12 17:44:53 +08:00
parent d854d6fda3
commit 2246859984
17 changed files with 235 additions and 10 deletions
@@ -268,6 +268,11 @@ func runAutonomousLifecyclePlan(distribution domain.RunDistribution, instance do
plan.LogSources = append(plan.LogSources, autonomousLogSource(source))
}
}
for _, target := range plugin.RuntimeProfiles.DataTargets {
if runtimePlatformsContain(target.Platforms, distribution.TargetOS) {
plan.DataTargets = append(plan.DataTargets, autonomousDataTarget(target))
}
}
if hasProfile && len(profile.DLLExtensionRefs) > 0 {
endpoint := domain.RunEndpoint{ID: distribution.RunEndpointID, Platform: distribution.TargetOS, Architecture: distribution.TargetArch}
extensions, err := lifecycleDLLExtensionPlans(plugin.RuntimeProfiles, profile, endpoint)
@@ -326,6 +331,10 @@ func autonomousDLLExtension(extension domain.RuntimeDLLExtensionPlan) domain.Run
return domain.RunAutonomousDLLExtension{Key: extension.Key, Version: extension.Version, ReleaseURL: extension.ReleaseURL, Checksum: extension.Checksum, SizeBytes: extension.SizeBytes, TargetKey: extension.TargetKey, ModKey: extension.ModKey, DLLRef: extension.DLLRef, SCUMExecutableChecksum: extension.SCUMExecutableChecksum, UE4SSABI: extension.UE4SSABI, RCONPort: extension.RCONPort}
}
func autonomousDataTarget(target domain.RuntimeDataTarget) domain.RunAutonomousDataTarget {
return domain.RunAutonomousDataTarget{Key: target.Key, Kind: target.Kind, TransportKey: target.TransportKey, SourceRootKey: target.SourceRootKey, SourcePath: target.SourcePath, WorkspaceKey: target.WorkspaceKey, RefreshPolicy: target.RefreshPolicy, MaxBytes: target.MaxBytes, Platforms: domain.CopyStringSlice(target.Platforms)}
}
func autonomousDeploymentFromDefinition(definition domain.ServerDeploymentDefinition, profileKey string, bindings map[string]string) *domain.RunAutonomousDeployment {
if definition.Mode == "" {
return nil
@@ -45,6 +45,13 @@ func TestCoreServiceKeepsPlatformBuildKeyOffMachineJobChannel(t *testing.T) {
domain.RuntimeLogSource{Key: "console", Kind: "process.stdout", TargetKey: "server/process", StreamKey: "console", CursorKind: "sequence", RetentionDays: 14},
domain.RuntimeLogSource{Key: "server-events", Kind: "file.tail", TargetKey: "logs/server", StreamKey: "scum.server", CursorKind: "fingerprint", RetentionDays: 90},
)
plugin.RuntimeProfiles.TransportProfiles = append(plugin.RuntimeProfiles.TransportProfiles,
domain.RuntimeTransportProfile{Key: "server-files", Kind: "file", TargetKey: "server-root", Capabilities: []string{domain.JobCapabilityRemoteRunFilesRead}},
domain.RuntimeTransportProfile{Key: "world-db", Kind: "sqlite", TargetKey: "world-db", Capabilities: []string{domain.JobCapabilityRemoteRunDBSQLiteProbe}},
)
plugin.RuntimeProfiles.DataTargets = append(plugin.RuntimeProfiles.DataTargets,
domain.RuntimeDataTarget{Key: "world-db", Kind: "sqlite.snapshot", TransportKey: "world-db", SourceRootKey: "server-root", SourcePath: "world/current.db", WorkspaceKey: "databases/world-db", RefreshPolicy: "on-demand-snapshot", MaxBytes: 128 * 1024 * 1024, Platforms: []string{"linux"}},
)
if err := svc.store.GamePlugins().Update(plugin); err != nil {
t.Fatalf("seed plugin lifecycle assets: %v", err)
}
@@ -90,14 +97,14 @@ func TestCoreServiceKeepsPlatformBuildKeyOffMachineJobChannel(t *testing.T) {
if plan == nil || plan.SchemaVersion != "1" || plan.ServerInstanceID != instance.ID || plan.PluginID != plugin.ID || plan.ProfileKey != "local" || plan.Bootstrap == nil || plan.Bootstrap.Action != domain.ServerLifecycleActionStart || plan.Bootstrap.TargetKey != "actions/start.json" {
t.Fatalf("platform builder received incomplete autonomous lifecycle plan: %+v", plan)
}
if len(plan.DependencyProbes) != 1 || plan.DependencyProbes[0].Key != "java-runtime" || len(plan.InstallPlans) != 1 || plan.InstallPlans[0].Key != "java-install" || len(plan.LogSources) != 3 || !hasAutonomousLogSource(plan.LogSources, "process.stdout", "console") || !hasAutonomousLogSource(plan.LogSources, "file.tail", "latest-log") || !hasAutonomousLogSource(plan.LogSources, "file.tail", "scum.server") || plan.RuntimeBindings["logs/latest"] != "runtime.logs.latest" {
if len(plan.DependencyProbes) != 1 || plan.DependencyProbes[0].Key != "java-runtime" || len(plan.InstallPlans) != 1 || plan.InstallPlans[0].Key != "java-install" || len(plan.LogSources) != 3 || !hasAutonomousLogSource(plan.LogSources, "process.stdout", "console") || !hasAutonomousLogSource(plan.LogSources, "file.tail", "latest-log") || !hasAutonomousLogSource(plan.LogSources, "file.tail", "scum.server") || len(plan.DataTargets) != 1 || plan.DataTargets[0].WorkspaceKey != "databases/world-db" || plan.DataTargets[0].SourcePath != "world/current.db" || plan.RuntimeBindings["logs/latest"] != "runtime.logs.latest" {
t.Fatalf("autonomous lifecycle plan lost plugin runtime declarations: %+v", plan)
}
var seededPlan domain.RunAutonomousLifecyclePlan
if err := json.Unmarshal([]byte(seedFiles[2].Content), &seededPlan); err != nil {
t.Fatalf("unmarshal seeded autonomous lifecycle plan: %v", err)
}
if seededPlan.ServerInstanceID != plan.ServerInstanceID || seededPlan.Bootstrap == nil || seededPlan.Bootstrap.TargetKey != plan.Bootstrap.TargetKey {
if seededPlan.ServerInstanceID != plan.ServerInstanceID || seededPlan.Bootstrap == nil || seededPlan.Bootstrap.TargetKey != plan.Bootstrap.TargetKey || len(seededPlan.DataTargets) != 1 || seededPlan.DataTargets[0].WorkspaceKey != "databases/world-db" {
t.Fatalf("seeded lifecycle plan differs from build input: seed=%+v input=%+v", seededPlan, plan)
}
auth, err := svc.AuthenticateComponent(domain.ComponentAuthenticationRequest{
+5 -1
View File
@@ -95,7 +95,11 @@ func TestSCUMSchemaProbeDispatchIsPlatformScheduledAndFenced(t *testing.T) {
plugin.DeclaredPermissions = append(plugin.DeclaredPermissions, "server.remote.access")
plugin.RequiredRunCapabilities = append(plugin.RequiredRunCapabilities, domain.JobCapabilityRemoteRunDBSQLiteProbe)
plugin.RemoteAccess = domain.GamePluginRemoteAccess{Methods: []string{"run"}, RunCapabilities: []string{domain.JobCapabilityRemoteRunDBSQLiteProbe}, DatabaseEngines: []string{"sqlite"}}
plugin.RuntimeProfiles.TransportProfiles = append(plugin.RuntimeProfiles.TransportProfiles, domain.RuntimeTransportProfile{Key: "scum-database", Kind: "sqlite", TargetKey: "scum-database", Capabilities: []string{domain.JobCapabilityRemoteRunDBSQLiteProbe}})
plugin.RuntimeProfiles.TransportProfiles = append(plugin.RuntimeProfiles.TransportProfiles,
domain.RuntimeTransportProfile{Key: "server-files", Kind: "file", TargetKey: "server-root", Capabilities: []string{domain.JobCapabilityRemoteRunFilesRead}},
domain.RuntimeTransportProfile{Key: "scum-database", Kind: "sqlite", TargetKey: "scum-database", Capabilities: []string{domain.JobCapabilityRemoteRunDBSQLiteProbe}},
)
plugin.RuntimeProfiles.DataTargets = append(plugin.RuntimeProfiles.DataTargets, domain.RuntimeDataTarget{Key: "scum-database", Kind: "sqlite.snapshot", TransportKey: "scum-database", SourceRootKey: "server-root", SourcePath: "SCUM/Saved/SaveFiles/SCUM.db", WorkspaceKey: "databases/scum-database", RefreshPolicy: "on-demand-snapshot", MaxBytes: 1024 * 1024 * 1024, Platforms: []string{"windows"}})
plugin.RuntimeProfiles.LifecycleProfiles[0].TransportKeys = append(plugin.RuntimeProfiles.LifecycleProfiles[0].TransportKeys, "scum-database")
plugin.SCUMLiveData = domain.SCUMLiveDataManifest{SchemaVersion: "1", Probe: domain.SCUMSchemaProbeDeclaration{Capability: domain.JobCapabilityRemoteRunDBSQLiteProbe, TargetKey: "scum-database", Bounds: domain.DefaultSCUMSchemaProbeBounds()}, CapabilityGates: []domain.SCUMLiveDataCapabilityGateDeclaration{{Capability: domain.SCUMDataCapabilitySchemaProbe, Gate: domain.SCUMCapabilityGateDisabled, AdapterVersion: "scum-live-data-v0", EvidenceStatus: domain.SCUMCapabilityEvidenceMissing, SafeReason: "waiting for current service evidence"}}}
if err := svc.store.GamePlugins().Update(plugin); err != nil {
+13
View File
@@ -28,6 +28,9 @@ func (svc *CoreService) RequestSCUMSchemaProbeForSession(sessionID, serverInstan
if plugin.SCUMLiveData.SchemaVersion == "" || probe.Capability != domain.JobCapabilityRemoteRunDBSQLiteProbe || strings.TrimSpace(probe.TargetKey) == "" {
return domain.SCUMSchemaProbeRequest{}, domain.RemoteAdapterResult{}, validationError("SCUM schema probe is not declared by the plugin")
}
if !scumSchemaProbeHasDataTarget(plugin.RuntimeProfiles, probe.TargetKey) {
return domain.SCUMSchemaProbeRequest{}, domain.RemoteAdapterResult{}, validationError("SCUM schema probe target is not declared as a generated Run data target")
}
endpoint, err := svc.store.RunEndpoints().Get(instance.RunEndpointID)
if err != nil {
return domain.SCUMSchemaProbeRequest{}, domain.RemoteAdapterResult{}, err
@@ -94,6 +97,16 @@ func scumSchemaProbeDatabaseIdentity(targetKey string) string {
return strings.TrimPrefix(trimmed, "databases/")
}
func scumSchemaProbeHasDataTarget(profiles domain.GamePluginRuntimeProfiles, targetKey string) bool {
expectedWorkspaceKey := "databases/" + scumSchemaProbeDatabaseIdentity(targetKey)
for _, target := range profiles.DataTargets {
if target.Key == targetKey && target.Kind == "sqlite.snapshot" && target.WorkspaceKey == expectedWorkspaceKey && target.RefreshPolicy == "on-demand-snapshot" {
return true
}
}
return false
}
func scumSchemaProbeAdapterVersion(manifest domain.SCUMLiveDataManifest) string {
for _, gate := range manifest.CapabilityGates {
if gate.Capability == domain.SCUMDataCapabilitySchemaProbe {