Práce s XML

Farming Simulátor staví na xml souborech. Každý stroj je konfigurován v xml. A právě k XML potřebujeme něco, jak ho ovládat. K tomu slouží funkce, které jsou definovány přímo v jádře hry. K tomu slouží následující funkce:

  1. Vytcoření, načtení, ukládání:
    • createXMLFile();
    • loadXMLFile();
    • saveXMLFile();
  2. Načítání hodnot:
    • getXMLInt();
    • getXMLFloat();
    • getXMLBool();
    • getXMLString();
  3. Ukládání hodnot:
    • setXMLInt();
    • setXMLFloat();
    • setXMLBool();
    • setXMLString();
  4. Ostatní:
    • removeXMLProperty();
    • hasXMLProperty();

Nyní si všechny tyto funkce rozebereme a vysvětlíme si co dělají. Pokud vás nebude zajímat tento článek a orientujete se v dokumentaci zkuste GDN dokumentaci.

Vytvoření, načtení a ukládání XML souboru

Pro tyto tři věci existují funkce createXMLFile, loadXMLFile a saveXMLFile. Protože už neco vytváříme, musíme vědět kam to uložit. K tomu nám slouží proměnné g_modsDirectory - odkazuje na složku s mody - a g_currentModDirectory - odkazuje na složku s aktuálním modem pod kterým běží skript. A protože potřebujeme ukládat a načítat XML soubory ze složky aktuálního modu, a nebo do složky mods (v případě konfiguračních souborů), velice se nám hodí. Proměnnou g_currentModDirectory si však ještě před použitím ve třídě se skriptem musíme přeuložit do jiné proměnné - po spuštění třídy již není platná.

Nyní si v příkladu vytvoříme jednoduchý XML soubor:

1
2mujSkript = {};
3
4-- ulozeni aktualni cesty k modu do lokalni promenne ktera plati pro tento soubor
5local MDR = g_currentModDirectory;
6
7function mujSkript.prerequisitesPresent(specializations)
8 return true;
9end;
10
11function mujSkript:load(xmlFile)
12 -- vytvoreni souboru xml a ulozeni jeho ID do promenne mojeXML
13 local mojeXML = createXMLFile("mujXMLsoubor",MDR.."xmltest.xml","konfigurace");
14 -- ulozeni XML souboru podle ID
15 saveXMLFile(mojeXML);
16end;
17
18function mujSkript:mouseEvent(posX, posY, isDown, isUp, button)
19end;
20
21function mujSkript:keyEvent(unicode, sym, modifier, idDown)
22end;
23
24function mujSkript:update(dt)
25end;
26
27function mujSkript:updateTick(dt)
28end;
29
30function mujSkript:draw()
31end;

Nyní, když se koukneme do složky s modem pod kterým skript běžel, můžeme vidět vedle modDesc.xml také soubor xmltest.xml a v něm něco jako <konfigurace />. Je to proto, protože jsme do něj nic neuložili. Vstupní parametry funkce createXMLFile jsou (vnitřní název entity(souboru), Celá cesta k xml v PC, Název hlavního prvku) a funkce vrací ID objektu. Funkce saveXMLFile má vstupní parametr pouze ID objektu. Toto byl příklad na createXMLFile a saveXMLFile. Nyní si ukážeme načtení XML souboru.

1
2mujSkript = {};
3
4-- ulozeni aktualni cesty k modu do lokalni promenne ktera plati pro tento soubor
5local MDR = g_currentModDirectory;
6
7function mujSkript.prerequisitesPresent(specializations)
8 return true;
9end;
10
11function mujSkript:load(xmlFile)
12 -- nacteme XML soubor
13 local soubor = loadXMLFile("mujXMLsoubor",MDR.."xmltest.xml");
14 print("ID XML souboru je "..soubor);
15 -- ulozeni XML souboru podle ID
16 saveXMLFile(soubor);
17end;
18
19function mujSkript:mouseEvent(posX, posY, isDown, isUp, button)
20end;
21
22function mujSkript:keyEvent(unicode, sym, modifier, idDown)
23end;
24
25function mujSkript:update(dt)
26end;
27
28function mujSkript:updateTick(dt)
29end;
30
31function mujSkript:draw()
32end;

