Welche Schleife nutzen in LUA? und Abfrage für MP, vorm Laden einer XML aus dem Savegame?!

  • Hi,
    Heute mal mit einer grundlegenden Frage zu unterschieden in schleifen.


    Es handelt sich bei dem Skript um dieses Thema: http://planet-ls.de/board/inde…age=Thread&threadID=17816


    Im Skript, lese ich die Texte der Beschreibung mit einer Schleife aus. Man muss die Anzahl der Zeilen angeben und dann die Zeilen durchnummerieren:


    [lua] self.numberTextLines = getXMLInt(xml, "MapBuyableObjects."..self.buyableText..".description#lines");
    for k=1, self.numberTextLines do
    local linesname = string.format("MapBuyableObjects."..self.buyableText..".line%d", k);
    self.descriptionText[k] = getXMLString(xml, linesname .. "#description");
    end;
    [/lua]


    Ein Beispiel für eine andere Schleife wäre so:
    (orginale Sprayer.LUA)
    [lua] local i=0;
    while true do
    local baseName = string.format("vehicle.sprayValves.sprayValve(%d)", i);
    local node = getXMLString(xmlFile, baseName.. "#index");
    if node == nil then
    break;
    end;
    node = Utils.indexToObject(self.components, node);
    i = i+1;
    end;[/lua]


    Meine Frage zu den Schleifen ist nun, welche Schleifen sind die besseren oder auch sinnvolleren??? Vielleicht gibt es sogar unterschiede in der Performance?!


    Ein großer unterschied zu meiner Schleife ist natürlich, das ich bei mir die Anzahl der Zeilen vorher angebe und dann die Zeilen auch nummeriere.



    Und noch eine kleine Frage bezüglich des Ladens einer XML aus dem Savegame. Da ich einzelnen Statuse der Objekte speichern muss, muss ich diese auch wieder laden, das klappt soweit auch schon ganz gut. Beim wälzen von anderen Map Skripten aus dem Ls11 ist mir diesbezüglich folgendes aufgefallen, dort wird vorm Laden der XML abgefragt:


    [lua] if g_server ~= nil then
    [/lua]
    Wie ich aus eigener Erfahrung weiß, wirft mir g_server im MP beim Client ein Error aus (a nil value, bei falscher Abfrage ;) ), also g_server ist für den Client nil!
    Auf die jetztige Abfrage bezogen bedeutet das ja, das im MP der Client die XML nicht aus dem Savegame lädt. Sondern dann wohl vom Host erhält während der Synchronisierung zu Spielbeginn.


    Sehe ich das richtig? Das ich im MP beim Client das Laden der XML aus dem Savegames "abschalten" muss?

  • Bei der while-Variante braucht man die Anzahle Zeilen/Einträge nicht zu wissen. Meiner Meinung nach besser, weil wenn man die Anzahl Zeilen manuell angibt, da eine Zeile hinzufügt oder löscht und die Anzahl vergisst zu ändern (passiert schnell mal) funktioniert gleich nix mehr richtig, im Extremfall schmiert das Spiel ab.


    Durchnummerieren muss auch nicht sein. Ist auch eine unnötige Fehlerquelle, wenn man da mal eine Zahl vergisst, oder eine doppelt hat gibts auch wieder Fehler. Da kann man besser die Variante ohne manuelle Nummerierung verwenden, ist weniger fehleranfällig. Da muss man dann nur beachten, dass die bei 0 anfangen und Indexe von Tabellen in LUA bei 1 anfangen. Um dein Beispiel aufzugreifen: das sieht dann so aus:
    [lua]"line(%d)"[/lua]
    Also einfach zusätzlich Klammern um das %d drum.
    Ist dann mit einer while-Schleife ganz praktisch: Vor der while-Schleife die Zählvariable mit 0 belegen, in der Schleife erst die XML auslesen, dann die Variable hochzählen und dann verarbeiten. Da würde dein Beispiel also so aussehen:
    [lua]local k = 0;
    while true do
    local linesname = string.format("MapBuyableObjects."..self.buyableText..".line(%d)", k);
    if not hasXMLProperty(xml, linesname) then
    break;
    end;
    k = k + 1;
    self.descriptionText[k] = getXMLString(xml, linesname .. "#description");
    end;[/lua]



    Und wegen dem Server Client Zeug: Lass die Texte bloß von den Clients auslesen. Wenn die nur der Server auslesen darf muss man die Texte explizit manuell an die Clients verteilen, wenn diese die auch haben sollen. Da die Daten aber sowieso auch beim Client in einer XML vorhanden sein sollten (sonst hätte man ja unterschiedliche Hash-Werte der Mods, extra im Savegame speichern macht wohl kein Sinn, die Texte wirst du ja wohl nicht ingame ändern, oder?) wäre das unnötige Bandbreitenverschwendung. Mal abgesehen vom zusätzlichen Scriptingaufwand für die Synchronisierung.

  • Erstmal danke für die Erklärung bezüglich der Schleifen. Das mit dem Abschmieren bzw Errors bei falsch angegebenen Zeilen, hatte ich bei den Tests auch schon.
    Werde ich also nochmal überarbeiten den Punkt.



    Das mit den Texten laden, da habe ich mich wohl faslch ausgedrückt. Die Texte lasse ich natürlich auch vom Client laden, im Load () Teil. Da ich und der MP noch keine so dicken Freunde sind, wollte ich eh das nur der Host Objekte kaufen kann, der Client bekommt davon nix mit. Erst nach dem Kauf, bekommt der Client eine Meldung angezeigt, das der Host ein Objekt gekauft. Somit braucht der Client eigentlich die ganzen Texte gar nicht. Vielleicht nutze ich, in der Meldung beim Client, aber eventuell noch den Namen des Objektes, da bin ich mir noch nicht ganz sicher.


    Was ich meinte ist die XML die beim Spiel speichern erstellt wird, dort werden im Savegame die einzelnen Objekte gespeichert, welche Objekte gekauft sind und welche noch nicht. Aber die Frage ist im Grund auch schon beantwortet. Wenn ich aus dem Savegame den Status der Objekte lade, darf dies im MP nur beim Server erfolgen, beim Client natürlich nicht. Der Server kann ja seit dem Spielstart schon ein Objekt gekauft haben, wenn dann der Client die "veraltete" XML aus dem Savegame laden würde, hätte er ja nicht alle Objekte sichtbar.


    Daher ist mir jetzt auch klar warum beim Laden der XML aus dem Savegame vorher die Abfrage:
    [lua]if g_server ~= nil then[/lua] erfolgt.




    Aber, ein Problem habe ich doch noch, gestern beim schreiben des Themas hatte ich es schon mit geschrieben, aber dann dachte ich das ich einen Saudummen Denkfehler hatte und das Problem leicht zu beheben ist, geht aber doch nicht. Es geht dabei um das Laden der XML aus dem Savegame, also dem Status der einzelnen Objekte.


    Wenn ich in eine Map ein paar Objekte einbaue und einen Spielstand starte ist alles ok, keine LOG fehler. Da ich im Load Teil, vorm Laden dieser XML abfrage:
    [lua]
    if g_currentMission.missionStats.playTime == 0 then
    --mache nix
    else
    --lade die XML mit dem Status der Objeke aus dem Savegame
    end;
    [/lua]


    Soweit klar, neuer Spielstand, also keine XML aus dem Savegame laden.


    Wenn ich nun aber bei einem vorhandenen Spielstand, die Map bearbeite,dort Objekte einbaue und dann den vorhandenen Spielstand laden will, bekomme ich einen LOG Error bezüglich der XML: "Error: Can't Load XML"..., eigentlich auch klar, die Spielzeit ist nicht 0, also ist für das Skript auch eine XML da, die nun geladen werden soll, aber nicht gefunden wird, weil die Objekte nachträglich eingebaut sind. Und in Game noch nicht gespeichert wurden ist und somit die XML noch nicht existiert.
    Ich habe dann eine weitere Abfrage eingebaut:
    [lua] local dir = getUserProfileAppPath() .. "savegame"..g_currentMission.missionInfo.savegameIndex;
    self.xmlFile = dir .. "/MapBuyableObjects.xml";


    if g_currentMission.missionStats.playTime == 0 then
    --mache nix
    else
    if self.xmlFile ~= nil then
    --lade XML
    else
    print("No Savegame XML vorhanden");
    end;
    end;
    [/lua]
    Geht auch nicht, logisch, self.xmlFile habe ich vorher etwas zugewiesen, kann somit auch nicht mehr nil sein.
    Anstatt self.xmlFile, habe ich auch mal den direkten Pfad angegeben, geht aber auch nicht.


    Dazu habe ich dann mal LS11 Skripte angesehen die auch im Savegame einen Status gespeichert haben, MapBGA, MapDoorTrigger, MapFruitChangeTrigger. Dort ist es beim Laden der XML so geregelt, am Beispiel MapBGA:


    [lua] local existDir = io.open (self.saveXMLFile, "r");
    if existDir ~= nil then
    local xmlFile = loadXMLFile("TempConfig", self.saveXMLFile);
    --laden des Füllstandes der Silos usw
    delete(xmlFile);
    print("BGASilo saves loaded");
    else
    print("Warning: ", self.saveXMLFile, " can not be found, BGASilo saves not loaded.")
    end;
    [/lua]


    Das ganze mit io.open(self.saveXMLFile, "r"); habe ich dann auch mal versucht, funktioniert aber leider nicht. Wirft mir einen Error aus, das "only write Mode "w" " erlaubt ist.


    Leider weiß ich absolut nicht wie diesen Error wegbekommen kann, wenn man Objekte auf einem vorhandenen Spielstand einbaut. Und dann den Spielstand lädt.
    Kann mir da eventuell auch jemand weiterhelfen???


    Ich weiß das der Error nur einmal auftritt, und nach dem Speichern des Spielstandes die XML erstellt wird und der Fehler nicht mehr auftritt, aber so ein Error stört mich halt, vorallem weil es in anderen Skripten diesen Error nicht gibt.

  • Hi,


    Ich dachte du bist von uns beiden der Experte...;)! Nein, Spass beiseite...


    Wenn ich ein Slash vor "savegame" einfüge und mir den Pfad in die LOG schreiben lasse, dann sieht der Pfad so aus:


    Code
    C:/Users/Björn/Documents/My Games/FarmingSimulator2013//savegame3


    Dort sind dann vor savegame3 2x Slash.


    Was das io.open angeht, das weiß ich ja ebend nicht ganz genau. Es ist aber genau die Zeile die in den MapBGASilos, dem MapDoorTrigger und auch in rafftnix seinem MapFruitChangeTrigger aufgerufen/abgefragt wird. Wenn ich im internet suche, so kriege ich zu io.open, die Antwort das man damit Dateien lesen kann, was in dem Fall wohl genau dazu führt, wenn die XML nicht vorhanden ist, kann sie nicht gelesen werden, somit ist sie ja nil und damit kann ich dann wieder abfragen bzw mit einer Abfrage vor "loadXMLFile" einen anderen Weg gehen wenn die XML nil ist.
    Zu dem io.open, finde ich das:
    http://lua.bumuckl.com/index.p…psplua_arbeitenmitdateien
    und etliche Seiten in Englisch...was ja nicht so meine Sahneseite ist...;)!


    Aber ebend habe ich noch dieses Thema gefunden:
    http://www.autoit.de/index.php?page=Thread&threadID=30374
    da die letzte Antwort im Thema, dort insbesondere der Hinweis mit "io.read("*all") " das werde ich dann morgen mal testen. Ob mir das eventuell hilft um zu prüfen ob es die XML gibt und somit zu verhindern das der Error kommt.



    Wobei, wenn ich mir das überlege, kommt mir schon der nächste Zweifel.
    Sobald auf einem Spielstand mal das Skript vorhanden war, so habe ich ja eine XML im Savegame.
    Spiele ich auf dem Spielstand eine Map ohne das Skript wird die XML nicht gelöscht. Baue ich nun das Skript im Nachhinein in die Map ein, so kriege ich das nächste Problem, denn dann existiert eine XML die aber wohl garantiert nicht die Werte/Pfade enthält wie ich sie brauche. Da ich ja in der XML auch einen Namen mit speichere, nämlich "..self.buyableText..".


    Ich glaube das ganze wird wohl alles nicht so hinhauen wie ich mir das denke, bzw ist es doch noch etwas zuviel in manchen Punkten für mich...!



    Aber zum MP auch noch eine Sache. Wie ist das bei euerm Gameserver, das Thema was es im offiziellen gibt, wo ihr einen Server habt auf dem ihr MP spielt.
    Ist der Server dann bei einem MP Spiel auch der Host???
    Ich meine im Bezug auf dieses Skript, wenn nur der Host Objekte kaufen kann, würde man da bei euch auf dem Server das ganze nicht nutzen können?

  • OK, dann ist da der Slash wohl schon im Pfad mit drin. Es gab zumindest in LS11 auf jeden Fall eine (oder mehrere) Variable oder Funktion, wo der Slash noch nicht im Pfad drin war, wird das wohl eine andere gewesen sein.


    Hab mal kurz nachgeforscht: Man kann auch nur mit dem loadXMLFile rausfinden, ob eine XML existiert. Hier mal ein Beispiel aus dem Multiaddon für LS11 von Interceptor:
    [lua]MultiAddOnXMLid = loadXMLFile("HAxml", getUserProfileAppPath().."/ModShare/MultiAddOn/MultiAddOn.xml")
    HAexist = Utils.getNoNil(getXMLBool (MultiAddOnXMLid, "MultiAddOn.global.exist"), false)[/lua]
    Da wird die MultiAddOn.xml geladen, in der nächsten Zeile wird der XML-Pfad "MultiAddOn.global.exist" aus der XML gelesen. Ist der Pfad nil existiert die Datei nicht und wird HAexist mit false belegt. In dem Fall wird dann später die Datei angelegt und (zusammen mit anderen Sachen) der genannte XML-Pfad mit true gespeichert, damit wird also beim nächsten laden HAexist true sein.
    Dann brauchst du dich gar nicht mit der io-Klasse auseinander setzen. Fällt dein jetztiges Problem weg.


    Server = Host. Ist bei jedem MP-Spiel in LS so. Auch bei dem Gameserver, da läuft ja auch nur eine normale LS-Version drauf. Wenn nur der Host etwas machen darf wird das also zumindest auf so einem Gameserver nicht funktionieren, bzw müsste sich jemand extra beim Server einloggen um das zu machen. Zumindest mit Teamviewer eine Qual die grafische Oberfläche im Spiel zu bedienen, glaub auch nicht das irgendwelche andere mögliche Lösungen da besser wirklich wären.
    Meine Empfehlung: Auch der Client soll alles machen dürfen. Ist Scripting-technisch zwar vermutlich aufwendiger, aber die bessere Lösung.

  • [lua]
    if fileExists(pfad zur Datei) then
    -- Datei existiert
    else
    -- Datei existiert nicht
    end;
    [/lua]


    Die andere Möglichkeit ist wie bassaddict schon geschrieben hat, indem man einen Wert aus der XML ausliest und je nach dem ob der Wert dann existiert oder nicht ist die XML da oder nicht.


    Was io. angeht wurde im LS13 io irgendwie deaktiviert. Vermutlich weil ein paar Leute heraus gefunden haben wie man mit io.execute Blödsinn anstellen könnte..


    EDIT: loadXMLFile gibt übrigens nie nil zurück, selbst wenn die XML nicht existiert wird eine Object ID vergeben.. Fragt mich nicht wieso. ?(



    LG

  • Danke für die Hinweise und Antworten.


    Ich habe es jetzt als erstes mit modelleichers Code getestet,
    [lua]if fileExists(pfad zur Datei) then
    -- Datei existiert
    else
    -- Datei existiert nicht
    end;[/lua]
    Das funktioniert einwandfrei. Wenn ich die XML im Savegame lösche, dann kommt kein Error mehr, sondern nur noch mein Text in der LOG den ich ausgeben lasse wenn die Datei nicht existiert. :thumbsup:



    Habe mir dann auch mal das angesprochene MultiAddon runtergeladen und angesehen. Habe dann deinen code einfach mal genauso verbaut.
    Wenn ich in der XML eine entsprechende Variable anlge, dann klappt das so auch, also erst ist es nil, durch die Variable bekommt es ein true, wenn die Variable vorhanden ist.
    Aber, wenn ich die XML nun wieder lösche, kriege ich wieder den Error in der LOG:

    Code
    Error: Failed to open xml file C:/Users/Björn/Documents/My Games/FarmingSimulator2013/savegame3/MapBuyableObjects.xml'


    Hm, also nochmal im MultiAddon nachgesehen, da ist mir aufgefallen das die Abfrage ja innerhalb einer Schleife verläuft:


    [lua]repeat
    if MAexist == nil then
    --hier wird die XML geladen, bzw MAexist mit true oder false belegt, dann folgt wohl das 2.elseif MAexist==false
    elseif MAexist and not MA1stload then
    --hier werden nun die die einzelnen LUAs geladen und am Ende wird auf die global.lua "verlinkt", wo dann alle einträge aus dem Savegame geladen werden
    --zudem wird MA1stload auf true gesetzt und damit die Schleife beendet
    elseif MAexist == false then
    --da MAexist false ist, werden die XML einträge gesetzt, also die komplette XML erstellt, dazu wird am ende MAexist auf true gesetzt, nun das 1.elseif
    end;
    until MA1stload[/lua]
    Kann bei mir der Error daran liegen, das ich es nicht wie im Multiaddon in einer Schleife geschrieben habe, sondern nur mit der if abfrage?


    Da der Code von modelleicher funktioniert, werde ich das ganze so einbauen.
    Bleibt nur noch ein Problem:
    Wenn ich auf Spielstand XY eine Map spiele mit dem Skript, wird die XML gespeichert, soweit klar. Nun lösche ich den Spielstand und spiele auf einer anderen Map weiter (ohne das Skript), die XML besteht weiterhin. Wenn ich nun nachträglich das Skript mit einbaue und dann weiterspiele, so kriege ich ja einen Error.
    Spielzeit ist ungleich 0, also Savegmae laden.
    XML File existiert, also die XML Werte auslesen.
    Da kommt dann der Error, weil ich ja für die Objekte Namen anlege, diese aber sicher nicht mit den Namen aus einer anderen Map übereinstimmen.


    Aber das sollte ich ja umgehen können, wenn ich es in der Art wie im MultiAddon löse,nur anstatt mit true/false, speichere ich einfach den aktuellen Map namen,
    g_currentMission.missionInfo.mapId, beim laden frage ich ab ob die gespeicherte "mapId" mit der aktuellen übereinstimmt, wenn das so ist, dann die XML komplett laden.


    Somit sollte ich dann alle Fehlerquellen beim Laden ausgeschlossen haben:
    -keine Errors, bei vorhanden Spielständen die verändert werden,
    -keine Errors wenn die XML nicht existiert
    -und keine Fehler wenn eine XML existiert aber diese von einer vorher gespielten Map stammt und das Skript erst hinterher verbaut wurde.(also XML die nicht zur Map passt)


    Da kann ich nur DANKE sagen, was dieses Problem angeht.


    bassaddict:
    Wegen dem Slash, ich frage den Pfad zum Savegame ab:
    [lua]local dir = getUserProfileAppPath() .. "savegame"..g_currentMission.missionInfo.savegameIndex;[/lua]
    dann gebe ich die XML an die geladen werden soll:
    [lua] self.xmlFile = dir .. "/MapBuyableObjects.xml";
    [/lua]
    hier muss ich den Slash setzen. Der ausgegebene Pfad für self.xmlFile in der LOG ist dann auch korrekt geschrieben:

    Code
    C:/Users/Björn/Documents/My Games/FarmingSimulator2013/savegame3/MapBuyableObjects.xml


    also ist das so eine Variable wie du meintest, wo das Slash davor gehört.



    Was das Server = Host angeht und dem "Scripting-technisch zwar vermutlich aufwendiger", das ist bisher für mich teilweise schon das ganze Skript...!
    MP habe ich zwar bisher nicht getestet mit einem Client zusammen, aber den MP Teil habe ich zumindest schonmal im Skript stehen, mit den ganzen Event klassen usw. und da es bisher keinen Fehler in der LOG gab hoffe ich das es vielleicht sogar auf anhieb im MP mit einem Client klappt, die gewonnene Zeit kann ich dann gleich wieder investieren.


    Aber dazu gleich mal eine Frage was einen Trigger im MP angeht. Ich als Host betrete den Trigger, bekomme dann meine Texte angezeigt und kann das Objekt kaufen.
    Muss ich da schon eine function/Event aufrufen bzw senden(beim trigger betreten), damit wenn der Client den gleichen Trigger betritt er nicht kaufen kann bzw gar keine Texte angezeigt bekommt?
    Also das immer nur ein Spieler im Trigger die Texte angezeigt bekommt und etwas kaufen kann?
    Oder ist das egal?


    Ich bin ja kein MP Spieler und weiss daher in Sachen MP nicht wirklich viel was den Spielablauf betrifft.



    Edit: modelleicher:
    ich hatte das am Anfang der Probleme auch mal getestet, mit:
    [lua] local xmlFile =Utils.getNoNil( loadXMLFile("MapBuyableObjects", self.saveXMLFile), nil);
    [/lua]
    Aber auch das hat nicht geklappt, dachte aber eher daran das es mit "Utils.getNoNil" in diesem Falle nicht geht.

  • MP habe ich zwar bisher nicht getestet mit einem Client zusammen, aber den MP Teil habe ich zumindest schonmal im Skript stehen, mit den ganzen Event klassen usw. und da es bisher keinen Fehler in der LOG gab hoffe ich das es vielleicht sogar auf anhieb im MP mit einem Client klappt, die gewonnene Zeit kann ich dann gleich wieder investieren.


    Erwarte bloß nicht zuviel. Das hab ich auch schonmal gdacht und das ging so richtig daneben^^
    Mit dem Trigger weiß ich so auch nicht.

  • Das hab ich auch schonmal gdacht und das ging so richtig daneben


    Manchmal ist es echt unglaublich, wie man mit nur einem Satz die gesamte Hoffnung eines Menschen voll in den Keller ziehen kann....:D!



    Mit dem Trigger, da habe ich nochmal in anderen Skripten nachgesehen, dort ist im TriggerCallback auch nix zu sehen, was verhindert das ein 2.Spieler den Trigger betreten kann. Aber wie wahrscheinlich ist es das beide dann noch zur identischen Zeit "kaufen" drücken. Daher denke ich, es wird wohl nur sehr schwer möglich sein, da einen Fehler zu produzieren.


    Ich werde das am WE wohl im MP mal antesten ob es überhaupt geht und dann sehe ich weiter...

  • Zitat

    Aber dazu gleich mal eine Frage was einen Trigger im MP angeht. Ich als Host betrete den Trigger, bekomme dann meine Texte angezeigt und kann das Objekt kaufen.
    Muss ich da schon eine function/Event aufrufen bzw senden(beim trigger betreten), damit wenn der Client den gleichen Trigger betritt er nicht kaufen kann bzw gar keine Texte angezeigt bekommt?
    Also das immer nur ein Spieler im Trigger die Texte angezeigt bekommt und etwas kaufen kann?
    Oder ist das egal?


    Der Client weiß wenn du als Host in den Trigger gehst, und der Host weiß wenn du als Client in den Trigger gehst, bzw. in beiden Fällen wird bei beiden das Trigger Callback aufgerufen.
    Wenn du möchtest dass immer nur einer im Trigger sein kann bzw. erkannt wird, dann kannst du das ja über einen einfachen Count realisieren. D.h. du setzt eine Variable auf +1 wenn jemand den Trigger betritt, und -1 wenn der Trigger verlassen, wird. Die Variable ist dann am Anfang 0, sobald sie 1 wird lässt du den Trigger alle weitern Spieler ignorieren, ist sie wieder 0 geht es wieder weiter.
    Eine Alternative wenn du vor hast dass nur der Host den Trigger nutzen kann, dass du einfach generell überprüfst ob der Spieler im Trigger der Host ist, dürfte ja sicher ne Variable dafür geben.
    Ansonsten, den Mapdoortrigger von Tobi anschauen, der ist ja auch im MP fähig und es geht auch um Player die in einen Trigger rein laufen, ist zwar LS11 aber vielleicht findest du da ne Idee oder so.. ;)



    LG

Jetzt mitmachen!

Sie haben noch kein Benutzerkonto auf unserer Seite? Registrieren Sie sich kostenlos und nehmen Sie an unserer Community teil!