Baumenü-Gebäudedreh

» Siedler Map Source Forum » Siedler DEdK Script Forum » Baumenü-Gebäudedreh

Seiten: 1

jojo818
#1
06.12.2016 16:27
Beiträge: 31

Baumenü-Gebäudedreh

Servus leute,
ich versuche schon seit längerem Gebäude vor dem Bau drehen zu können, doch dabei
kenne ich nur eine variante:

 Logic.CreateConstructionSite(_posX, _posY, _rotation, _entityType, _playerId) 



doch leider gibt es dabei Probleme wie:

1) Falls es im Baumenü eingebaut wird, gibt es keine vorschau (logisch) original:

 GUI.ActivatePlaceBuildingState(_upCat) 



2) Logic + Create ist z.b im multiplayer nicht nutzbar, da die funktion nur clientside angebracht ist.

Ist es irgendwie via s5hook oder functions möglich vor dem Bau Gebäude zu drehen?
(rotation wird vorab eingestellt)

schonmal dake im vorraus!

____________________
Siedler DEdK / Siedler 5 Multiplayer!

mcb
#2
06.12.2016 16:49
Beiträge: 1472

Ich hab mal Gebäude relativ zur Burg gedreht gesetzt, so das am Ende ein Palisadenumzäuntes Dorf raus kam. Mit dem Hook bräuchte man nicht mal mehr die Uni-Buttons dafür
De Gebäude bei GUI.ActivatePlaceBuildingState zu drehen wäre ein ganz anderes Problem, dazu müsste man wohl den Hook erweitern.

Zur Synchronisation im MP: Rein Theoretisch kanst du für alles Tribute erzeugen und dann passend Auslösen. So hat Noigi sogar Mauspositionen in DOTA synchronisiert. Man könnte auch ausprobieren, die Tribute über Messages und S5Hook.Eval zu erstellen und dann auszulösen. Ich hab mich ehrlich gesagt aber nicht allzu viel mit MP beschäftigt, also das ganze besser mit Vorsicht genießen

jojo818
#3
06.12.2016 18:47
Beiträge: 31

multiplayer = "clientside" + actions

findet man einen weg bestimme befehle zu senden, könnte man theoretisch viel synchronisieren


zum gebäudedrehbeispiel von dir, könntest du mir ein Beispiel(code) geben?

____________________
Siedler DEdK / Siedler 5 Multiplayer!

mcb
#4
06.12.2016 22:54
Beiträge: 1472

Das Syncen stell ich mir ungefär so vor:

