• Willkommen im Geoclub - dem größten deutschsprachigen Geocaching-Forum. Registriere dich kostenlos, um alle Inhalte zu sehen und neue Beiträge zu erstellen.

Anderen Editor für UserCode im Urwigo Builder?

bodenseepingu

Geomaster
@Krolock - ich nehme an, es funktionieren auch mehrere "require", so dass man zu einer echten Wiederverwendung von querschnittlichem Code kommen kann.

z.B.

Package Message: z.B. Funktionen wie out_with_mainscreen()

Package WIG_objects: Allgemeine Funktionen z.b. um für Namen von Objekten ein Objekt zu erhalten, zu überprüfen, ob ein Objekt

Package geographic: Z.B. Funktionen für Zonen verschieben, Play-Anywhere, Überschreiten einer Linie

Package Debug: Debug-Funktionen

Package Umlaute: deutsche Umlaute ersetzen

Sollten wir in diesem Fall nicht einfach ne Bibliothek aufbauen mit Dingen, die dann jeder verwenden kann?
 

bodenseepingu

Geomaster
Hat jemand in Bezug auf LUA Erfahrung, wie man denn z.B. mit den Funktionsnamen
umgeht, wenn man seine Sachen in mehrere Packages strukturiert.

Wenn die Namen ungünstig gewählt sind, läuft man ja in Gefahr, dass in einem anderen Package derselbe Name definiert ist und was anderes damit gemeint ist - sei es bei globalen Variablen oder Funktionen.

Sollte man immer ein prefix verwenden - oder die Punkt-Notation ?

Urwigo selber generiert ja z.B. folgenden Code:

Code:
require"Wherigo"
ZonePoint = Wherigo.ZonePoint
Distance = Wherigo.Distance
Player = Wherigo.Player

D.h. eigentlich heißt das Objekt Wherigo.ZonePoint, kann aber in der Cartridge dann mit ZonePoint angesprochen werden dadurch dass ZonePoint=Wherigo.ZonePoint festgelegt wird.

Wenn man das sauber macht, müsste bei Auslagerung von Code wie in Krolocks-Seite beschrieben im "Package" hello die Funktion als
Code:
function hello.helloWithName(name)
definiert werden und entweder mit
Code:
require "hello"
..
hello.helloWithName("Krolock")
oder mit
Code:
require "hello"
helloWithName = hello.helloWithName
..
helloWithName("Krolock")

verwendet werden. Wie ist eure Meinung?
 

Krolock

Geocacher
@bodenseepingu:

Zur ersten Frage: Du kannst sowohl mehrere requires einfügen, also transitiv ein require auf die erste Datei, die wiederum ein require auf die nächste enthält.
Dürfte auch für die Abhängigkeit bei deiner Package Bibliothek interessant sein.
Ich hab aber noch nicht untersucht, wie es sich verhält, wenn ein Modul mehrfach eingebunden wird.

Zum zweiten Post:
Du musst die Methoden innerhalb einer Klasse definieren und dann darüber aufrufen.
Ich werds morgen mal testen und dann Beispielcode veröffentlichen.
 

Krolock

Geocacher
Ich konnt's nicht lassen und hab mal ein wenig herumexpirimentiert:

Aufgabe: Erstelle eine Bibliothek PrintWorker, die eine vorgefertige Methode printHelloWithName anbietet:

printworker.lua:
Code:
--declare PrintWorker
PrintWorker = {}
PrintWorker.__index = PrintWorker

function PrintWorker:new()
  local self = {}
  setmetatable(self, PrintWorker)
  return self
end

function PrintWorker:printHelloWithName(name)
  _Urwigo.MessageBox{
    Text = "Hello " .. name .. "!"
  }
end

Wer verstehen möchte, warum .__index und setmetatable aufgerufen werden muss, der möge http://lua-users.org/wiki/SimpleLuaClasses und http://www.lua.org/pil/16.1.html nachschlagen, ansonsten hilft auch benutzen und glücklich sein. :roll:

