Импорт пресета песочницы .cfg и списка модов
Сборки нередко раздают только пресет одиночной игры. Внутри это плоский список «Параметр=значение» с точками вместо вложенности, поэтому значения переносятся в SandboxVars.lua профиля по одному: комментарии игры и порядок остаются на месте, а параметры модов, которых в файле ещё нет, пропускаются со счётом. Если файла нет вовсе, он собирается из пресета целиком. Заодно панель принимает строку из игровой кнопки «Поделиться» («Название:ModA;ModB;…»): она заменяет список включённых модов, сохраняя порядок загрузки. Идентификаторов мастерской в такой строке нет, поэтому пакеты добавляются только для модов, чьи файлы уже на диске, а про остальные панель предупреждает. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
@@ -140,23 +141,66 @@ func (s *Server) applyPreset(files preset.Files, id string, keep []string) (map[
|
||||
written = append(written, filepath.Base(path))
|
||||
}
|
||||
|
||||
raw := [][2]any{
|
||||
{s.cfg.SandboxConfigPathFor(id), files.Sandbox},
|
||||
{spawnRegionsPath(s.cfg, id), files.SpawnRegions},
|
||||
}
|
||||
for _, pair := range raw {
|
||||
data, _ := pair[1].([]byte)
|
||||
if data == nil {
|
||||
continue
|
||||
result := map[string]any{"kept": kept, "kept_from": keptFrom}
|
||||
|
||||
sandboxPath := s.cfg.SandboxConfigPathFor(id)
|
||||
switch {
|
||||
case files.Sandbox != nil:
|
||||
// Готовый файл сервера кладём как есть.
|
||||
if err := pzconfig.SaveRaw(sandboxPath, files.Sandbox); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
path := pair[0].(string)
|
||||
if err := pzconfig.SaveRaw(path, data); err != nil {
|
||||
written = append(written, filepath.Base(sandboxPath))
|
||||
case files.SandboxPreset != nil:
|
||||
applied, skipped, err := applySandboxPreset(sandboxPath, files.SandboxPreset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
written = append(written, filepath.Base(sandboxPath))
|
||||
result["applied"] = applied
|
||||
result["skipped"] = skipped
|
||||
}
|
||||
|
||||
if files.SpawnRegions != nil {
|
||||
path := spawnRegionsPath(s.cfg, id)
|
||||
if err := pzconfig.SaveRaw(path, files.SpawnRegions); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
written = append(written, filepath.Base(path))
|
||||
}
|
||||
|
||||
return map[string]any{"files": written, "kept": kept, "kept_from": keptFrom}, nil
|
||||
result["files"] = written
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// applySandboxPreset переносит значения пресета одиночной игры в песочницу
|
||||
// профиля. Пресет плоский, а файл сервера — таблица Lua с комментариями игры,
|
||||
// поэтому значения переставляются по одному, а сам файл остаётся прежним.
|
||||
// Если файла ещё нет (профиль ни разу не запускался), он создаётся целиком.
|
||||
func applySandboxPreset(path string, data []byte) (applied, skipped int, err error) {
|
||||
values := preset.ParseSandboxPreset(data)
|
||||
|
||||
sandbox, err := pzconfig.LoadSandbox(path)
|
||||
if os.IsNotExist(err) {
|
||||
return len(values.Values), 0, pzconfig.SaveRaw(path, values.Lua())
|
||||
}
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
for _, key := range values.Order() {
|
||||
// Параметров модов в файле нет, пока моды не установлены и сервер не
|
||||
// запускался: такие значения пропускаем и считаем.
|
||||
if err := sandbox.Set(key, values.Values[key]); err != nil {
|
||||
skipped++
|
||||
continue
|
||||
}
|
||||
applied++
|
||||
}
|
||||
if applied == 0 {
|
||||
return 0, skipped, errors.New("ни один параметр пресета не подошёл к настройкам профиля")
|
||||
}
|
||||
return applied, skipped, pzconfig.SaveSandbox(path, sandbox)
|
||||
}
|
||||
|
||||
// keepSource выбирает конфиг, из которого берутся сохраняемые значения.
|
||||
@@ -218,8 +262,10 @@ func (s *Server) readPresetUpload(w http.ResponseWriter, r *http.Request) ([]pre
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
if !strings.HasSuffix(strings.ToLower(header.Filename), ".zip") {
|
||||
writeError(w, r, http.StatusBadRequest, "ожидается ZIP-архив пакета настроек")
|
||||
name := strings.ToLower(header.Filename)
|
||||
if !strings.HasSuffix(name, ".zip") && !strings.HasSuffix(name, ".cfg") {
|
||||
writeError(w, r, http.StatusBadRequest,
|
||||
"ожидается ZIP-пакет настроек или пресет песочницы .cfg")
|
||||
return nil, false
|
||||
}
|
||||
data, err := io.ReadAll(io.LimitReader(file, preset.MaxArchiveSize))
|
||||
@@ -228,7 +274,7 @@ func (s *Server) readPresetUpload(w http.ResponseWriter, r *http.Request) ([]pre
|
||||
return nil, false
|
||||
}
|
||||
|
||||
sets, err := preset.FromZip(data)
|
||||
sets, err := preset.FromUpload(header.Filename, data)
|
||||
if err != nil {
|
||||
writeError(w, r, http.StatusBadRequest, err.Error())
|
||||
return nil, false
|
||||
|
||||
Reference in New Issue
Block a user