mcbTrigger

» Siedler Map Source Forum » Siedler DEdK Script Forum » mcbTrigger

Seiten: 1

mcb
#1
04.10.2014 11:25
Beiträge: 1285

mcbTrigger

Ich habe den Trigger-Fix von Chromix verbessert und erweitert.
Fehler werden abgefangen und über das DebugWindow ausgegeben.

-- Trigger-Fix    mcb      1.1                            Dank an Chromix
-- Funktionen anstelle von Funktionsnamen als Trigger
-- tables als Trigger-Argumente
-- Trigger-Argumente bei StarteSimpleJob, StartSimpleHiResJob, StartJob, StartHiResJob
-- error-handling (Trigger werden nicht gelöscht!)
-- Warnung, wenn Trigger / HiRes-Trigger zusammen länger als 0.03 sec brauchen (ab da ruckelts!)
-- Fügt Events.LOGIC_EVENT_LOW_PRIORITY hinzu, für Jobs die komplizierte Berechnungen durchführen, länger brauchen oder unwichtig sind
--   action wird als coroutine ausgeführt
--   condition bestimmt, ob action diesen tick weiter ausgeführt wird
--   coroutine.yield() Zeitprüfung, fortgesetzt wenn wieder Rechenzeit zur Verfügung steht
--   coroutine.yield(-1) diesen Job erst nächsten Tick weiter ausführen
--   ret = coroutine.yield(func, ...) führt Funktionen außerhalb der coroutine aus (OHNE Zeitprüfung)
--   beenden über return true! / EndJob
--   StartSimpleLowPriorityJob / StartLowPriorityJob
--
-- mcbTrigger.protectedCall(func, ...)	Ruft eine Funktion geschützt auf, und leitet Fehler an mcbTrigger.err
-- mcbTrigger.err(txt)					Standard - Fehlerausgabe (über DebugWindow)
-- mcbTrigger.getPerf()					Gibt die Performance-Werte zurück: HiResJob, Job
-- mcbTrigger.isDebuggerActive()		Prüft, ob der Debugger aktiv ist
--
-- Für Debugger optimiert:
--   Wenn der Debugger aktiv ist, werden Fehler nicht abgefangen, sondern an den Debugger weitergeleitet
--   DEBUG - Funktionen hinzugefügt (die meisten machen nur mit Debugger Sinn)
--
-- mcbTrigger.DEBUG_GetCurrentTID()					Gibt den aktuell aktiven Trigger zurück
-- mcbTrigger.DEBUG_GetInfo(tid)					Schreibt Debug-Infos über den Trigger in den Debugger und gibt das Trigger-table zurück
-- mcbTrigger.DEBUG_KillCurrentTrigger()			Löscht den aktuellen Trigger
-- mcbTrigger.DEBUG_SuspendCurrentTrigger()			Schaltet den aktuellen Trigger inaktiv
-- mcbTrigger.DEBUG_TriggerSetNoDebug(noDebug, tid)	Schaltet die Fehlerausgabe ab.
-- tid ist optional, wenn es nicht angegeben wird, wird mcbTrigger.DEBUG_GetCurrentTID() genommen.
mcbTrigger = {init = function()
		mcbTrigger_action = mcbTrigger.act
		mcbTrigger_contition = mcbTrigger.con
		mcbTrigger.Mission_OnSaveGameLoaded = Mission_OnSaveGameLoaded
		Mission_OnSaveGameLoaded = function()
			mcbTrigger.hackTrigger()
			mcbTrigger.Mission_OnSaveGameLoaded()
		end
		mcbTrigger.hackTrigger()
		
		Trigger.RequestTrigger(Events.LOGIC_EVENT_EVERY_TURN, nil, function()
			mcbTrigger.perf = XGUIEng.GetSystemTime()
		end, 1, nil, nil)
		Trigger.RequestTrigger(Events.LOGIC_EVENT_EVERY_SECOND, nil, function()
			mcbTrigger.perf2 = XGUIEng.GetSystemTime()
		end, 1, nil, nil)
		
		Events.LOGIC_EVENT_LOW_PRIORITY = "mcb_lpjob"
		
		StartSimpleJob = function(f, ...)
			return Trigger.RequestTrigger(Events.LOGIC_EVENT_EVERY_SECOND, nil, f, 1, nil, arg)
		end
		StartSimpleHiResJob = function(f, ...)
			return Trigger.RequestTrigger(Events.LOGIC_EVENT_EVERY_TURN, nil, f, 1, nil, arg)
		end
		StartJob = function(f, ...)
			return Trigger.RequestTrigger(Events.LOGIC_EVENT_EVERY_SECOND, "Condition_"..f, "Action_"..f, 1, arg, arg)
		end
		StartHiResJob = function(f, ...)
			return Trigger.RequestTrigger(Events.LOGIC_EVENT_EVERY_TURN, "Condition_"..f, "Action_"..f, 1, arg, arg)
		end
		StartSimpleLowPriorityJob = function(f, ...)
			return Trigger.RequestTrigger(Events.LOGIC_EVENT_LOW_PRIORITY, nil, f, 1, nil, arg)
		end
		StartLowPriorityJob = function(f, ...)
			return Trigger.RequestTrigger(Events.LOGIC_EVENT_LOW_PRIORITY, "Condition_"..f, "Action_"..f, 1, arg, arg)
		end
	end,
	trigger = {},
	vtrigger = {},
	vtid = 0,
	perf = 0,
	perfJob = 0,
	perf2 = 0,
	perfJob2 = 0,
	performance = 0,
	performance2 = 0,
	lptrigger = {},
	lptids = {},
	lptid = 0,
	lpnow = 1,
	errtext = {},
	errtid = nil,
}
function mcbTrigger.err(txt)
	Message("@color:255,0,0 Err:")
	Message(txt)
	table.insert(mcbTrigger.errtext, txt)
	if table.getn(mcbTrigger.errtext) > 15 then
		table.remove(mcbTrigger.errtext)
	end
	XGUIEng.ShowWidget("DebugWindow", 1)