--- mcbMPSyncer		mcb		v2.0
-- Ermöglicht es, beliebige Lua-Ausdrücke auf allen verbundenen PCs zu parsen und synchron auszuführen.
-- Wird das Script im SP geladen, wird keines Synchronisierung durchgeführt.
-- 
-- mcbMPSyncer.init(player)							Aus der GameCallback_OnGameStart aufrufen, bei player werden Tribute zur Synchronisierung erstellt.
-- 
-- mcbMPSyncer.executeSynced(vname, ...)			Ruft die Virtuelle Funktion vname synchron auf allen PCs mit den übergebenen Argumenten arg auf.
-- 
-- mcbMPSyncer.allConnected							Gibt an, ob schon alle PCs gestartet sind. Wenn nicht, sind keine synchronisierungen möglich.
-- 
-- mcbMPSyncer.virtualFuncs.create(func, vname, ...)
-- 													Erstellt eine virtuelle Funktion vname, mit den virtuellen Argumenttypen arg,
-- 													die func mit den übergebenen Argumenten aufruft.
-- 
-- mcbMPSyncer.virtualFuncs.argumentTypeInt()		Gibt den Argumenttyp für Lua-number zurück.
-- mcbMPSyncer.virtualFuncs.argumentTypeString()	Gibt den Argumenttyp für Lua-string zurück.
-- 
-- mcbMPSyncer.virtualFuncs.patchLuaFunc(fname, ...)
-- 													Schnelle Möglichkeit, eine Funktion zu synchronisieren. Ersetzt _G[fname] mit einer Funktion, die
-- 													automatisch Synchronisiert. arg sind die Argumenttypen number/string.
-- 
-- Anmerkungen:
-- vname: Darf nur aus Buchstaben bestehen, keine Zahlen/Sonderzeichen.
-- patchLuaFunc: Die zu patchende Funktion muss über einen tablezugriff aus _G erreichbar sein, Außerdem darf der Funktionsname keine Zahlen/Sonderzeichen enthalten.
-- Positionen als Argumente: Eine Position p ist nur ein table mit X und Y Eintragen. Einfach p.X und p.Y als einzelne number-Argumente übertragen und in der Funktion mit p = {X=X, Y=Y} wieder zusammenbauen.
-- Entitys als Argumente: Ich empfehle, die id als number zu übertragen.
-- Aufruf Synchronisierter Funktionen: Die Synchronisierungs-Funktionen müssen immer aus einem asynchronen Status ausgeführt werden, sonst wird der Aufruf vervielfältigt (Notfalls per Vergleich mit GUI.GetPlayerID() sicherstellen).
-- 
-- Beispiel 1:
-- 	Script:
-- 		function foo(s, n)
-- 			for i=1,n do
-- 				Message(s)
-- 			end
-- 		end
-- 	
-- 	FMA:
-- 		mcbMPSyncer.init(8)
-- 		mcbMPSyncer.virtualFuncs.patchLuaFunc("foo", "string", "number")
-- 	
-- 	Aufruf:
-- 		foo("bar", 5)
-- 
-- Beispiel 2:
-- 	FMA:
-- 		mcbMPSyncer.init(8)
-- 		mcbMPSyncer.virtualFuncs.create(function(s, n)
-- 				for i=1,n do
-- 				 		Message(s)
-- 			 	end
-- 			end, "foo", mcbMPSyncer.virtualFuncs.argumentTypeString(), mcbMPSyncer.virtualFuncs.argumentTypeInt())
-- 	
-- 	Aufruf:
-- 		mcbMPSyncer.executeSynced("foo", "bar", 5)
-- 
-- Beide Beispiele fürhren zum exakt selben Ergebniss. Nr1 ist einfacher für Anfänger und leichter in bestehende SP-Scripte einbaubar,
-- 		Nr2 ist deutlich Flexibler.
-- 
-- Benötigt:
-- - S5Hook (optional) (Logging/debug Eval)
-- 
mcbMPSyncer = {nextTribute = 0, playerAck={}, warnings={}, connected={}, allConnected=false, whitelist={}}
function mcbMPSyncer.init(player)
	if not mcbMPSyncer.isMP() then
		mcbMPSyncer.allConnected = true
		return
	end
	mcbMPSyncer.MPGame_ApplicationCallback_ReceivedChatMessage = MPGame_ApplicationCallback_ReceivedChatMessage
	MPGame_ApplicationCallback_ReceivedChatMessage = function(msg, allied, sender)
		mcbMPSyncer.log("recieved message from "..sender.." to "..(allied==1 and "allied" or "all")..": "..msg)
		local start = string.sub(msg, 1, 2)
		local en = string.sub(msg, 3)
		if start=="@f" then
			mcbMPSyncer.recievedFunc(en, sender)
		elseif start=="@a" then
			mcbMPSyncer.recievedAck(en, sender)
		elseif start=="@w" then
			if not mcbMPSyncer.warningRepeat then
				table.insert(mcbMPSyncer.warnings, en)
				GUI.AddStaticNote(en)
				mcbMPSyncer.log("warning recieved: "..en)
			end
		elseif start=="@i" then
			mcbMPSyncer.recievedInit(en)
		elseif start=="@s" then
			mcbMPSyncer.allConnected = true
			mcbMPSyncer.log("player "..sender.." finished loading first, all connected, ready to play")
		else -- normal message
			mcbMPSyncer.MPGame_ApplicationCallback_ReceivedChatMessage(msg, allied, sender)
		end
	end
	mcbMPSyncer.nextTribute = GUI.GetPlayerID()
	mcbMPSyncer.player = player
	GameCallback_FulfillTribute = function() return 1 end
	if LuaDebugger.Log then
		XNetwork.Chat_SendMessageToAll("@wWarning: Player "..GUI.GetPlayerID().." ("..XNetwork.GameInformation_GetLogicPlayerUserName(GUI.GetPlayerID())..") startet this map with active Debugger!")
	end
	mcbMPSyncer.connected[GUI.GetPlayerID()] = true
	XNetwork.Chat_SendMessageToAll("@i"..GUI.GetPlayerID())
end

