Импорт пресета песочницы .cfg и списка модов
Сборки нередко раздают только пресет одиночной игры. Внутри это плоский список «Параметр=значение» с точками вместо вложенности, поэтому значения переносятся в SandboxVars.lua профиля по одному: комментарии игры и порядок остаются на месте, а параметры модов, которых в файле ещё нет, пропускаются со счётом. Если файла нет вовсе, он собирается из пресета целиком. Заодно панель принимает строку из игровой кнопки «Поделиться» («Название:ModA;ModB;…»): она заменяет список включённых модов, сохраняя порядок загрузки. Идентификаторов мастерской в такой строке нет, поэтому пакеты добавляются только для модов, чьи файлы уже на диске, а про остальные панель предупреждает. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
package preset
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/mrleo1nid/pzmanager/internal/pzconfig"
|
||||
)
|
||||
|
||||
// sandboxCFG повторяет то, как пресет выглядит у игры: комментарии с решёткой,
|
||||
// расшифровки значений со знаком равенства внутри и точки вместо вложенности.
|
||||
const sandboxCFG = `Version=6
|
||||
# Changing this also sets the "Population Multiplier" in Advanced Zombie Options. Default = Normal
|
||||
-- 1 = Insane
|
||||
-- 4 = Normal
|
||||
Zombies=4
|
||||
|
||||
# Controls whether some randomization is applied.
|
||||
ZombieVoronoiNoise=true
|
||||
DayLength=3
|
||||
XpMultiplier=1.5
|
||||
StatsDecrease=3
|
||||
Temperature=3
|
||||
Rain=3
|
||||
LootRespawn=1
|
||||
FoodRotSpeed=5
|
||||
Nutrition=true
|
||||
WorldItemRemovalList=Base.Hat, Base.Glasses
|
||||
ZombieLore.Speed=2
|
||||
ZombieLore.Strength=2
|
||||
KDOT.SpecialVehicleChance=4.0
|
||||
`
|
||||
|
||||
func TestParseSandboxPreset(t *testing.T) {
|
||||
p := ParseSandboxPreset([]byte(sandboxCFG))
|
||||
|
||||
if p.Version != "6" {
|
||||
t.Errorf("версия = %q", p.Version)
|
||||
}
|
||||
// Строки «-- 4 = Normal» тоже содержат знак равенства, и параметрами они
|
||||
// не являются.
|
||||
for key := range p.Values {
|
||||
if !sandboxKeyRe.MatchString(key) {
|
||||
t.Errorf("в значения попал мусор: %q", key)
|
||||
}
|
||||
}
|
||||
|
||||
want := map[string]string{
|
||||
"Zombies": "4",
|
||||
"ZombieVoronoiNoise": "true",
|
||||
"XpMultiplier": "1.5",
|
||||
"ZombieLore.Speed": "2",
|
||||
"KDOT.SpecialVehicleChance": "4.0",
|
||||
"WorldItemRemovalList": `"Base.Hat, Base.Glasses"`,
|
||||
}
|
||||
for key, value := range want {
|
||||
if got := p.Values[key]; got != value {
|
||||
t.Errorf("%s = %q, ожидалось %q", key, got, value)
|
||||
}
|
||||
}
|
||||
if _, ok := p.Values["Version"]; ok {
|
||||
t.Error("Version не должен попадать в значения: у файла сервера свой VERSION")
|
||||
}
|
||||
}
|
||||
|
||||
// Сгенерированный файл читает и сама игра, и парсер панели — второе проверить
|
||||
// можно прямо здесь.
|
||||
func TestSandboxPresetLuaRoundTrip(t *testing.T) {
|
||||
p := ParseSandboxPreset([]byte(sandboxCFG))
|
||||
lua := p.Lua()
|
||||
|
||||
parsed := pzconfig.ParseSandbox(lua)
|
||||
if got, ok := parsed.Get("VERSION"); !ok || got != "6" {
|
||||
t.Errorf("VERSION = %q (%v)", got, ok)
|
||||
}
|
||||
for key, want := range p.Values {
|
||||
got, ok := parsed.Get(key)
|
||||
if !ok {
|
||||
t.Errorf("параметр %s не читается обратно:\n%s", key, lua)
|
||||
continue
|
||||
}
|
||||
if got != want {
|
||||
t.Errorf("%s = %q, ожидалось %q", key, got, want)
|
||||
}
|
||||
}
|
||||
// Вложенность должна быть настоящей: «ZombieLore.Speed = 2» внутри таблицы
|
||||
// Lua не примет.
|
||||
if strings.Contains(string(lua), "ZombieLore.Speed =") {
|
||||
t.Errorf("точка осталась в имени параметра:\n%s", lua)
|
||||
}
|
||||
}
|
||||
|
||||
// Пути бывают глубже одного уровня — файл должен остаться разбираемым.
|
||||
func TestSandboxPresetDeepPath(t *testing.T) {
|
||||
p := ParseSandboxPreset([]byte("A.B.C=7\nZombies=1\nDayLength=3\nRain=2\n"))
|
||||
parsed := pzconfig.ParseSandbox(p.Lua())
|
||||
if got, ok := parsed.Get("A.B.C"); !ok || got != "7" {
|
||||
t.Fatalf("A.B.C = %q (%v)\n%s", got, ok, p.Lua())
|
||||
}
|
||||
}
|
||||
|
||||
func TestLooksLikeSandboxPreset(t *testing.T) {
|
||||
if !looksLikeSandboxPreset([]byte(sandboxCFG)) {
|
||||
t.Error("пресет песочницы не опознан")
|
||||
}
|
||||
// В том же архиве лежат списки модов и настройки папок с тем же
|
||||
// расширением .cfg.
|
||||
modFolders := "version=2\nnext=15\nfolder=3|1|4D,61,70,73\n"
|
||||
if looksLikeSandboxPreset([]byte(modFolders)) {
|
||||
t.Error("настройки папок приняты за пресет песочницы")
|
||||
}
|
||||
modList := "!fav!:\nSophie-42-v2:ModA;ModB;ModC\n"
|
||||
if looksLikeSandboxPreset([]byte(modList)) {
|
||||
t.Error("список модов принят за пресет песочницы")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user