end
GUIUpdate_UpdateDebugInfo = function()
	local txt = ""
	for k,v in ipairs(mcbTrigger.errtext) do
		txt = txt.." @color:255,0,0 "..v.." @cr "
	end
	XGUIEng.SetText("DebugWindow", txt)
end
mcbTrigger.perfJ = function()
	local p2 = XGUIEng.GetSystemTime()
	if p2-mcbTrigger.perf < 0.025 and mcbTrigger.lptids[1] then
		local forceRun = false
		while true do
			p2 = XGUIEng.GetSystemTime()
			if p2-mcbTrigger.perf > 0.025 and not forceRun then
				break
			end
			forceRun = false
			if not mcbTrigger.lptids[mcbTrigger.lpnow] then
				mcbTrigger.lpnow = 1
			end
			if not mcbTrigger.lptids[mcbTrigger.lpnow] then
				break
			end
			local t = mcbTrigger.lptrigger[mcbTrigger.lptids[mcbTrigger.lpnow]]
			local run = false
			if t.con then
				local f = t.con
				if type(f)=="string" then
					f = _G[f]
				end
				if type(f)~="function" then
					mcbTrigger.err("Trigger "..mcbTrigger.lptids[mcbTrigger.lpnow].." condition "..tostring(t.con).." invalid! action not called!")
					run = false
				else
					local r = {xpcall(function() return f(unpack(t.acon)) end, mcbTrigger.err)}
					local e = table.remove(r, 1)
					if not e then
						run = false
					else
						run = r[1]
					end
				end
			else
				run = true
			end
			if run then
				if not t.corot or coroutine.status(t.corot)=="dead" then
					local f = t.act
					if type(f)=="string" then
						f = _G[f]
					end
					if type(f)~="function" then
						mcbTrigger.err("Trigger "..mcbTrigger.lptids[mcbTrigger.lpnow].." action "..tostring(t.act).." invalid!")
						run = false
					else
						t.corot = coroutine.create(f)
						--Message("corot create")
					end
				end
				if t.corot then
					local argu = t.aact
					if t.aact2 then
						argu = t.aact2
						t.aact2 = nil
					end
					local r = {coroutine.resume(t.corot, unpack(argu))}
					if not table.remove(r, 1) then
						mcbTrigger.err(r[1])
						run = false
					else
						if type(r[1])=="function" then
							t.aact2 = {mcbTrigger.protectedCall(unpack(r))}
							forceRun = true
						elseif r[1] == -1 then
							run = false
						elseif r[1] == true then
							mcbTrigger.lptrigger[mcbTrigger.lptids[mcbTrigger.lpnow]] = nil
							table.remove(mcbTrigger.lptids, mcbTrigger.lpnow)
							run = true
						end
					end
				else
					run = false
				end
			end
			if not run then
				mcbTrigger.lpnow = mcbTrigger.lpnow + 1
			end
		end
	end
	p2 = XGUIEng.GetSystemTime()
	mcbTrigger.performance = p2-mcbTrigger.perf
	if mcbTrigger.performance > 0.03 then
		Message("@color:255,0,0 HiRes-Trigger runtime too long: "..mcbTrigger.performance)
	end