function mcbMPSyncer.log(txt)
	if LuaDebugger.Log then
		LuaDebugger.Log(txt)
	end
	if S5Hook then
		S5Hook.Log("mcbMPSyncer: "..txt)
	end
end

function mcbMPSyncer.executeSynced(f, ...)
	if not mcbMPSyncer.isMP() then
		mcbMPSyncer.log("SP mode, direct executing")
		--S5Hook.Eval(f)()
		mcbMPSyncer.virtualFuncs.funcs[f].func(unpack(arg))
		--local t = mcbMPSyncer.virtualFuncs.serialize(f, unpack(arg))
		--t = mcbMPSyncer.virtualFuncs.deserialize(t)
		--mcbMPSyncer.virtualFuncs.execute(t)
		return
	end
	assert(mcbMPSyncer.allConnected)
	local cntxt = {}
	for i=1,XNetwork.GameInformation_GetMapMaximumNumberOfHumanPlayer() do
		if XNetwork.GameInformation_IsHumanPlayerAttachedToPlayerID(i)==1 and Logic.PlayerGetLeftGameFlag(i)==0 then
			cntxt[i] = "needAck"
		end
	end
	local tId = mcbMPSyncer.nextTribute
	mcbMPSyncer.nextTribute = mcbMPSyncer.nextTribute + 8
	mcbMPSyncer.playerAck[tId] = cntxt
	f = mcbMPSyncer.virtualFuncs.serialize(f, unpack(arg))
	assert(mcbMPSyncer.virtualFuncs.deserialize(f)) -- be sure, deserialisation is possible
	mcbMPSyncer.log("host mode: sended tribute "..tId.." for call "..f)
	XNetwork.Chat_SendMessageToAll("@f"..tId..":"..f)
end

function mcbMPSyncer.recievedFunc(f, sender)
	local st, en = string.find(f, "^%d+:")
	local id = string.sub(f, st, en-1)
	local toEval = string.sub(f, en+1)
	--if not mcbMPSyncer.acceptSyncCall(toEval) then
	--	XNetwork.Chat_SendMessageToAll("@wPlayer "..GUI.GetPlayerID()
	--		.." rejected sync Message. Please check for cheating attempt or missing whitelist entry. Rejected message: "..toEval
	--	)
	--	return
	--end
	--local func = S5Hook.Eval(toEval)
	local func = mcbMPSyncer.virtualFuncs.deserialize(toEval)
	--assert(type(func)=="function")
	local trib = {
		Tribute = tonumber(id),
		pId = mcbMPSyncer.player,
		func = func,
		Callback = function(t)
			mcbMPSyncer.log("client mode: synced executing "..t.Tribute)
			--t.func()
			mcbMPSyncer.virtualFuncs.execute(t.func)
		end,
	}
	Logic.AddTribute(mcbMPSyncer.player, trib.Tribute, 0, 0, "", ResourceType.Gold, 0)
	SetupTributePaid(trib)
	mcbMPSyncer.log((sender==GUI.GetPlayerID() and "host mode: " or "client mode: ").."recieved tribute "..id.." from "..sender.." for "..toEval)
	XNetwork.Chat_SendMessageToAll("@a"..id)
end

function mcbMPSyncer.recievedAck(a, sender)
	a = tonumber(a)
	if mcbMPSyncer.playerAck[a] then
		mcbMPSyncer.playerAck[a][sender] = "acked"
		mcbMPSyncer.log("host mode: ack recieved for "..a.." from "..sender)
		mcbMPSyncer.checkAcks(a)
	else
		mcbMPSyncer.log("client mode: dropped ack for "..a.." from "..sender..", not for me")
	end
end

function mcbMPSyncer.checkAcks(id)
	local cntxt = mcbMPSyncer.playerAck[id]
	for i=1,XNetwork.GameInformation_GetMapMaximumNumberOfHumanPlayer() do
		if cntxt[i] == "needAck" then
			mcbMPSyncer.log("host mode: waiting for more acks to execute "..id)
			return
		end
	end
	mcbMPSyncer.log("host mode: started synced execution "..id)
	GUI.PayTribute(mcbMPSyncer.player, id)
	mcbMPSyncer.playerAck[id] = nil
end

