130 lines
4.5 KiB
Go
130 lines
4.5 KiB
Go
package repo
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"sort"
|
|
"strings"
|
|
|
|
"browser.local/platform/domain"
|
|
)
|
|
|
|
func normalizeStoreSnapshot(snapshot StoreSnapshot) StoreSnapshot {
|
|
plugins, replacements := latestGamePlugins(snapshot.GamePlugins)
|
|
snapshot.GamePlugins = plugins
|
|
snapshot.ServerInstances = rewriteSnapshotServerInstances(snapshot.ServerInstances, replacements)
|
|
snapshot.RuntimeBindings = rewriteSnapshotRuntimeBindings(snapshot.RuntimeBindings, replacements)
|
|
snapshot.PluginLifecycles = rewriteSnapshotPluginLifecycles(snapshot.PluginLifecycles, replacements)
|
|
snapshot.PluginDataRecords = retainedSnapshotPluginData(snapshot.PluginDataRecords, replacements)
|
|
return snapshot
|
|
}
|
|
|
|
func latestGamePlugins(values []domain.GamePlugin) ([]domain.GamePlugin, map[string]domain.GamePlugin) {
|
|
byIdentity := map[string]domain.GamePlugin{}
|
|
for _, plugin := range values {
|
|
key := domain.GamePluginIdentityKey(plugin)
|
|
if current, exists := byIdentity[key]; !exists || domain.GamePluginIsNewer(plugin, current) {
|
|
byIdentity[key] = plugin
|
|
}
|
|
}
|
|
replacements := map[string]domain.GamePlugin{}
|
|
out := make([]domain.GamePlugin, 0, len(byIdentity))
|
|
for _, plugin := range byIdentity {
|
|
out = append(out, domain.CopyGamePlugin(plugin))
|
|
}
|
|
sort.SliceStable(out, func(left, right int) bool { return out[left].ID < out[right].ID })
|
|
for _, plugin := range values {
|
|
kept, ok := byIdentity[domain.GamePluginIdentityKey(plugin)]
|
|
if ok && plugin.ID != kept.ID {
|
|
replacements[plugin.ID] = kept
|
|
}
|
|
}
|
|
return out, replacements
|
|
}
|
|
|
|
func rewriteSnapshotServerInstances(values []domain.ServerInstance, replacements map[string]domain.GamePlugin) []domain.ServerInstance {
|
|
out := make([]domain.ServerInstance, len(values))
|
|
for index, value := range values {
|
|
if kept, ok := replacements[value.PluginID]; ok {
|
|
value.PluginID = kept.ID
|
|
value.PluginVersion = kept.Version
|
|
}
|
|
out[index] = domain.CopyServerInstance(value)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func rewriteSnapshotRuntimeBindings(values []domain.RuntimeBinding, replacements map[string]domain.GamePlugin) []domain.RuntimeBinding {
|
|
out := make([]domain.RuntimeBinding, len(values))
|
|
for index, value := range values {
|
|
if kept, ok := replacements[value.PluginID]; ok {
|
|
value.PluginID = kept.ID
|
|
value.PluginVersion = kept.Version
|
|
}
|
|
out[index] = domain.CopyRuntimeBinding(value)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func rewriteSnapshotPluginLifecycles(values []domain.PluginLifecycleInstallation, replacements map[string]domain.GamePlugin) []domain.PluginLifecycleInstallation {
|
|
out := make([]domain.PluginLifecycleInstallation, len(values))
|
|
for index, value := range values {
|
|
if kept, ok := replacements[value.PluginID]; ok {
|
|
value.PluginID = kept.ID
|
|
if value.TargetVersion != "" {
|
|
value.TargetVersion = kept.Version
|
|
}
|
|
}
|
|
out[index] = domain.CopyPluginLifecycleInstallation(value)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func retainedSnapshotPluginData(values []domain.PluginDataRecord, replacements map[string]domain.GamePlugin) []domain.PluginDataRecord {
|
|
byID := map[string]domain.PluginDataRecord{}
|
|
for _, value := range values {
|
|
if isLegacySCUMPluginData(value.PluginID, value.Collection) {
|
|
continue
|
|
}
|
|
if kept, ok := replacements[value.PluginID]; ok {
|
|
value.PluginID = kept.ID
|
|
value.ID = snapshotPluginDataID(value.ServerInstanceID, kept.ID, value.Collection, value.Key)
|
|
}
|
|
if current, exists := byID[value.ID]; !exists || value.UpdatedAt.After(current.UpdatedAt) || (value.UpdatedAt.Equal(current.UpdatedAt) && value.CreatedAt.After(current.CreatedAt)) {
|
|
byID[value.ID] = domain.CopyPluginDataRecord(value)
|
|
}
|
|
}
|
|
ids := make([]string, 0, len(byID))
|
|
for id := range byID {
|
|
ids = append(ids, id)
|
|
}
|
|
sort.Strings(ids)
|
|
out := make([]domain.PluginDataRecord, 0, len(ids))
|
|
for _, id := range ids {
|
|
out = append(out, domain.CopyPluginDataRecord(byID[id]))
|
|
}
|
|
return out
|
|
}
|
|
|
|
func isLegacySCUMPluginData(pluginID string, collection string) bool {
|
|
if !isSCUMPluginID(pluginID) {
|
|
return false
|
|
}
|
|
switch strings.ToLower(strings.TrimSpace(collection)) {
|
|
case "scum_users", "scum_players", "scum_trajectories", "scum_user_trajectories", "scum_vehicles", "scum_vehicle_trajectories", "scum_vehicle_locks":
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func isSCUMPluginID(pluginID string) bool {
|
|
canonical := domain.CanonicalGamePluginID(pluginID)
|
|
return canonical == "game.scum" || canonical == "server.scum"
|
|
}
|
|
|
|
func snapshotPluginDataID(serverID, pluginID, collection, key string) string {
|
|
sum := sha256.Sum256([]byte(serverID + "\x00" + pluginID + "\x00" + collection + "\x00" + key))
|
|
return "plugin-data-" + hex.EncodeToString(sum[:])[:24]
|
|
}
|