end
mcbTrigger.perfJ2 = function()
	local p2 = XGUIEng.GetSystemTime()
	mcbTrigger.performance2 = p2-mcbTrigger.perf2
	if mcbTrigger.performance2 > 0.03 then
		Message("@color:255,0,0 Trigger runtime too long: "..mcbTrigger.performance2)
	end
end
function mcbTrigger.hackTrigger()
	if not unpack{true} then
		unpack = function(t, i)
			i = i or 1
			if i <= table.getn(t) then
				return t[i], unpack(t, i+1)
			end
		end
	end
	mcbTrigger.RequestTrigger = Trigger.RequestTrigger
	Trigger.RequestTrigger = function(typ, con, act, active, acon, aact)
		local tid = mcbTrigger.add(typ, con, act, active, acon, aact)
		if typ == Events.LOGIC_EVENT_EVERY_TURN then
			EndJob(mcbTrigger.perfJob)
			mcbTrigger.perfJob = mcbTrigger.add(Events.LOGIC_EVENT_EVERY_TURN, nil, mcbTrigger.perfJ, 1, nil, nil)
		end
		if typ == Events.LOGIC_EVENT_EVERY_SECOND then
			EndJob(mcbTrigger.perfJob2)
			mcbTrigger.perfJob2 = mcbTrigger.add(Events.LOGIC_EVENT_EVERY_SECOND, nil, mcbTrigger.perfJ2, 1, nil, nil)
		end
		return tid
	end
	mcbTrigger.UnrequestTrigger = Trigger.UnrequestTrigger
	Trigger.UnrequestTrigger = function(tid)
		if tid < 0 then
			mcbTrigger.lptrigger[tid] = nil
			for i=table.getn(mcbTrigger.lptids),1,-1 do
				if mcbTrigger.lptids[i] == tid then
					table.remove(mcbTrigger.lptids, i)
					return
				end
			end
			return
		end
		
		tab = mcbTrigger.trigger[tid]
		if tab then
			mcbTrigger.trigger[tab.tid] = nil
			mcbTrigger.vtrigger[tab.vtid] = nil
		end
		return mcbTrigger.UnrequestTrigger(tid)
	end
end
function mcbTrigger.add(typ, con, act, active, acon, aact)
	if typ == Events.LOGIC_EVENT_LOW_PRIORITY then
		mcbTrigger.lptid = mcbTrigger.lptid - 1
		local tab = {
			act = act,
			con = con~="" and con or nil,
			acon = acon or {},
			aact = aact or {},
		}
		mcbTrigger.lptrigger[mcbTrigger.lptid] = tab
		table.insert(mcbTrigger.lptids, mcbTrigger.lptid)
		return mcbTrigger.lptid
	end
	
	mcbTrigger.vtid = mcbTrigger.vtid + 1
	local tab = {
		act = act,
		con = con~="" and con or nil,
		acon = acon or {},
		aact = aact or {},
		vtid = mcbTrigger.vtid,
	}
	assert(type(tab.acon)=="table")
	assert(type(tab.aact)=="table")
	assert(type(typ)=="number")
	mcbTrigger.vtrigger[mcbTrigger.vtid] = tab
	tab.tid = mcbTrigger.RequestTrigger(typ, "mcbTrigger_contition", "mcbTrigger_action", active, {tab.vtid}, {tab.vtid})
	mcbTrigger.trigger[tab.tid] = tab
	return tab.tid
end
function mcbTrigger.con(vtid)
	local tab = mcbTrigger.vtrigger[vtid]
	mcbTrigger.errtid = tab.tid
	if tab.con then
		local f = tab.con
		if type(f)=="string" then
			f = _G[f]
		end
		if type(f)~="function" then
			mcbTrigger.err("Trigger "..tab.tid.." condition "..tostring(tab.con).." invalid! action not called!")
			return false
		end
		local r = nil
		if mcbTrigger.isDebuggerActive() and not tab.noDebug then
			r = {true, f(unpack(tab.acon))}
		else
			r = {xpcall(function() return f(unpack(tab.acon)) end, mcbTrigger.err)}
		end
		local e = table.remove(r, 1)
		if not e then
			return false
		end
		return unpack(r)
	else
		return true
	end
