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