function mcbMPSyncer.recievedInit(player)
	local p = tonumber(player)
	if p==GUI.GetPlayerID() then
		return
	end
	mcbMPSyncer.log("init recieved from "..p)
	mcbMPSyncer.warningRepeat = true
	for _,s in ipairs(mcbMPSyncer.warnings) do
		XNetwork.Chat_SendMessageToAll("@w"..s)
	end
	mcbMPSyncer.warningRepeat = nil
	mcbMPSyncer.connected[p] = true
	for i=1,XNetwork.GameInformation_GetMapMaximumNumberOfHumanPlayer() do
		if XNetwork.GameInformation_IsHumanPlayerAttachedToPlayerID(i) and not mcbMPSyncer.connected[i] then
			return
		end
	end
	mcbMPSyncer.log("got all inits, sending start")
	XNetwork.Chat_SendMessageToAll("@s")
end

function mcbMPSyncer.isMP()
	if XNetworkUbiCom.Manager_DoesExist()==1 then
		return 2
	elseif XNetwork.Manager_DoesExist()==1 then
		return 1
	else
		return false
	end
end

function mcbMPSyncer.acceptSyncCall(f)
	for _, pattern in ipairs(mcbMPSyncer.whitelist) do
		if string.find(f, pattern) then
			return true
		end
	end
	return false
end

function mcbMPSyncer.addToWhitelist(pattern)
	table.insert(mcbMPSyncer.whitelist, "^"..pattern.."$")
end

mcbMPSyncer.virtualFuncs = {funcs={}}

function mcbMPSyncer.virtualFuncs.create(func, vname, ...)
	assert(string.find(vname, "^%w+$"))
	local pattern = "^"..vname.."%("
	local serializer = {}
	local deserializer = {}
	for i,a in ipairs(arg) do
		pattern = pattern..a.pattern..", "
		table.insert(serializer, a.serialize)
		table.insert(deserializer, a.deserialize)
	end
	pattern = pattern.."%)$"
	local t = {pattern = pattern, serializer = serializer, deserializer = deserializer, func = func}
	mcbMPSyncer.virtualFuncs.funcs[vname] = t
	return t
end

function mcbMPSyncer.virtualFuncs.argumentTypeInt()
	return {pattern="(%d+%.?%d*)", serialize=tostring, deserialize = tonumber}
end

function mcbMPSyncer.virtualFuncs.argumentTypeString()
	return {pattern = "\"(%w*)\"",
		serialize = function(s)
		return '"'..s..'"'
	end, deserialize = function(s)
		return s
	end}
end

function mcbMPSyncer.virtualFuncs.parse(vfunc, str)
	local t = {string.find(str, vfunc.pattern)}
	if not t[1] then
		return
	end
	local context = {vfunc.func}
	for i=1, table.getn(vfunc.deserializer) do
		local dsr = vfunc.deserializer[i]
		local ar = t[i+2]
		context[i+1] = dsr(ar)
	end
	return context
end

function mcbMPSyncer.virtualFuncs.deserialize(string)
	for vname, vfunc in pairs(mcbMPSyncer.virtualFuncs.funcs) do
		local r = mcbMPSyncer.virtualFuncs.parse(vfunc, string)
		if r then
			return r
		end
	end
end

function mcbMPSyncer.virtualFuncs.serialize(vname, ...)
	local vfunc = mcbMPSyncer.virtualFuncs.funcs[vname]
	local str = vname.."("
	for i=1, table.getn(vfunc.serializer) do
		local a = arg[i]
		local sr = vfunc.serializer[i]
		str = str..sr(a)..", "
	end
	str = str..")"
	return str
end

function mcbMPSyncer.virtualFuncs.execute(context)
	local fnc = table.remove(context, 1)
	if LuaDebugger.Log then
		fnc(unpack(context))
	else
		xpcall(function()
			fnc(unpack(context))
		end, function(e)
			XNetwork.Chat_SendMessageToAll("Lua Error at player "..GUI.GetPlayerID()..": "..e)
		end)
	end
end

function mcbMPSyncer.virtualFuncs.patchLuaFunc(fname, ...)
	if not mcbMPSyncer.isMP() then
		return
	end
	local f = _G[fname]
	assert(type(f)=="function")
	local vname = string.gsub(fname, "%.", "")
	vname = string.gsub(vname, "_", "")
	local varg = {}
	for _, atyp in ipairs(arg) do
		if atyp=="string" then
			table.insert(varg, mcbMPSyncer.virtualFuncs.argumentTypeString())
		elseif atyp=="number" then
			table.insert(varg, mcbMPSyncer.virtualFuncs.argumentTypeInt())
		end
	end
	mcbMPSyncer.virtualFuncs.create(f, vname, unpack(varg))
	_G[fname] = function(...)
		mcbMPSyncer.executeSynced(vname, unpack(arg)) -- uses upvalue vname. never use this in SP!
	end