end
function mcbTrigger.act(vtid)
	local tab = mcbTrigger.vtrigger[vtid]
	local f = tab.act
	if type(f)=="string" then
		f = _G[f]
	end
	if type(f)~="function" then
		mcbTrigger.err("Trigger "..tab.tid.." action "..tostring(tab.act).." invalid!")
		return false
	end
	local r = nil
	if mcbTrigger.isDebuggerActive() and not tab.noDebug then
		r = {true, f(unpack(tab.aact))}
	else
		r = {xpcall(function() return f(unpack(tab.aact)) end, mcbTrigger.err)}
	end
	local e = table.remove(r, 1)
	if not e then
		return false
	end
	if r[1] == true then
		mcbTrigger.vtrigger[tab.vtid] = nil
		mcbTrigger.trigger[tab.tid] = nil
	end
	mcbTrigger.errtid = nil
	return unpack(r)
end
function mcbTrigger.protectedCall(func, ...)
	local r = nil
	xpcall(function()
		r = {func(unpack(arg))}
	end, function(err)
		mcbTrigger.err("protectedCall: "..err)
	end)
	return unpack(arg)
end
function mcbTrigger.getPerf()
	return mcbTrigger.performance, mcbTrigger.performance2
end
function mcbTrigger.isDebuggerActive()
	return LuaDebugger.Log and true or false
end
function mcbTrigger.DEBUG_GetCurrentTID()
	return mcbTrigger.errtid
end
function mcbTrigger.DEBUG_TriggerSetNoDebug(noDebug, tid)
	tid = mcbTrigger.trigger[tid] and tid or mcbTrigger.errtid
	mcbTrigger.trigger[tid].noDebug = noDebug
end
function mcbTrigger.DEBUG_KillCurrentTrigger()
	EndJob(mcbTrigger.errtid)
end
function mcbTrigger.DEBUG_SuspendCurrentTrigger()
	Trigger.DisableTrigger(mcbTrigger.errtid)
end
function mcbTrigger.DEBUG_GetInfo(tid)
	tid = mcbTrigger.trigger[tid] and tid or mcbTrigger.errtid
	local t = mcbTrigger.trigger[tid]
	LuaDebugger.Log("Trigger-Info: "..t.tid.." / "..t.vtid..", type = "..type(t.con).." / "..type(t.act)..
		", func = "..tostring(t.con).." / "..tostring(t.act)
	)
	return t
end
mcbTrigger.init()



Der LowPriorityJob ist noch nicht groß getestet, bei mir funktioniert jedoch alles Fehlerfrei. Wenn jemandem ein Fehler auffällt, bitte mir mitteilen.

Edit: v1.1

Dieser Beitrag wurde von mcb am 19.10.2014 um 11:52 editiert.

Kantelo
#2
05.10.2014 16:38
Beiträge: 356

Vielen Dank für diese ganze Funktionen.
Von Metatables hab ich zwar keine Ahnung aber die anderen beiden Sachen kann ich gut gebrauchen

mcb
#3
05.10.2014 17:07
Beiträge: 1285

Zitat von Kantelo:
Vielen Dank für diese ganze Funktionen.
Von Metatables hab ich zwar keine Ahnung aber die anderen beiden Sachen kann ich gut gebrauchen



Gerne
Metatables braucht man im normalen Script eher selten... Die einzigen Scripte die das Verwenden (und die ich kenne) sind Noigis Siedler-Schach, das AI-Script und meine newArmy.

mcb
#4
19.10.2014 11:51
Beiträge: 1285

Version 1.1 online:
- Anpassung auf yoqs LuaDebugger:
- Fehler werden im Debugger angezeigt, wenn aktiv
- DEBUG - Funktionen

Messoras
#5
13.12.2016 12:42
Beiträge: 61

Auf Linux kriege ich nach einer Cutscene, wenn ich versuche einen Job aufzumachen die Performance Meldung. Die Trigger Laufzeit ist knapp über 0.05...
Kann ich da was machen?

Gruß,
Messoras

____________________
Six feet of earth make us all equal.

Spielt Siedler 5 online mit mir, dank Jojos und Yoqs Ubi.com Ersatz Projekts.

mcb
#6
13.12.2016 14:14
Beiträge: 1285

Ich müsste da auch mal ne neue Version von schreiben. Die jetzige hat da so ein paar Schwächen (unter anderem die ziemlich aufwändige Implementierung des LowPriorityJobs).
Hast du den Debugger an? Der braucht ordentlich Laufzeit und eine realitätsnahe Messung ist kaum noch möglich.
Ansonsten kannst du mal mit mcbTrigger.DEBUG_SearchBadPerformanceTrigger() den Trigger suchen, der am langsamsten ist, und dir mit mcbTrigger.DEBUG_GetInfo(tId) ausgeben lassen, welcher das ist. (Dafür kannst du durchaus den Debugger nehmen, der macht nur alles gleichmäßig langsamer)

