Baumenü-Gebäudedreh

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

Pages: 1

jojo818
#1
06-12-2016 16:27
Posts: 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!

____________________
Auch bekannt als GameRaider

Siedler DEdK / Siedler 5 Multiplayer!

mcb
#2
06-12-2016 16:49
Posts: 1108

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
Posts: 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?

____________________
Auch bekannt als GameRaider

Siedler DEdK / Siedler 5 Multiplayer!

mcb
#4
06-12-2016 22:54
Posts: 1108

Das Syncen stell ich mir ungefär so vor:

--- mcbMPSyncer		mcb		v1.1
-- 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(f)					f wird an alle PCs übertragen und geparst, dann synchron ausgeführt.
-- 
-- mcbMPSyncer.allConnected						Gibt an, ob schon alle PCs gestartet sind. Wenn nicht, sind keine synchronisierungen möglich.
-- 
-- mcbMPSyncer.addToWhitelist(pattern)			Fügt ein Pattern zur integrierten Whitelist hinzu.
-- 												Nur Sync-Anfragen, bei denen eines der Pattern matcht, werden ausgeführt.
-- 												Niemals mcbMPSyncer.addToWhitelist über ein Pattern freigeben!
-- 
-- Beispielfunktion:
-- Aufruf der Funktion foo("bar") aus einer nicht synchronen Funktion -> Desync
-- Aufruf von mcbMPSyncer.executeSynced('foo("bar")') aus einer nicht synchronen Funktion -> Automatische synchronisierung und Ausführung
-- 
-- Whitelist-Pattern Beispiel:
-- "foo%(\"bar\"%)" Matcht foo("bar") (und nur dieses).
-- "foo%(\"%w*\"%)" Matcht foo("XXX") wobei XXX ein beliebiger String aus Zahlen und Buchstaben ist.
-- "%d+" Matcht eine Integer-Zahl.
-- "%d+%.?%d*" Matcht eine Float oder Integer-Zahl.
-- "\"%w*\"" Matcht einen String aus Zahlen und Buchstaben (0-9,a-z,A-Z).
-- "%s+" Matcht ein oder mehrere Whitespaces.
-- "[%w_%.]+" Matcht eine Variable (mit Tablezugriff) (sowas wie foo.bar_1).
-- "[%w_%.]+%s+=%s+%d+%.?%d*" Matcht eine Zuweisung einer Zahl an eine beliebige Variable.
-- "foo%(\"%w*\",%s*%d+%.?%d*,%s*{%s*X%s*=%s*%d+%.?%d*,%s*Y%s*=%s*%d+%.?%d*%s*}%%s*)" Matcht den Aufruf von foo mit einem String, einer Zahl und einer Position.
-- 
-- Benötigt:
-- - S5Hook
-- 
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)
				S5Hook.Log("mcbMPSyncer: 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
end

function mcbMPSyncer.executeSynced(f)
	if not mcbMPSyncer.isMP() then
		mcbMPSyncer.log("SP mode, direct executing")
		S5Hook.Eval(f)()
		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
	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)
	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()
		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


(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

This posting has been edited by mcb: 15-01-2017 at 16:46.

mcb
#5
07-12-2016 18:52
Posts: 1108

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
Posts: 1108

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
Posts: 337

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
Posts: 1108

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

mcb
#9
15-01-2017 16:47
Posts: 1108

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

Pages: 1

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

Impressum