Die Methode kann wie folgt an beliebieger Stelle aufgerufen werden.
Beispiel mit Aufruf in einer run Methode, die von onStart aufgerufen wird.
Code:
require "printworker"

function run()
	pw = PrintWorker:new()
	pw:printHelloWithName("Krolock")
end
So kann man gezielte Packet erstellen, um Timer, Debug, Logbuch ... wiederzuverwenden.
Das Beispielprojekt kann auch als zip heruntergeladen werden.
 

Anhänge

  • ClassTest.zip
    1,7 KB · Aufrufe: 17

bodenseepingu

Geomaster
Ich fürchte es gibt noch die ein- oder anderen Kinken.

Ich wollte jetzt ein in LUA geschriebenes Item in separate LUA-Files auslagern und
einbinden. Dieses Item benötigt persistente Variablen - z.B. eine Table.

D.h. die Table muß in die ZVariables Table, damit sie beim Speichern / Wiederladen der Cartridge mitgespeichert werden, was man gemeinhin mit einer Urwigo-Variable z.B. vom Typ String macht, die später mit der echten Table überschrieben wird.

Wenn man normalen User-Code schreibt, ist diese Userspezifische Table hinter der Urwigo-Definition und überschreibt diese (d.h. aus String wird dadurch die benötigte Table)

Durch das Inlining wie es Urwigo macht, ist das jetzt andersrum. Es wird zuerst die benötigte Table generiert, die dann später durch die Urwigo-Stringvariable überschrieben wird.

Hierdurch kommt es zu einem Laufzeit-Fehler, weil auf eine String-Variable und nicht auf eine Table zugegriffen wird.

Die einzige Lösungsmöglichkeit, die mir spontan einfällt: Benötigte Variablen müssen separat in den normalen User-Code von Urwigo reinkopiert werden.
 

Krolock

Geocacher
bodenseepingu schrieb:
Durch das Inlining wie es Urwigo macht, ist das jetzt andersrum. Es wird zuerst die benötigte Table generiert, die dann später durch die Urwigo-Stringvariable überschrieben wird.

Das stimmt (zum Glück) nicht ganz. Entscheidend ist, ob man die Datei in 'user directives' oder 'user functions' einbindet. Ich hab mal das erzeugte _cartridge.lua untersucht:

Code:
_Urwigo.InlineRequireLoaded = {}
_Urwigo.InlineRequireRes = {}
_Urwigo.InlineRequire = function(moduleName)
  local res
  if _Urwigo.InlineRequireLoaded[moduleName] == nil then
    res = _Urwigo.InlineModuleFunc[moduleName]()
    _Urwigo.InlineRequireLoaded[moduleName] = 1
    _Urwigo.InlineRequireRes[moduleName] = res
  else
    res = _Urwigo.InlineRequireRes[moduleName]
  end
  return res
end

......

-- Inlined modules --
_Urwigo.InlineModuleFunc = {
	[0] = function()
		function beforeUserDirective()
			return "Teil1"
		end
	end, 
	[1] = function()		
		function beforeUserFunctions()
			return "Teil2"
		end
	end
}

-- Begin user directives --
_Urwigo.InlineRequire(0)

-- End user directives --

objSequenceTest = Wherigo.ZCartridge()

-- Media --
-- Cartridge Info --
objSequenceTest.Id="115bc0c2-0cc8-4341-8407-eaec559d917a"
objSequenceTest.Name="SequenceTest"
objSequenceTest.Description=[[]]

.....

-- Zones --

-- Characters --

-- Items --

-- Tasks --

-- Cartridge Variables --
currentZone = "dummy"
currentCharacter = "dummy"
currentItem = "dummy"
currentTask = "dummy"
currentInput = "dummy"
currentTimer = "dummy"
objSequenceTest.ZVariables = {
	currentZone = "dummy", 
	currentCharacter = "dummy", 
	currentItem = "dummy", 
	currentTask = "dummy", 
	currentInput = "dummy", 
	currentTimer = "dummy"
}

-- Timers --

-- Inputs --