end

function mcbMPSyncer.virtualFuncs.debug_createEval()
	mcbMPSyncer.virtualFuncs.create(function(s)
		S5Hook.Eval(s)()
	end, "Eval", {pattern = "\"(.*)\"",		-- .* is greedy, takes as much as possible.
		serialize = function(s)
			return '"'..s..'"'
		end, deserialize = function(s)
			return s
		end
	})
end


(Jetzt mal schnell und ungetestet zusammengehackt )
Einfach mcbMPSyncer.init mit dem Player, bei dem die Tribute erstellt werden sollen aus der FMA aufrufen. Danach können per mcbMPSyncer.callFuncSynced Funktionen aufgerufen werden, der Aufruf musss als String übergeben werden:

mcbMPSyncer.executeSynced('Message("Test")')


Wie gesagt, ich hab nicht so die Ahnung von MP, muss also unbedingt getestet werden, auch ob GUI.GetPlayerID() die richtige playerId zurückgibt und der Sender die Message überhaupt über den Callback erhält.

Zum Gebäudedrehen: Mein Code würde dir nicht viel weiterhelfen, ich hab einfach einen Button gehackt und dann relativ zum Selektierten Gebäude eine Baustelle gesetzt.

Edit: 2 Bugs gefixt
Edit2: v0.9
Edit3: v1.0
Edit4: v1.1
Edit5: v2.0

Dieser Beitrag wurde von mcb am 29.03.2018 um 16:37 editiert.

mcb
#5
07.12.2016 18:52
Beiträge: 1472

Ich hab das ganze jetzt mal getestet und ausgebaut.
Das ganze funktioniert ohne größere Probleme, solange keine Synchronisierungsanfragen gestellt werden, bevor alle Spieler die Map auch tatsächlich geladen haben.

Wer so eine Map mit aktiviertem Debugger startet, sendet automatisch eine Warnung an alle anderen Spieler.

TODO: Das ganze System abschalten, wenn die map im SP gestartet wird.

mcb
#6
10.12.2016 15:18
Beiträge: 1472

Um das ganze entgültig zum laufen zu bringen, hätte ich noch ein paar Fragen:
- Wie kann man rausfinden, ob ein Player tatsächlich ein Mensch ist? (Wichtig, sonst müssen alle Player bis zum Maximum besetzt sein)
- Kann man rausfunden, ob die Map bei allen schon gestartet ist? (Nicht so wichtig, init-message als Workaround)
- Wie kann man erkennen, ob die Map als Singleplayer gestartet wurde? (Wichtig, erlaubt konsistente Aufrufe in SP und MP)

MadShadow
#7
10.12.2016 17:27
Beiträge: 372

Meine Erfahrung dazu, du kannst bestimmt das für dich nützliche raussuchen.
Zu 1

-- **************************************************************************** --
-- *                        GetConnections                                    * --
-- * sets up a table containing all connected players                         * --
-- **************************************************************************** --

function EMS.Tools.GetConnections()
	local connections = {};
	if EMS.Tools.GetNetworkMode() > 1 then
		for playerId = 1,8 do
			if XNetwork.GameInformation_IsHumanPlayerAttachedToPlayerID(playerId) == 1 then
				table.insert(connections, playerId);
			end
		end
	else
		connections = {1};
	end
	return connections;
end



Zu 2
Obs konkret eine Funktion gibt das abzufragen weiss ich nicht.
Vermute du musst den MessageInit nehmen oder alternativ einen SimpleJob starten.
Nachdem start im MP pausiert das Spiel und wartet bis alle Rechner gestartet haben. Der Simplejob läuft solange nicht weiter. Wenn du also im ~2Tick einen Aufruf machst, sollte der dann auf allen Rechnern synchron sein.

Zu 3

-- **************************************************************************** --
-- *                            GetNetworkMode                                * --
-- * returns the 'networkmode'                                                * --
-- * 1 => 'singleplayer'                                                      * --
-- * 2 => 'LAN-Multiplayer'                                                   * --
-- * 3 => 'UbiCom-Multiplayer'                                                * --
-- **************************************************************************** --