Messoras
#7
13.12.2016 15:16
Beiträge: 61

Zitat von mcb:
Ich müsste da auch mal ne neue Version von schreiben. Die jetzige hat da so ein paar Schwächen (unter anderem die ziemlich aufwändige Implementierung des LowPriorityJobs).
Hast du den Debugger an? Der braucht ordentlich Laufzeit und eine realitätsnahe Messung ist kaum noch möglich.
Ansonsten kannst du mal mit mcbTrigger.DEBUG_SearchBadPerformanceTrigger() den Trigger suchen, der am langsamsten ist, und dir mit mcbTrigger.DEBUG_GetInfo(tId) ausgeben lassen, welcher das ist. (Dafür kannst du durchaus den Debugger nehmen, der macht nur alles gleichmäßig langsamer)



Bin jetzt an einer Linux Maschiene ohne alles. Habe Siedler mit Wine zum laufen gebracht und wollte einfach ein bisschen basteln. Der Debugger geht leider gar nicht.
Ich weiß genau welcher Trigger zu langsam ist, nämlich der erste mit dem ich einen CustomNPC erstellen will. Ich durchschaue die Meldung allerdings nicht ganz.

Trigger runtime too long: 0.501..


Will der mir sagen, dass ein Durchlauf der Methode zu lange dauert, oder wie?

Gruß,
Messoras

____________________
Six feet of earth make us all equal.

Spielt Siedler 5 online mit mir, dank Jojos und Yoqs Ubi.com Ersatz Projekts.

mcb
#8
13.12.2016 15:39
Beiträge: 1285

Sie bedeutet, das alle Trigger (= SimpleJob) zusammen zu lange brauchen. (0.5 Sekunden ist schon verdammt viel. Auf so einen Wert komme ich höchstens wenn ich ne komplette Kürzeste-Wege Abfrage über die ganze Map mache...)
Was hast du denn alles für Jobs laufen? Die fiesen Performancefresser die ich kenne, fallen mir in deiner Comfort jetzt nicht auf... (Sowas wie über alle Entitys iterieren)

Messoras
#9
13.12.2016 17:34
Beiträge: 61

Zitat von mcb:
Sie bedeutet, das alle Trigger (= SimpleJob) zusammen zu lange brauchen. (0.5 Sekunden ist schon verdammt viel. Auf so einen Wert komme ich höchstens wenn ich ne komplette Kürzeste-Wege Abfrage über die ganze Map mache...)
Was hast du denn alles für Jobs laufen? Die fiesen Performancefresser die ich kenne, fallen mir in deiner Comfort jetzt nicht auf... (Sowas wie über alle Entitys iterieren)



Sry, hatte mich verschrieben. Es waren 0.0501.. und zwar auf einer neuen Testmap ohne andere Jobs.
Ich habe deine EntityFind Comfort, die das starten des TriggerFix verhindert hat jetzt bei meiner Map rausgenommen und habe da jetzt auch die Meldung mit ca 0.6.
Vielleicht fressen die Typabfragen zu viel Zeit?

Edit: Merkwürdig, ich habe nicht verändert, das ganze einfach mal neu gestartet und jetzt hat es funktioniert o.O

Gruß,
Messoras

____________________
Six feet of earth make us all equal.

Spielt Siedler 5 online mit mir, dank Jojos und Yoqs Ubi.com Ersatz Projekts.

Dieser Beitrag wurde von Messoras am 13.12.2016 um 17:41 editiert.

Settlerman
#10
19.12.2016 11:09
Beiträge: 236

Frage: Wenn ich jetzt den mcbTrigger verwende, kann ich dann auch einem SimpleJob Parameter übergeben, oder?

mcb
#11
19.12.2016 12:05
Beiträge: 1285

Ja, einfach:

StartSimpleJob(function(a, b, c)
   -- do something
end, a, b, c)

Settlerman
#12
19.12.2016 12:12
Beiträge: 236

Gut, danke! Genau nach sowas hab ich gesucht.

mcb
#13
19.12.2016 12:32
Beiträge: 1285

StartSimpleJob sind nur 3 Zeilen:

StartSimpleJob = function(f, ...)
	return Trigger.RequestTrigger(Events.LOGIC_EVENT_EVERY_SECOND, nil, f, 1, nil, arg)
end


Aber ohne den Rest davon könntest du keine tables oder Funktionen übergeben.

Seiten: 1

SiteEngine v1.5.0 by nevermind, ©2005-2007
Design by SpiderFive (www.siedler-games.de) - English translation by juja

Impressum