-- WorksWithList for object commands --

-- functions --
function objSequenceTest:OnStart()
end
function objSequenceTest:OnRestore()
end

-- Urwigo functions --

-- Begin user functions --
_Urwigo.InlineRequire(1)

Die InlineModuleFunc[0] wird in 'user directives' mit require eingehängen, InlineModuleFunc [1] in 'user functions'.
wie man im resultierenden Code sehen kann, wird directives bei
Code:
-- Begin user directives --
und functions bei
Code:
 -- Begin user functions --

Die Methode _Urwigo.InlineRequire(0) bewirkt, dass an diese Stelle der Inhalt von InlineModuleFunc[0] also
Code:
function()
		function beforeUserDirective()
			return "Teil1"
		end
eingefügt wird. In lua kann man selbst Programmcode dynamisch zur Laufzeit einbinden. :gott:
(Man könnte sogar den Code abhängig vom Player einfügen -
Code:
if (iphone) return getCodeIPhone()
)
 

Krolock

Geocacher
Da das Speicherproblem bei selbst geschriebenem lua mich schon länger beschäftigt, habe ich mal versucht, das Problem über Callback-Architektur zu lösen.
Die Idee dabei ist, dass man beim Einbinden eines Moduls (z.B Logbuch) diesem Modul ein Objekt übergibt, in das es seine zu peristierenden Werte schreiben und lesen kann.
Dieses Objekt muss in Urwigo definiert sein, damit es in den ZVariablen gelistet ist und somit persitiert wird. Es muss weiterhin ein Tabelle sein um somit beliebig viele Werte speichern zu können.
Das Modul bietet dann eine save() und restore() Methode an, die dem lua-Teil der das Modul nutzen möchte beim OnSave bzw. OnRestore aufgerufen wird.
Der Vorteil dieser Methode ist die Trennung der Aufgaben:
- Das Modul kümmert sich selbst um die Persistierung seiner Daten, d.h. wenn jemand das Modul nutzen möchte, muss er nicht nachschauen und verstehen, was das Modul speichern muss.
- Die Bereitstellung des Speicherobjekts erfolgt durch den Benutzer. Somit hat er die Kontrolle über die Namen der Variablen und kann Kollisionen vermeiden.

Nachfolgend ist das BeispielModul SaveWorker aufgelistet:
Code:
require"class"

SaveWorker = class(function(sav, storable)
  sav.storable = storable
end)

-- save routines
function SaveWorker:save()
  self.storable[0] = self.nr
  self.storable[1] = self.name
end

function SaveWorker:restore()
  self.nr = self.storable[0]
  self.name = self.storable[1]
end



-- Getter and Setter (working routines)
function SaveWorker:setNr(aNr)
  self.nr = aNr
end

function SaveWorker:getNr()
  return self.nr
end

function SaveWorker:setName(name)
  self.name = name
end

function SaveWorker:getName()
  return self.name
end

Im oberen Teil sehen wir die Erzeugung des Objektes mit der class Funktion aus http://lua-users.org/wiki/SimpleLuaClasses. Ist ein wenig eleganter als sich selbst um .__index und setmetatable zu kümmern.
Danach folgen die save und restore Methoden, die jedes Modul implementieren sollte.

Der untere Teil soll den Arbeitsbereich simulieren. Das Beispielmodul macht hier nicht wirklich was außer eine Zahl und einen Namen zu speichern. Dieser Teil ist natürlich individuell in jedem Modul.

Kommen wir nun zum Arbeitsablauf in unserem Cartridge:
Code:
require"printworker"
require"saveworker"

function initWorker()
  pw = PrintWorker:new()
  sw = SaveWorker(storableForSW)
end


function init()
  storableForSW = {}
  initWorker()
end

function run()
  init()
  --pw:printHelloWithName("Krolock")
  local nr = math.random(1, 1000)
  local name = "Krolock_" .. math.random(1, 1000)
  pw:print(nr .. " " ..name)
  sw:setNr(nr)
  sw:setName(name)
end

