Импорт пресета не терял моды: полный набор и активный профиль
В пакете сборки рядом с комплектом сервера лежит пресет песочницы одиночной игры. Наборы сортировались по имени, и пресет песочницы часто оказывался первым — панель предлагала его по умолчанию, а модов и карт в нём нет. Теперь первым идёт набор с конфигом сервера, а в списке выбора видно, что внутри каждого. Вкладки показывают активный профиль, поэтому импорт в новый выглядел так, будто ничего не подтянулось. Появилась галочка «Сделать этот профиль активным»; если её снять, панель говорит, где искать импортированное. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -52,6 +52,7 @@ func (s *Server) handleImportPreset(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
createNew := r.FormValue("target") != "existing"
|
||||
activate := r.FormValue("activate") == "1"
|
||||
id := strings.TrimSpace(r.FormValue("profile"))
|
||||
if id == "" && createNew {
|
||||
id = config.SanitizeProfileID(files.Name)
|
||||
@@ -68,8 +69,9 @@ func (s *Server) handleImportPreset(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// Живой сервер держит мир и конфиг в памяти и переписывает файлы при
|
||||
// остановке — импорт под ним просто пропал бы.
|
||||
if id == s.cfg.Active && s.sup.State() != pzserver.StateStopped {
|
||||
// остановке — импорт под ним просто пропал бы. Переключать профиль на
|
||||
// живом сервере нельзя по той же причине.
|
||||
if s.sup.State() != pzserver.StateStopped && (id == s.cfg.Active || activate) {
|
||||
writeError(w, r, http.StatusConflict, "сначала остановите игровой сервер")
|
||||
return
|
||||
}
|
||||
@@ -94,7 +96,17 @@ func (s *Server) handleImportPreset(w http.ResponseWriter, r *http.Request) {
|
||||
writeError(w, r, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
if createNew {
|
||||
// Вкладки панели показывают активный профиль: без переключения импорт
|
||||
// выглядит так, будто моды и настройки не подтянулись.
|
||||
switched := false
|
||||
if activate && s.cfg.Active != id {
|
||||
if err := s.cfg.SetActive(id); err != nil {
|
||||
writeError(w, r, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
switched = true
|
||||
}
|
||||
if createNew || switched {
|
||||
if err := s.cfg.Save(); err != nil {
|
||||
writeError(w, r, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
@@ -104,9 +116,13 @@ func (s *Server) handleImportPreset(w http.ResponseWriter, r *http.Request) {
|
||||
summary := files.Describe()
|
||||
s.sup.AppendLog("manager", fmt.Sprintf("Импортирован пресет %q в профиль %q (модов: %d)",
|
||||
files.Name, id, summary.Mods))
|
||||
if switched {
|
||||
s.sup.AppendLog("manager", fmt.Sprintf("Активный профиль: %s", s.cfg.ActiveProfile().Name()))
|
||||
}
|
||||
|
||||
result["profile"] = id
|
||||
result["created"] = createNew
|
||||
result["switched"] = switched
|
||||
result["summary"] = summary
|
||||
writeJSON(w, http.StatusOK, result)
|
||||
}
|
||||
|
||||
@@ -160,10 +160,35 @@ func FromZip(data []byte) ([]Files, error) {
|
||||
if len(out) == 0 {
|
||||
return nil, ErrNoServerFiles
|
||||
}
|
||||
sort.Slice(out, func(i, j int) bool { return out[i].Name < out[j].Name })
|
||||
// Первым идёт самый полный набор: панель предлагает его по умолчанию, а
|
||||
// одинокий пресет песочницы переносит только значения песочницы — ни модов,
|
||||
// ни карт в нём нет, и импорт выглядит как «ничего не подтянулось».
|
||||
sort.Slice(out, func(i, j int) bool {
|
||||
wi, wj := weight(out[i]), weight(out[j])
|
||||
if wi != wj {
|
||||
return wi > wj
|
||||
}
|
||||
return out[i].Name < out[j].Name
|
||||
})
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// weight — насколько набор полон. Конфиг сервера перевешивает всё остальное:
|
||||
// только в нём есть список модов, карты и настройки самого сервера.
|
||||
func weight(f Files) int {
|
||||
total := 0
|
||||
if f.INI != nil {
|
||||
total += 4
|
||||
}
|
||||
if f.Sandbox != nil {
|
||||
total += 2
|
||||
}
|
||||
if f.SpawnRegions != nil {
|
||||
total++
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
type kind int
|
||||
|
||||
const (
|
||||
|
||||
@@ -192,3 +192,25 @@ func TestFromZipRejectsGarbage(t *testing.T) {
|
||||
t.Fatal("мусор принят за архив")
|
||||
}
|
||||
}
|
||||
|
||||
// В пакете сборки рядом с конфигом сервера лежит и пресет песочницы для
|
||||
// одиночной игры. Первым должен идти набор с конфигом: панель предлагает его по
|
||||
// умолчанию, а из пресета песочницы моды и карты не возьмутся.
|
||||
func TestFromZipServerSetGoesFirst(t *testing.T) {
|
||||
data := buildZip(t, map[string]string{
|
||||
// Имя пресета нарочно сортируется раньше по алфавиту.
|
||||
"Sandbox Presets/Alpha-Sandbox.cfg": sandboxCFG,
|
||||
"Server/zulu-server.ini": serverINI,
|
||||
"Server/zulu-server_SandboxVars.lua": sandboxLua,
|
||||
})
|
||||
sets, err := FromZip(data)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(sets) != 2 {
|
||||
t.Fatalf("наборов: %d", len(sets))
|
||||
}
|
||||
if sets[0].INI == nil {
|
||||
t.Fatalf("первым идёт набор без конфига сервера: %q", sets[0].Name)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user