function EMS.Tools.GetNetworkMode()
	if XNetworkUbiCom.Manager_DoesExist() == 1 then
		return 3;
	elseif XNetwork.Manager_DoesExist() == 1 then
		return 2;
	else
		return 1;
	end
end

mcb
#8
10.12.2016 20:10
Beiträge: 1472

Danke, Version 1.0 läuft jetzt so wie ich es erwarte. Vielleicht kanns noch jemand brauchen

mcb
#9
15.01.2017 16:47
Beiträge: 1472

Version 1.1 hat jetzt ein eingebautes Pattern-Matching mit einer whitelist (nur explizit erlaubte Aufrufe werden zugelassen).

Play4FuN
#10
29.03.2018 12:42
Beiträge: 704

Hab deinen Syncher mal probiert, mcb. Ist der Aufruf falsch? Müsste die ID anders übergeben werden und der Ziel-Spieler weiß nicht, welche Einheit gechanged werden soll? Es kommt immer ein Fehler mit dem Hinweis, dass evtl ein Cheatversuch vorliegt. Dann kommt's zum Desynch. Hier ist, was ich vorhabe:

function HackExpellSettler()
	GUIAction_ExpelSettler_Orig = GUIAction_ExpelSettler
	GUIAction_ExpelSettler = function()
		if (XGUIEng.IsModifierPressed(Keys.ModifierShift) == 1) then
			local selection = { GUI.GetSelectedEntities() }
			if selection == nil then
				return
			end
			
			local player = GetPlayer(selection[1])
			for i = 1, table.getn(selection) do
			
				if (Logic.IsHero(selection[i]) == 0) and ((Logic.IsLeader(selection[i]) == 1) or (Logic.IsSerf(selection[i]) == 1)) then
					local allyTable = GetAlliesOfPlayer(player)
					if allyTable[1] == nil then return end
					local rnd = math.random(1, table.getn(allyTable))
					local newPlayer = allyTable[rnd]
					if (Logic.GetPlayerAttractionLimit(newPlayer) - Logic.GetPlayerAttractionUsage(newPlayer)) > 0 then
						-- #todo: Desynch!
						ChangePlayer(selection[i], newPlayer)
						--mcbMPSyncer.executeSynced('ChangePlayer(selection[i], newPlayer)')
					end
				end
			end
		else
			GUIAction_ExpelSettler_Orig()
		end
	end
end


GetAlliesOfPlayer gibt einfach nur ein Table mit allen Allies zurück.

Ich habe das auch noch wie folgt getestet:

function ChangePlayerSafe(entityID, _player)
	if Logic.IsLeader(entityID) == 1 then
		local SoldiersList = {Logic.GetSoldiersAttachedToLeader(entityID)}
		local i
		for i = 2, SoldiersList[1] + 1 do
			Logic.ChangeEntityPlayerID(SoldiersList[i], _player)
		end
		local NewLeaderID = Logic.ChangeEntityPlayerID(entityID, _player)
		local j
		for j = 1, SoldiersList[1] do
			Logic.LeaderGetOneSoldier( NewLeaderID )
		end
	else
		Logic.ChangeEntityPlayerID(entityID,_player)
	end
end

anstelle des normalen ChangePlayer, was bei Truppen Tools nutzt und soweit ich weiß sind wenigstens die Logic-Funktionen synchron ... dachte ich jedenfalls denn auch mit meinem Code (der im SP macht, was er soll) kommt es zum Desynch, jedoch auch schon, wenn nur Leibeigene ausgewählt sind, also nur Logic.ChangeEntityPlayerID(entityID,_player) aufgerufen werden sollte.

____________________
LG Play4FuN

Siedler DEdK Mapping + Scripting Tutorials

mcb
#11
29.03.2018 13:43
Beiträge: 1472

Das Problem ist der Aufruf

mcbMPSyncer.executeSynced('ChangePlayer(selection[i], newPlayer)')


und die Funktion von Eval. Eval nimmt den übergebenen string und lädt ihn wie ein extra Lua-file. Das heißt dann das deine Variablen selection und newPlayer nicht mehr existieren (und auf anderen Systemen schon garnicht.)

Lösen lässt sich das jetzt damit, das du die Variablen beim String-erstellen ausliest und die darin gespeicherten ids versendest:

mcbMPSyncer.executeSynced('ChangePlayer('..selection[i]..', '..newPlayer..')')