function saveAll()
  sw:save()
end

function restoreAll()
  initWorker()
  sw:restore()
  pw:print(sw:getNr() .. " " .. sw:getName())
end

Das Modul PrintWorker hab ich um eine einfache print-Methode erweitert, die ne MessageBox ausgibt.
Auch hier wird im onStart des Urwigo-Projektes die run() Methode aufgerufen. Diese Initialisiert erstmal die Variable storableForSW (In Urwigo mit Identifier 'storableForSW' angelegt.
Anschließend werden die beiden Module (Worker) initialisiert, wobei man sieht, dass die Variable storableForSW hier dem SaveWorker übergeben wird.
Nachdem Zufallszahl und Zufallsnamen erzeugt sind, werden sie zu Kontrollzwecken ausgeben und unser Cartridge ist erstmal fertig.

Die Methode saveAll() wird von Urwigo_OnSave() aufgerufen und veranlasst alle Module, die etwas persitieren müssen dies zu tun.
Ebendso wird restoreAll von Uriwog.OnResume() aufgerufen, initialisiert die Module erneut und veranlasst sie ihre Daten aus den Persistenzveriablen (hier storableForSW) wieder herzustellen.
Der anschließende Print Befehl dient der Kontrolle, dass nun die gleichen Wert wie beim ersten Lauf angezeigt werden.

Auf dem Oregon läuft das ganze wunderbar. Über Feedback bzgl. Android und iPhone würde ich mich sehr freuen.
Leider gibt es wohl keine Möglichkeit in lua ein Interface zu definieren. Man kann aber mit dem class Modul "Klassen" vererben. Ich werd mal forschen, ob man nicht ne abstract Oberklasse definieren kann, die save() und restore() anbietet und dessen Logik man dann in dem Modul überschreibt. Dann kann man sich blind darauf verlassen, dass kein Fehler auftaucht wenn save() und restore() aufgerufen wird.

Beispielprojekt hängt an.
 

Anhänge

  • ClassTestV2.zip
    3,3 KB · Aufrufe: 15

bodenseepingu

Geomaster
OK :gott: Krolock, dann habe ich vermutlich den Fehler begangen, dass ich das require in die User directives reingenommen habe.

Da WIG's doch irgendwo ne Beschränkung haben, reichen mir einfachere Konstrukte aus. Mir reichts aus in einem LUA-Modul einfach anzugeben, welche (wenigen) Handgriffe notwendig sind, um das LUA-Modul mit Urwigo zu nutzen.

Insbesonders geht's mir eher darum, dass in einer guten Kombination aus Urwigo und LUA interessante Dinge gemacht werden.

Alles was Urwigo dabei kann - Zonen, Items etc. möchte ich weiterhin in Urwigo auch machen und LUA benutze ich mehr als eine funktionale Schnittstelle und weniger objektorientiert.

Trotzdem - :gott: Respekt
 

Krolock

Geocacher
Ich hab mir die zwei Probleme mit dem Zeilenumbruch und der Persistenz nochmal angeschaut.
Um einen Zeilenumbruch vor einer Variablen
Code:
"Der Spieler X ist an der Reihe[[
]] .. amount .. " EUR stehen im noch zur Verfuegung"
hinzubekommen muss man den Zeilenumbruch als Variable cr definieren. So wie im Wherigo-Handbuch beschrieben.
Man darf die Definition aber nicht in einer einzubettenden .lua Datei einfügen, sondern sollte dies im "lua user directives" machen.
Zeilenumbruch.PNG

Zur Persistenz:
Wenn im eingebetteten Modul eine Variable, z.b Tabelle persistent gehalten werden soll, muss man diese einfach nur an die ZVariablen anhängen:
Code:
myCartridge.ZVariables.myTable = myTable
wobei myCartridge dem Identifier des Wherigos entspricht (auf der Startseite einzutragen) und myTable der Identifier der zu peristierenden Variable ist.
Wer es etwas ausführlich mag kann unter Datenstrukturen in lua nachschauen
 
Oben