Naneštěstí funkce loadXMLFile vrátí ID souboru i přesto, že žádný není a vygeneruje chybovou hlášku. Díky tomu nelze ošetřit problém neexistence souboru a musíte mít jistotu že tam ten soubor je a nebo prostě složitě ověřovat existenci (což je podle mě zbytečné u menších projektů).

Proměnná xmlFile ve funkci :load

Již jste si mohli všimnout, že ve funkci load je jedním vstupním parametrem xmlFile. Tento parametr nám předává ID XML souboru stroje a je tak velmi užitečný. Díky němu můžeme číst data z XML stroje a nemusíme znát jeho název. Chová se jako klasický XML soubor. Nedoporučuji přepisovat.

Načítání dat z XML

Je jasné že potřebujeme někdy taky data načítat. K tomu nám slouží funkce začínající slůvkem "getXML" a pokračují příponou datového typu. Čistě teoreticky můžete použít pouze getXMLString a Lua si to převede na co potřebuje, ovšem v takovém případě nelze tak jednoduše ověřit zda tam to číslo nebo pravdivostní hodnota opravdu je.

Mějme xml soubor stroje a chceme z něj přečíst data a provést výpočty. Zde je příklad:

1
2mujSkript = {};
3
4-- ulozeni aktualni cesty k modu do lokalni promenne ktera plati pro tento soubor
5local MDR = g_currentModDirectory;
6
7function mujSkript.prerequisitesPresent(specializations)
8 return true;
9end;
10
11function mujSkript:load(xmlFile)
12 --[[ Nacitani z XML stroje ]]--
13 local cisel = Utils.getNoNil(getXMLInt(xmlFile,"vehicle.mujSkript#pocetCisel"),5);
14 local hodnoty = {};
15 local soucet = 0;
16 local maxCislo;
17 for i=1, cisel do
18 if Utils.getNoNil(getXMLString(xmlFile,"vehicle.mujSkript.cislo"..i.."#type"),"tag") == "tag" then
19 hodnoty[i] = getXMLInt(xmlFile,"vehicle.mujSkript.cislo"..i);
20 else
21 hodnoty[i] = getXMLFloat(xmlFile,"vehicle.mujSkript.cislo"..i.."#hodnota");
22 end;
23 print(i..". nactene cislo je "..hodnoty[i]);
24 end;
25 for i=1, cisel do
26 if hodnoty[i] ~= nil then
27 soucet = soucet + hodnoty[i];
28 if i == 1 then
29 maxCislo = hodnoty[i];
30 end;
31 if maxCislo < hodnoty[i] then
32 maxCislo = hodnoty[i];
33 end;
34 end;
35 end;
36 print("Prumer nactenych cisel: "..(soucet/cisel));
37 print("Pocet nactenych cisel je "..cisel);
38 print("Maximalni nactene cislo je "..maxCislo);
39end;
40
41function mujSkript:mouseEvent(posX, posY, isDown, isUp, button)
42end;
43
44function mujSkript:keyEvent(unicode, sym, modifier, idDown)
45end;
46
47function mujSkript:update(dt)
48end;
49
50function mujSkript:updateTick(dt)
51end;
52
53function mujSkript:draw()
54end;

V příkladu jsem naneštěstí nevyužil getXMLBool, ovšem funkci si lze odvodit. Navíc jsem použil funkci Utils.getNoNil();, která nám zajišťuje filtrování nil hodnot. Vstupním parametrem je hodnota a poté náhradní hodnota. V případě že hodnota bude nil, funkce vrátí náhradní hodnotu. Dále všechny funkce na načítání z xml, mají vstupním parametrem č. 1 XML soubor a druhým je cesta k hodnotě.