Was dann z.B. zu sowas verarbeitet wird:

mcbMPSyncer.executeSynced('ChangePlayer(123456, 1)')


Je nach Version müsstest du jetzt noch einen Eintrag in der Whitelist machen, damit das ganze auch akzepiert wird.

Ich habe aber auch eine neuere Version, bei der ich die Aufrufe selbst parse und wo es eine relativ einfache Möglichkeit gibt eine Fuktion zu "patchen", sodass automatisch synchronisiert wird. Müsste ich allerdings mal testen (und ich hab grad keinen 2. PC hier).

Play4FuN
#12
29.03.2018 14:53
Beiträge: 704

String erstellen aka ...

local s = id..","..newPlayer
						mcbMPSyncer.executeSynced('ChangePlayer(s)')


...?

Und mein whitelist Eintrag würde so aussehen:

mcbMPSyncer.addToWhitelist("ChangePlayer%(%d+\",\"%d%)")

wenn ich deine Syntax richtig verstanden habe.

____________________
LG Play4FuN

Siedler DEdK Mapping + Scripting Tutorials

mcb
#13
29.03.2018 15:21
Beiträge: 1472

Dein Lua Code baut ein Lua File zusammen. Und ChangePlayer(s) ist kein sinnvolles Lua (s nicht definiert).
ChangePlayer(123456, 1) schon.

Einfach das hier

mcbMPSyncer.executeSynced('ChangePlayer('..selection[i]..', '..newPlayer..')')

anstatt deinem ChangePlayer einfügen.

(Oder du hilfst mir, die neueste version zu testen, dann kannst du die verwenden, wenn sie funktioniert)

Außerdem wäre der Whitelist eintrag so:

mcbMPSyncer.addToWhitelist("ChangePlayer%(%d+, %d+%)")

(2 ints)

Play4FuN
#14
29.03.2018 15:32
Beiträge: 704

Können wir machen. Wie stellst du dir das vor? Habe die nächsten Tage noch ein wenig Zeit für S5 (bin auch gerne mal für Koops oder PvP online...) Die Einschränkung wäre lediglich, dass mich Discord nicht mag bzw vice versa.

btt: Truppen werden (als p1) erfolgreich zu p2 übertragen und können von dem auch gesteuert werden, aber einen Desynch erhalte ich dennoch.

____________________
LG Play4FuN

Siedler DEdK Mapping + Scripting Tutorials

Dieser Beitrag wurde von Play4FuN am 29.03.2018 um 15:44 editiert.

mcb
#15
29.03.2018 16:00
Beiträge: 1472

Müsste dir halt ne Testmap schicken (eventuell mehrmals). Und du müsstest hosten können. Dann map starten, ich geb ein paar Befehle per Debugger ein und wir sehen nach obs desyncs gibt.

Hast du das normale ChangePlayer rausgenommen? (Ansonsten würde ich einfach versuchen den die komplette Übergabe zu synchronisieren. Also alles nach dem IsModifierPressed in ne Funktion auslagern und die über den syncer aufrufen.)

Play4FuN
#16
29.03.2018 16:09
Beiträge: 704

Ach ich weiß nicht, habe nen "workaround" gemacht mit nem table, in welches alle zu changenden Einheiten reingeschrieben werden, aber das will auch nicht so ganz... ich werd das aber jetzt hinten anstellen, diese Funktion wäre nur ein nettes Extra.

Jou, hosten kann ich.

____________________
LG Play4FuN

Siedler DEdK Mapping + Scripting Tutorials

Dieser Beitrag wurde von Play4FuN am 29.03.2018 um 16:31 editiert.

mcb
#17
29.03.2018 16:40
Beiträge: 1472

Hab schon jemand anderen gefunden.

Version 2.0:
Ohne Hook mit eigenem Parser und virtuellen Funktionen. Automatische pattern-Erstellung und _G funktionspatcher inklusive. (Am besten die Beispiele ansehen).

mcb
#18
28.05.2019 18:26
Beiträge: 1472

So, neueste Version vom mcbMPSyncer lässt sich hier finden: https://github.com/mcb5637/s5C.../comfort/other/mcbMPSyncer.lua
Version 3.0b hat automatische Erkennung für Kimichuras Modserver und sollte ein einheitliches Interface für diesen und den Nachbau bieten.

Seiten: 1

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

Impressum