Ukládání dat do XML

Tato část Vás naučí, jak ukládat data do XML. V žádném případě ale nepřepisujte XML soubor stroje! Tato funkce se hodí pro tvorbu například konfiguračních souborů pro nějaký mod. K ukládání/modifikaci XML dat slouží funkce začínající slůvkem "setXML" a pokračují příponou datového typu. Narozdíl od načítání, zde hrají datové typy důležitou roli - určují v jakém tvaru bude co zapsáno.

Využijeme předchozího příkladu, s tím rozdílem, že teď načtené data uložíme do jiného souboru.

1
2mujSkript = {};
3
4-- ulozeni aktualni cesty k modu do lokalni promenne ktera plati pro tento soubor
5local MDR = g_currentModDirectory;
6
7function mujSkript.prerequisitesPresent(specializations)
8 return true;
9end;
10
11function mujSkript:load(xmlFile)
12 --[[ Vytvoreni XML souboru ]]--
13 local mojeXML = createXMLFile("mujXMLsoubor",MDR.."xmlSoubor.xml","mujSkript");
14 --[[ Nacitani z XML stroje ]]--
15 local cisel = Utils.getNoNil(getXMLInt(xmlFile,"vehicle.mujSkript#pocetCisel"),5);
16 local hodnoty = {};
17 local soucet = 0;
18 local maxCislo;
19 for i=1, cisel do
20 if Utils.getNoNil(getXMLString(xmlFile,"vehicle.mujSkript.cislo"..i.."#type"),"tag") == "tag" then
21 hodnoty[i] = getXMLInt(xmlFile,"vehicle.mujSkript.cislo"..i);
22 else
23 hodnoty[i] = getXMLFloat(xmlFile,"vehicle.mujSkript.cislo"..i.."#hodnota");
24 end;
25 print(i..". nactene cislo je "..hodnoty[i]);
26 end;
27 for i=1, cisel do
28 if hodnoty[i] ~= nil then
29 soucet = soucet + hodnoty[i];
30 if i == 1 then
31 maxCislo = hodnoty[i];
32 end;
33 if maxCislo < hodnoty[i] then
34 maxCislo = hodnoty[i];
35 end;
36 if hodnoty[i] %2==0 then -- delitelnost
37 setXMLFloat(mojeXML,"mujSkript.sude.cislo"..i.."",hodnoty[i]);
38 else
39 setXMLFloat(mojeXML,"mujSkript.liche.cislo"..i.."",hodnoty[i]);
40 end;
41 end;
42 end;
43 -- Novy XML soubor - zapisy
44 setXMLString(mojeXML,"mujSkript#hlaska","Muj Skript Napsal Soubor");
45 setXMLInt(mojeXML,"mujSkript.pocet",cisel);
46 setXMLFloat(mojeXML,"mujSkript.maxCislo",maxCislo);
47 setXMLFloat(mojeXML,"mujSkript.soucet",soucet);
48 saveXMLFile(mojeXML);
49end;
50
51function mujSkript:mouseEvent(posX, posY, isDown, isUp, button)
52end;
53
54function mujSkript:keyEvent(unicode, sym, modifier, idDown)
55end;
56
57function mujSkript:update(dt)
58end;
59
60function mujSkript:updateTick(dt)
61end;
62
63function mujSkript:draw()
64end;

Samozřejmě že si pak můžete příklad upravit podle své libosti a upravovat ho. Dalo by se tak také naprogramovat počítadlo koupených/načtených strojů toho typu, kdy si vytvořím mini XML soubor, z něj si přečtu kolik je přístupů do souboru, soubor přepíšu a uložím do něj nové čísla. Doporučuji vyzkoušet.

Související video

Soubory ke stažení

  1. mujskript.lua
  2. stroj.xml (Jen výtažek)