Working
This commit is contained in:
@@ -45,14 +45,51 @@ local function resolveVehicle(id)
|
|||||||
return getVehicleById(id)
|
return getVehicleById(id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function reconcilePairState(vehicleA, vehicleB, attachmentA, attachmentB)
|
||||||
|
if not vehicleA or not vehicleB then return end
|
||||||
|
|
||||||
|
if TowBarMod.Utils and TowBarMod.Utils.updateAttachmentsForRigidTow then
|
||||||
|
TowBarMod.Utils.updateAttachmentsForRigidTow(vehicleA, vehicleB, attachmentA, attachmentB)
|
||||||
|
end
|
||||||
|
|
||||||
|
local towingMd = vehicleA:getModData()
|
||||||
|
local towedMd = vehicleB:getModData()
|
||||||
|
local currentScript = vehicleB:getScriptName()
|
||||||
|
|
||||||
|
if towingMd then
|
||||||
|
towingMd["isTowingByTowBar"] = true
|
||||||
|
vehicleA:transmitModData()
|
||||||
|
end
|
||||||
|
if towedMd then
|
||||||
|
if towedMd.towBarOriginalScriptName == nil and currentScript ~= "notTowingA_Trailer" then
|
||||||
|
towedMd.towBarOriginalScriptName = currentScript
|
||||||
|
end
|
||||||
|
if towedMd.towBarOriginalMass == nil then
|
||||||
|
towedMd.towBarOriginalMass = vehicleB:getMass()
|
||||||
|
end
|
||||||
|
if towedMd.towBarOriginalBrakingForce == nil then
|
||||||
|
towedMd.towBarOriginalBrakingForce = vehicleB:getBrakingForce()
|
||||||
|
end
|
||||||
|
towedMd["isTowingByTowBar"] = true
|
||||||
|
towedMd["towed"] = true
|
||||||
|
vehicleB:transmitModData()
|
||||||
|
end
|
||||||
|
|
||||||
|
vehicleB:setScriptName("notTowingA_Trailer")
|
||||||
|
if TowBarMod.Hook and TowBarMod.Hook.setVehiclePostAttach then
|
||||||
|
pcall(TowBarMod.Hook.setVehiclePostAttach, nil, vehicleB)
|
||||||
|
end
|
||||||
|
log("reconcilePairState A=" .. tostring(vehicleA:getId()) .. " B=" .. tostring(vehicleB:getId())
|
||||||
|
.. " attachmentA=" .. tostring(attachmentA) .. " attachmentB=" .. tostring(attachmentB))
|
||||||
|
end
|
||||||
|
|
||||||
local function applyAttachSync(args)
|
local function applyAttachSync(args)
|
||||||
if not args then return end
|
if not args then return end
|
||||||
|
|
||||||
local vehicleA = resolveVehicle(args.vehicleA)
|
local vehicleA = resolveVehicle(args.vehicleA)
|
||||||
local vehicleB = resolveVehicle(args.vehicleB)
|
local vehicleB = resolveVehicle(args.vehicleB)
|
||||||
if not vehicleA or not vehicleB then return end
|
if not vehicleA or not vehicleB then
|
||||||
|
log("applyAttachSync skipped: missing vehicle A=" .. tostring(args.vehicleA) .. " B=" .. tostring(args.vehicleB))
|
||||||
if vehicleA:getVehicleTowing() == vehicleB and vehicleB:getVehicleTowedBy() == vehicleA then
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -63,7 +100,15 @@ local function applyAttachSync(args)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if vehicleA:getVehicleTowing() == vehicleB and vehicleB:getVehicleTowedBy() == vehicleA then
|
||||||
|
log("applyAttachSync already linked A=" .. tostring(vehicleA:getId()) .. " B=" .. tostring(vehicleB:getId()))
|
||||||
|
else
|
||||||
|
log("applyAttachSync addPointConstraint A=" .. tostring(vehicleA:getId()) .. " B=" .. tostring(vehicleB:getId())
|
||||||
|
.. " attachmentA=" .. tostring(attachmentA) .. " attachmentB=" .. tostring(attachmentB))
|
||||||
vehicleA:addPointConstraint(nil, vehicleB, attachmentA, attachmentB)
|
vehicleA:addPointConstraint(nil, vehicleB, attachmentA, attachmentB)
|
||||||
|
end
|
||||||
|
|
||||||
|
reconcilePairState(vehicleA, vehicleB, attachmentA, attachmentB)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function safeBreak(vehicle)
|
local function safeBreak(vehicle)
|
||||||
|
|||||||
@@ -20,6 +20,10 @@ local function log(msg)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function vehicleId(vehicle)
|
||||||
|
return vehicle and vehicle:getId() or "nil"
|
||||||
|
end
|
||||||
|
|
||||||
local function hasAttachment(vehicle, attachmentId)
|
local function hasAttachment(vehicle, attachmentId)
|
||||||
if vehicle == nil or attachmentId == nil then return false end
|
if vehicle == nil or attachmentId == nil then return false end
|
||||||
local script = vehicle:getScript()
|
local script = vehicle:getScript()
|
||||||
@@ -180,6 +184,18 @@ local function restoreDefaultsIfLeadLost(vehicle)
|
|||||||
clearChangedOffset(vehicle)
|
clearChangedOffset(vehicle)
|
||||||
restoreTowBarDefaults(vehicle)
|
restoreTowBarDefaults(vehicle)
|
||||||
|
|
||||||
|
local md = vehicle:getModData()
|
||||||
|
if md then
|
||||||
|
md["towed"] = false
|
||||||
|
if vehicle:getVehicleTowing() == nil then
|
||||||
|
md["isTowingByTowBar"] = false
|
||||||
|
md.towBarOriginalScriptName = nil
|
||||||
|
md.towBarOriginalMass = nil
|
||||||
|
md.towBarOriginalBrakingForce = nil
|
||||||
|
end
|
||||||
|
vehicle:transmitModData()
|
||||||
|
end
|
||||||
|
|
||||||
setTowBarModelVisible(vehicle, false)
|
setTowBarModelVisible(vehicle, false)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -208,12 +224,104 @@ local function sendAttach(playerObj, towingVehicle, towedVehicle, attachmentA, a
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function captureFrontLink(middleVehicle)
|
local function isLinkedPair(towingVehicle, towedVehicle)
|
||||||
|
if not towingVehicle or not towedVehicle then return false end
|
||||||
|
return towingVehicle:getVehicleTowing() == towedVehicle and towedVehicle:getVehicleTowedBy() == towingVehicle
|
||||||
|
end
|
||||||
|
|
||||||
|
local function markTowBarPair(towingVehicle, towedVehicle)
|
||||||
|
local towingMd = towingVehicle and towingVehicle:getModData() or nil
|
||||||
|
local towedMd = towedVehicle and towedVehicle:getModData() or nil
|
||||||
|
|
||||||
|
if towingMd then
|
||||||
|
towingMd["isTowingByTowBar"] = true
|
||||||
|
towingVehicle:transmitModData()
|
||||||
|
end
|
||||||
|
if towedMd then
|
||||||
|
local currentScript = towedVehicle and towedVehicle:getScriptName() or nil
|
||||||
|
if towedMd.towBarOriginalScriptName == nil and currentScript ~= "notTowingA_Trailer" then
|
||||||
|
towedMd.towBarOriginalScriptName = currentScript
|
||||||
|
end
|
||||||
|
if towedMd.towBarOriginalMass == nil and towedVehicle then
|
||||||
|
towedMd.towBarOriginalMass = towedVehicle:getMass()
|
||||||
|
end
|
||||||
|
if towedMd.towBarOriginalBrakingForce == nil and towedVehicle then
|
||||||
|
towedMd.towBarOriginalBrakingForce = towedVehicle:getBrakingForce()
|
||||||
|
end
|
||||||
|
towedMd["isTowingByTowBar"] = true
|
||||||
|
towedMd["towed"] = true
|
||||||
|
towedVehicle:transmitModData()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function ensurePairAttached(playerObj, towingVehicle, towedVehicle, preferredA, preferredB, alwaysSend)
|
||||||
|
if not playerObj or not towingVehicle or not towedVehicle then
|
||||||
|
log("ensurePairAttached skipped: missing refs A=" .. tostring(vehicleId(towingVehicle)) .. " B=" .. tostring(vehicleId(towedVehicle)))
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
if towingVehicle == towedVehicle or towingVehicle:getId() == towedVehicle:getId() then
|
||||||
|
log("ensurePairAttached skipped: identical vehicles id=" .. tostring(vehicleId(towingVehicle)))
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
if wouldCreateTowLoop(towingVehicle, towedVehicle) then
|
||||||
|
log("ensurePairAttached skipped: loop A=" .. tostring(vehicleId(towingVehicle)) .. " B=" .. tostring(vehicleId(towedVehicle)))
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local attachmentA, attachmentB = resolvePair(towingVehicle, towedVehicle, preferredA, preferredB)
|
||||||
|
if not hasAttachment(towingVehicle, attachmentA) or not hasAttachment(towedVehicle, attachmentB) then
|
||||||
|
log("ensurePairAttached skipped: missing attachment A=" .. tostring(attachmentA) .. " B=" .. tostring(attachmentB)
|
||||||
|
.. " ids=" .. tostring(vehicleId(towingVehicle)) .. "->" .. tostring(vehicleId(towedVehicle)))
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
applyRigidTow(towingVehicle, towedVehicle, attachmentA, attachmentB)
|
||||||
|
towedVehicle:setScriptName("notTowingA_Trailer")
|
||||||
|
markTowBarPair(towingVehicle, towedVehicle)
|
||||||
|
|
||||||
|
local linked = isLinkedPair(towingVehicle, towedVehicle)
|
||||||
|
log("ensurePairAttached pair=" .. tostring(vehicleId(towingVehicle)) .. "->" .. tostring(vehicleId(towedVehicle))
|
||||||
|
.. " linked=" .. tostring(linked) .. " alwaysSend=" .. tostring(alwaysSend)
|
||||||
|
.. " attachmentA=" .. tostring(attachmentA) .. " attachmentB=" .. tostring(attachmentB))
|
||||||
|
|
||||||
|
if linked and TowBarMod.Hook and TowBarMod.Hook.setVehiclePostAttach then
|
||||||
|
pcall(TowBarMod.Hook.setVehiclePostAttach, nil, towedVehicle)
|
||||||
|
end
|
||||||
|
|
||||||
|
if alwaysSend or not linked then
|
||||||
|
sendAttach(playerObj, towingVehicle, towedVehicle, attachmentA, attachmentB)
|
||||||
|
elseif TowBarMod.Hook and TowBarMod.Hook.setVehiclePostAttach then
|
||||||
|
queueAction(playerObj, 10, TowBarMod.Hook.setVehiclePostAttach, towedVehicle)
|
||||||
|
end
|
||||||
|
|
||||||
|
setTowBarModelVisible(towedVehicle, true)
|
||||||
|
refreshAround(towingVehicle)
|
||||||
|
refreshAround(towedVehicle)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
local function captureFrontLinks(middleVehicle)
|
||||||
if not middleVehicle then return nil end
|
if not middleVehicle then return nil end
|
||||||
local frontVehicle = middleVehicle:getVehicleTowedBy()
|
|
||||||
if not frontVehicle or frontVehicle == middleVehicle then return nil end
|
local links = {}
|
||||||
local a, b = resolvePair(frontVehicle, middleVehicle)
|
local cursor = middleVehicle
|
||||||
return { front = frontVehicle, middle = middleVehicle, a = a, b = b }
|
local visited = {}
|
||||||
|
|
||||||
|
while cursor do
|
||||||
|
if visited[cursor] then break end
|
||||||
|
visited[cursor] = true
|
||||||
|
|
||||||
|
local frontVehicle = cursor:getVehicleTowedBy()
|
||||||
|
if not frontVehicle then break end
|
||||||
|
if frontVehicle == cursor or visited[frontVehicle] then break end
|
||||||
|
|
||||||
|
local a, b = resolvePair(frontVehicle, cursor)
|
||||||
|
table.insert(links, { front = frontVehicle, middle = cursor, a = a, b = b })
|
||||||
|
cursor = frontVehicle
|
||||||
|
end
|
||||||
|
|
||||||
|
if #links == 0 then return nil end
|
||||||
|
return links
|
||||||
end
|
end
|
||||||
|
|
||||||
local function restoreFrontLink(playerObj, link)
|
local function restoreFrontLink(playerObj, link)
|
||||||
@@ -225,23 +333,21 @@ local function restoreFrontLink(playerObj, link)
|
|||||||
if front == middle or front:getId() == middle:getId() then return end
|
if front == middle or front:getId() == middle:getId() then return end
|
||||||
if wouldCreateTowLoop(front, middle) then return end
|
if wouldCreateTowLoop(front, middle) then return end
|
||||||
|
|
||||||
local a, b = resolvePair(front, middle, link.a, link.b)
|
ensurePairAttached(playerObj, front, middle, link.a, link.b, false)
|
||||||
if not hasAttachment(front, a) or not hasAttachment(middle, b) then return end
|
|
||||||
|
|
||||||
applyRigidTow(front, middle, a, b)
|
|
||||||
middle:setScriptName("notTowingA_Trailer")
|
|
||||||
sendAttach(playerObj, front, middle, a, b)
|
|
||||||
|
|
||||||
setTowBarModelVisible(middle, true)
|
|
||||||
refreshAround(front)
|
|
||||||
refreshAround(middle)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function queueRestoreFront(playerObj, link, delay)
|
local function restoreFrontLinks(playerObj, links)
|
||||||
if not playerObj or not link then return end
|
if not links then return end
|
||||||
queueAction(playerObj, delay or 12, function(character, linkArg)
|
for _, link in ipairs(links) do
|
||||||
restoreFrontLink(character, linkArg)
|
restoreFrontLink(playerObj, link)
|
||||||
end, link)
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function queueRestoreFrontLinks(playerObj, links, delay)
|
||||||
|
if not playerObj or not links then return end
|
||||||
|
queueAction(playerObj, delay or 12, function(character, linksArg)
|
||||||
|
restoreFrontLinks(character, linksArg)
|
||||||
|
end, links)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function resolveDetachPair(towingVehicle, towedVehicle)
|
local function resolveDetachPair(towingVehicle, towedVehicle)
|
||||||
@@ -411,16 +517,31 @@ LT.performAttachWrapper = function(playerObj, towingVehicle, towedVehicle, attac
|
|||||||
if towingVehicle == towedVehicle or towingVehicle:getId() == towedVehicle:getId() then return end
|
if towingVehicle == towedVehicle or towingVehicle:getId() == towedVehicle:getId() then return end
|
||||||
if wouldCreateTowLoop(towingVehicle, towedVehicle) then return end
|
if wouldCreateTowLoop(towingVehicle, towedVehicle) then return end
|
||||||
|
|
||||||
local frontLink = captureFrontLink(towingVehicle)
|
local frontLinks = captureFrontLinks(towingVehicle)
|
||||||
|
log("performAttachWrapper begin pair=" .. tostring(vehicleId(towingVehicle)) .. "->" .. tostring(vehicleId(towedVehicle))
|
||||||
|
.. " frontLinks=" .. tostring(frontLinks and #frontLinks or 0))
|
||||||
original(playerObj, towingVehicle, towedVehicle, attachmentA, attachmentB)
|
original(playerObj, towingVehicle, towedVehicle, attachmentA, attachmentB)
|
||||||
|
|
||||||
restoreFrontLink(playerObj, frontLink)
|
restoreFrontLinks(playerObj, frontLinks)
|
||||||
queueRestoreFront(playerObj, frontLink, 12)
|
queueRestoreFrontLinks(playerObj, frontLinks, 12)
|
||||||
queueRestoreFront(playerObj, frontLink, 30)
|
queueRestoreFrontLinks(playerObj, frontLinks, 30)
|
||||||
|
|
||||||
setTowBarModelVisible(towedVehicle, true)
|
ensurePairAttached(playerObj, towingVehicle, towedVehicle, attachmentA, attachmentB, false)
|
||||||
refreshAround(towingVehicle)
|
|
||||||
refreshAround(towedVehicle)
|
local attachState = {
|
||||||
|
towing = towingVehicle,
|
||||||
|
towed = towedVehicle,
|
||||||
|
attachmentA = attachmentA,
|
||||||
|
attachmentB = attachmentB
|
||||||
|
}
|
||||||
|
queueAction(playerObj, 14, function(character, state)
|
||||||
|
if not state then return end
|
||||||
|
ensurePairAttached(character, state.towing, state.towed, state.attachmentA, state.attachmentB, true)
|
||||||
|
end, attachState)
|
||||||
|
queueAction(playerObj, 34, function(character, state)
|
||||||
|
if not state then return end
|
||||||
|
ensurePairAttached(character, state.towing, state.towed, state.attachmentA, state.attachmentB, true)
|
||||||
|
end, attachState)
|
||||||
end
|
end
|
||||||
|
|
||||||
LT.performDetachWrapper = function(playerObj, towingVehicle, towedVehicle)
|
LT.performDetachWrapper = function(playerObj, towingVehicle, towedVehicle)
|
||||||
@@ -430,12 +551,12 @@ LT.performDetachWrapper = function(playerObj, towingVehicle, towedVehicle)
|
|||||||
local resolvedTowing, resolvedTowed = resolveDetachPair(towingVehicle, towedVehicle)
|
local resolvedTowing, resolvedTowed = resolveDetachPair(towingVehicle, towedVehicle)
|
||||||
if resolvedTowing == nil or resolvedTowed == nil then return end
|
if resolvedTowing == nil or resolvedTowed == nil then return end
|
||||||
|
|
||||||
local frontLink = captureFrontLink(resolvedTowing)
|
local frontLinks = captureFrontLinks(resolvedTowing)
|
||||||
original(playerObj, resolvedTowing, resolvedTowed)
|
original(playerObj, resolvedTowing, resolvedTowed)
|
||||||
|
|
||||||
restoreFrontLink(playerObj, frontLink)
|
restoreFrontLinks(playerObj, frontLinks)
|
||||||
queueRestoreFront(playerObj, frontLink, 12)
|
queueRestoreFrontLinks(playerObj, frontLinks, 12)
|
||||||
queueRestoreFront(playerObj, frontLink, 30)
|
queueRestoreFrontLinks(playerObj, frontLinks, 30)
|
||||||
|
|
||||||
restoreDefaultsIfLeadLost(resolvedTowing)
|
restoreDefaultsIfLeadLost(resolvedTowing)
|
||||||
restoreDefaultsIfLeadLost(resolvedTowed)
|
restoreDefaultsIfLeadLost(resolvedTowed)
|
||||||
|
|||||||
@@ -70,8 +70,12 @@ local function processAttach(item)
|
|||||||
log("attach sync failed: server link not established A=" .. tostring(vehicleA:getId()) .. " B=" .. tostring(vehicleB:getId()))
|
log("attach sync failed: server link not established A=" .. tostring(vehicleA:getId()) .. " B=" .. tostring(vehicleB:getId()))
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
log("attach sync already linked A=" .. tostring(vehicleA:getId()) .. " B=" .. tostring(vehicleB:getId()))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
log("attach sync broadcast A=" .. tostring(vehicleA:getId()) .. " B=" .. tostring(vehicleB:getId())
|
||||||
|
.. " attachmentA=" .. tostring(attachmentA) .. " attachmentB=" .. tostring(attachmentB))
|
||||||
sendServerCommand("landtrain", "forceAttachSync", {
|
sendServerCommand("landtrain", "forceAttachSync", {
|
||||||
vehicleA = vehicleA:getId(),
|
vehicleA = vehicleA:getId(),
|
||||||
vehicleB = vehicleB:getId(),
|
vehicleB = vehicleB:getId(),
|
||||||
@@ -94,7 +98,8 @@ end
|
|||||||
local function processPending()
|
local function processPending()
|
||||||
if #pending == 0 then return end
|
if #pending == 0 then return end
|
||||||
|
|
||||||
for i = #pending, 1, -1 do
|
local remaining = {}
|
||||||
|
for i = 1, #pending do
|
||||||
local item = pending[i]
|
local item = pending[i]
|
||||||
item.ticks = item.ticks - 1
|
item.ticks = item.ticks - 1
|
||||||
if item.ticks <= 0 then
|
if item.ticks <= 0 then
|
||||||
@@ -103,17 +108,24 @@ local function processPending()
|
|||||||
elseif item.kind == "detach" then
|
elseif item.kind == "detach" then
|
||||||
processDetach(item)
|
processDetach(item)
|
||||||
end
|
end
|
||||||
table.remove(pending, i)
|
else
|
||||||
|
table.insert(remaining, item)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
pending = remaining
|
||||||
end
|
end
|
||||||
|
|
||||||
local function onClientCommand(module, command, player, args)
|
local function onClientCommand(module, command, player, args)
|
||||||
if module == "vehicle" and command == "attachTrailer" then
|
if module == "vehicle" and command == "attachTrailer" then
|
||||||
|
log("queue attach from " .. tostring(player and player:getUsername() or "unknown")
|
||||||
|
.. " A=" .. tostring(args and args.vehicleA) .. " B=" .. tostring(args and args.vehicleB)
|
||||||
|
.. " attachmentA=" .. tostring(args and args.attachmentA) .. " attachmentB=" .. tostring(args and args.attachmentB))
|
||||||
queueSync("attach", player, args)
|
queueSync("attach", player, args)
|
||||||
elseif module == "vehicle" and command == "detachTrailer" then
|
elseif module == "vehicle" and command == "detachTrailer" then
|
||||||
|
log("queue detach(vehicle) from " .. tostring(player and player:getUsername() or "unknown"))
|
||||||
queueSync("detach", player, args)
|
queueSync("detach", player, args)
|
||||||
elseif module == "towbar" and command == "detachTowBar" then
|
elseif module == "towbar" and command == "detachTowBar" then
|
||||||
|
log("queue detach(towbar) from " .. tostring(player and player:getUsername() or "unknown"))
|
||||||
queueSync("detach", player, args)
|
queueSync("detach", player, args)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import java.nio.file.Paths;
|
|||||||
import org.objectweb.asm.ClassReader;
|
import org.objectweb.asm.ClassReader;
|
||||||
import org.objectweb.asm.ClassWriter;
|
import org.objectweb.asm.ClassWriter;
|
||||||
import org.objectweb.asm.Opcodes;
|
import org.objectweb.asm.Opcodes;
|
||||||
|
import org.objectweb.asm.Type;
|
||||||
import org.objectweb.asm.tree.AbstractInsnNode;
|
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||||
import org.objectweb.asm.tree.ClassNode;
|
import org.objectweb.asm.tree.ClassNode;
|
||||||
import org.objectweb.asm.tree.InsnList;
|
import org.objectweb.asm.tree.InsnList;
|
||||||
@@ -11,6 +12,7 @@ import org.objectweb.asm.tree.InsnNode;
|
|||||||
import org.objectweb.asm.tree.LdcInsnNode;
|
import org.objectweb.asm.tree.LdcInsnNode;
|
||||||
import org.objectweb.asm.tree.MethodInsnNode;
|
import org.objectweb.asm.tree.MethodInsnNode;
|
||||||
import org.objectweb.asm.tree.MethodNode;
|
import org.objectweb.asm.tree.MethodNode;
|
||||||
|
import org.objectweb.asm.tree.VarInsnNode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Patches zombie.vehicles.BaseVehicle for Landtrain chain support:
|
* Patches zombie.vehicles.BaseVehicle for Landtrain chain support:
|
||||||
@@ -20,10 +22,21 @@ import org.objectweb.asm.tree.MethodNode;
|
|||||||
public final class BaseVehicleConstraintPatch {
|
public final class BaseVehicleConstraintPatch {
|
||||||
private static final String TARGET_NAME = "addPointConstraint";
|
private static final String TARGET_NAME = "addPointConstraint";
|
||||||
private static final String CONSTRAINT_CHANGED_NAME = "constraintChanged";
|
private static final String CONSTRAINT_CHANGED_NAME = "constraintChanged";
|
||||||
|
private static final String AUTHORIZATION_CHANGED_NAME = "authorizationChanged";
|
||||||
|
private static final String AUTHORIZATION_CLIENT_COLLIDE_NAME = "authorizationClientCollide";
|
||||||
|
private static final String AUTHORIZATION_SERVER_COLLIDE_NAME = "authorizationServerCollide";
|
||||||
|
private static final String AUTHORIZATION_SERVER_ON_SEAT_NAME = "authorizationServerOnSeat";
|
||||||
private static final String IS_ENTER_BLOCKED_NAME = "isEnterBlocked";
|
private static final String IS_ENTER_BLOCKED_NAME = "isEnterBlocked";
|
||||||
private static final String IS_ENTER_BLOCKED2_NAME = "isEnterBlocked2";
|
private static final String IS_ENTER_BLOCKED2_NAME = "isEnterBlocked2";
|
||||||
private static final String CLINIT_NAME = "<clinit>";
|
private static final String CLINIT_NAME = "<clinit>";
|
||||||
private static final String VOID_NOARG_DESC = "()V";
|
private static final String VOID_NOARG_DESC = "()V";
|
||||||
|
private static final String AUTHORIZATION_CHANGED_DESC =
|
||||||
|
"(Lzombie/characters/IsoGameCharacter;)V";
|
||||||
|
private static final String AUTHORIZATION_CLIENT_COLLIDE_DESC =
|
||||||
|
"(Lzombie/characters/IsoPlayer;)V";
|
||||||
|
private static final String AUTHORIZATION_SERVER_COLLIDE_DESC = "(SZ)V";
|
||||||
|
private static final String AUTHORIZATION_SERVER_ON_SEAT_DESC =
|
||||||
|
"(Lzombie/characters/IsoPlayer;Z)V";
|
||||||
private static final String ENTER_BLOCKED_DESC = "(Lzombie/characters/IsoGameCharacter;I)Z";
|
private static final String ENTER_BLOCKED_DESC = "(Lzombie/characters/IsoGameCharacter;I)Z";
|
||||||
private static final String EXIT_BLOCKED_DESC = "(Lzombie/characters/IsoGameCharacter;I)Z";
|
private static final String EXIT_BLOCKED_DESC = "(Lzombie/characters/IsoGameCharacter;I)Z";
|
||||||
private static final String EXIT_BLOCKED2_DESC = "(I)Z";
|
private static final String EXIT_BLOCKED2_DESC = "(I)Z";
|
||||||
@@ -31,6 +44,11 @@ public final class BaseVehicleConstraintPatch {
|
|||||||
private static final String BREAK_DESC_OBJECT_BOOL = "(ZLjava/lang/Boolean;)V";
|
private static final String BREAK_DESC_OBJECT_BOOL = "(ZLjava/lang/Boolean;)V";
|
||||||
private static final String BREAK_DESC_PRIMITIVE_BOOL = "(ZZ)V";
|
private static final String BREAK_DESC_PRIMITIVE_BOOL = "(ZZ)V";
|
||||||
private static final String BASE_VEHICLE_OWNER = "zombie/vehicles/BaseVehicle";
|
private static final String BASE_VEHICLE_OWNER = "zombie/vehicles/BaseVehicle";
|
||||||
|
private static final String BULLET_OWNER = "zombie/core/physics/Bullet";
|
||||||
|
private static final String BULLET_ADD_ROPE = "addRopeConstraint";
|
||||||
|
private static final String BULLET_ADD_POINT = "addPointConstraint";
|
||||||
|
private static final String BULLET_ADD_ROPE_DESC = "(IIFFFFFFF)I";
|
||||||
|
private static final String BULLET_ADD_POINT_DESC = "(IIFFFFFF)I";
|
||||||
private static final String GET_DRIVER_DESC = "()Lzombie/characters/IsoGameCharacter;";
|
private static final String GET_DRIVER_DESC = "()Lzombie/characters/IsoGameCharacter;";
|
||||||
private static final String HELPER_OWNER = "zombie/vehicles/LandtrainConstraintAuthHelper";
|
private static final String HELPER_OWNER = "zombie/vehicles/LandtrainConstraintAuthHelper";
|
||||||
private static final String HELPER_METHOD = "resolveConstraintDriver";
|
private static final String HELPER_METHOD = "resolveConstraintDriver";
|
||||||
@@ -41,6 +59,27 @@ public final class BaseVehicleConstraintPatch {
|
|||||||
"(Lzombie/vehicles/BaseVehicle;Lzombie/characters/IsoGameCharacter;I)Z";
|
"(Lzombie/vehicles/BaseVehicle;Lzombie/characters/IsoGameCharacter;I)Z";
|
||||||
private static final String HELPER_ENTER_BLOCKED2 = "isEnterBlocked2Landtrain";
|
private static final String HELPER_ENTER_BLOCKED2 = "isEnterBlocked2Landtrain";
|
||||||
private static final String HELPER_ENTER_BLOCKED2_DESC = "(Lzombie/vehicles/BaseVehicle;I)Z";
|
private static final String HELPER_ENTER_BLOCKED2_DESC = "(Lzombie/vehicles/BaseVehicle;I)Z";
|
||||||
|
private static final String HELPER_AUTHORIZATION_CHANGED =
|
||||||
|
"authorizationChangedLandtrain";
|
||||||
|
private static final String HELPER_AUTHORIZATION_CHANGED_DESC =
|
||||||
|
"(Lzombie/vehicles/BaseVehicle;Lzombie/characters/IsoGameCharacter;)V";
|
||||||
|
private static final String HELPER_AUTHORIZATION_CLIENT_COLLIDE =
|
||||||
|
"authorizationClientCollideLandtrain";
|
||||||
|
private static final String HELPER_AUTHORIZATION_CLIENT_COLLIDE_DESC =
|
||||||
|
"(Lzombie/vehicles/BaseVehicle;Lzombie/characters/IsoPlayer;)V";
|
||||||
|
private static final String HELPER_AUTHORIZATION_SERVER_COLLIDE =
|
||||||
|
"authorizationServerCollideLandtrain";
|
||||||
|
private static final String HELPER_AUTHORIZATION_SERVER_COLLIDE_DESC =
|
||||||
|
"(Lzombie/vehicles/BaseVehicle;SZ)V";
|
||||||
|
private static final String HELPER_AUTHORIZATION_SERVER_ON_SEAT =
|
||||||
|
"authorizationServerOnSeatLandtrain";
|
||||||
|
private static final String HELPER_AUTHORIZATION_SERVER_ON_SEAT_DESC =
|
||||||
|
"(Lzombie/vehicles/BaseVehicle;Lzombie/characters/IsoPlayer;Z)V";
|
||||||
|
|
||||||
|
private static final class AddPointPatchStats {
|
||||||
|
int removedBreakCalls;
|
||||||
|
int forcedRigidCalls;
|
||||||
|
}
|
||||||
|
|
||||||
private BaseVehicleConstraintPatch() {
|
private BaseVehicleConstraintPatch() {
|
||||||
}
|
}
|
||||||
@@ -59,18 +98,49 @@ public final class BaseVehicleConstraintPatch {
|
|||||||
new ClassReader(original).accept(classNode, 0);
|
new ClassReader(original).accept(classNode, 0);
|
||||||
|
|
||||||
int removedCalls = 0;
|
int removedCalls = 0;
|
||||||
|
int forcedRigidCalls = 0;
|
||||||
int inspectedAddPointMethods = 0;
|
int inspectedAddPointMethods = 0;
|
||||||
int patchedConstraintDriverCalls = 0;
|
int patchedConstraintDriverCalls = 0;
|
||||||
int patchedEnterBlockedCalls = 0;
|
int patchedEnterBlockedCalls = 0;
|
||||||
int patchedEnterBlocked2Calls = 0;
|
int patchedEnterBlocked2Calls = 0;
|
||||||
|
int patchedAuthorizationChangedMethods = 0;
|
||||||
|
int patchedAuthorizationClientCollideMethods = 0;
|
||||||
|
int patchedAuthorizationServerCollideMethods = 0;
|
||||||
|
int patchedAuthorizationServerOnSeatMethods = 0;
|
||||||
|
|
||||||
for (MethodNode method : classNode.methods) {
|
for (MethodNode method : classNode.methods) {
|
||||||
if (TARGET_NAME.equals(method.name) && isTargetAddPointConstraint(method.desc)) {
|
if (TARGET_NAME.equals(method.name) && isTargetAddPointConstraint(method.desc)) {
|
||||||
inspectedAddPointMethods++;
|
inspectedAddPointMethods++;
|
||||||
removedCalls += patchAddPointConstraint(method);
|
AddPointPatchStats stats = patchAddPointConstraint(method);
|
||||||
|
removedCalls += stats.removedBreakCalls;
|
||||||
|
forcedRigidCalls += stats.forcedRigidCalls;
|
||||||
} else if (CONSTRAINT_CHANGED_NAME.equals(method.name)
|
} else if (CONSTRAINT_CHANGED_NAME.equals(method.name)
|
||||||
&& VOID_NOARG_DESC.equals(method.desc)) {
|
&& VOID_NOARG_DESC.equals(method.desc)) {
|
||||||
patchedConstraintDriverCalls += patchConstraintChangedDriverCalls(method);
|
patchedConstraintDriverCalls += patchConstraintChangedDriverCalls(method);
|
||||||
|
} else if (AUTHORIZATION_CHANGED_NAME.equals(method.name)
|
||||||
|
&& AUTHORIZATION_CHANGED_DESC.equals(method.desc)) {
|
||||||
|
patchedAuthorizationChangedMethods += patchMethodDelegateToHelper(
|
||||||
|
method,
|
||||||
|
HELPER_AUTHORIZATION_CHANGED,
|
||||||
|
HELPER_AUTHORIZATION_CHANGED_DESC);
|
||||||
|
} else if (AUTHORIZATION_CLIENT_COLLIDE_NAME.equals(method.name)
|
||||||
|
&& AUTHORIZATION_CLIENT_COLLIDE_DESC.equals(method.desc)) {
|
||||||
|
patchedAuthorizationClientCollideMethods += patchMethodDelegateToHelper(
|
||||||
|
method,
|
||||||
|
HELPER_AUTHORIZATION_CLIENT_COLLIDE,
|
||||||
|
HELPER_AUTHORIZATION_CLIENT_COLLIDE_DESC);
|
||||||
|
} else if (AUTHORIZATION_SERVER_COLLIDE_NAME.equals(method.name)
|
||||||
|
&& AUTHORIZATION_SERVER_COLLIDE_DESC.equals(method.desc)) {
|
||||||
|
patchedAuthorizationServerCollideMethods += patchMethodDelegateToHelper(
|
||||||
|
method,
|
||||||
|
HELPER_AUTHORIZATION_SERVER_COLLIDE,
|
||||||
|
HELPER_AUTHORIZATION_SERVER_COLLIDE_DESC);
|
||||||
|
} else if (AUTHORIZATION_SERVER_ON_SEAT_NAME.equals(method.name)
|
||||||
|
&& AUTHORIZATION_SERVER_ON_SEAT_DESC.equals(method.desc)) {
|
||||||
|
patchedAuthorizationServerOnSeatMethods += patchMethodDelegateToHelper(
|
||||||
|
method,
|
||||||
|
HELPER_AUTHORIZATION_SERVER_ON_SEAT,
|
||||||
|
HELPER_AUTHORIZATION_SERVER_ON_SEAT_DESC);
|
||||||
} else if (IS_ENTER_BLOCKED_NAME.equals(method.name)
|
} else if (IS_ENTER_BLOCKED_NAME.equals(method.name)
|
||||||
&& ENTER_BLOCKED_DESC.equals(method.desc)) {
|
&& ENTER_BLOCKED_DESC.equals(method.desc)) {
|
||||||
patchedEnterBlockedCalls += patchEnterBlockedCall(
|
patchedEnterBlockedCalls += patchEnterBlockedCall(
|
||||||
@@ -98,11 +168,36 @@ public final class BaseVehicleConstraintPatch {
|
|||||||
+ inspectedAddPointMethods
|
+ inspectedAddPointMethods
|
||||||
+ ")");
|
+ ")");
|
||||||
}
|
}
|
||||||
|
if (forcedRigidCalls < 1) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Expected to force at least 1 rope->point constraint call, patched "
|
||||||
|
+ forcedRigidCalls);
|
||||||
|
}
|
||||||
if (patchedConstraintDriverCalls < 1) {
|
if (patchedConstraintDriverCalls < 1) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"Expected to patch at least 1 constraintChanged getDriver call, patched "
|
"Expected to patch at least 1 constraintChanged getDriver call, patched "
|
||||||
+ patchedConstraintDriverCalls);
|
+ patchedConstraintDriverCalls);
|
||||||
}
|
}
|
||||||
|
if (patchedAuthorizationChangedMethods < 1) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Expected to patch authorizationChanged, patched "
|
||||||
|
+ patchedAuthorizationChangedMethods);
|
||||||
|
}
|
||||||
|
if (patchedAuthorizationClientCollideMethods < 1) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Expected to patch authorizationClientCollide, patched "
|
||||||
|
+ patchedAuthorizationClientCollideMethods);
|
||||||
|
}
|
||||||
|
if (patchedAuthorizationServerCollideMethods < 1) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Expected to patch authorizationServerCollide, patched "
|
||||||
|
+ patchedAuthorizationServerCollideMethods);
|
||||||
|
}
|
||||||
|
if (patchedAuthorizationServerOnSeatMethods < 1) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Expected to patch authorizationServerOnSeat, patched "
|
||||||
|
+ patchedAuthorizationServerOnSeatMethods);
|
||||||
|
}
|
||||||
if (patchedEnterBlockedCalls < 1) {
|
if (patchedEnterBlockedCalls < 1) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"Expected to patch isEnterBlocked call, patched " + patchedEnterBlockedCalls);
|
"Expected to patch isEnterBlocked call, patched " + patchedEnterBlockedCalls);
|
||||||
@@ -123,8 +218,18 @@ public final class BaseVehicleConstraintPatch {
|
|||||||
System.out.println(
|
System.out.println(
|
||||||
"Patched BaseVehicle.class; removed breakConstraint calls: "
|
"Patched BaseVehicle.class; removed breakConstraint calls: "
|
||||||
+ removedCalls
|
+ removedCalls
|
||||||
|
+ ", forced rigid constraints: "
|
||||||
|
+ forcedRigidCalls
|
||||||
+ ", constraint driver hooks: "
|
+ ", constraint driver hooks: "
|
||||||
+ patchedConstraintDriverCalls
|
+ patchedConstraintDriverCalls
|
||||||
|
+ ", auth hooks (changed/client/server/seat): "
|
||||||
|
+ patchedAuthorizationChangedMethods
|
||||||
|
+ "/"
|
||||||
|
+ patchedAuthorizationClientCollideMethods
|
||||||
|
+ "/"
|
||||||
|
+ patchedAuthorizationServerCollideMethods
|
||||||
|
+ "/"
|
||||||
|
+ patchedAuthorizationServerOnSeatMethods
|
||||||
+ ", enter-block hooks: "
|
+ ", enter-block hooks: "
|
||||||
+ patchedEnterBlockedCalls
|
+ patchedEnterBlockedCalls
|
||||||
+ "/"
|
+ "/"
|
||||||
@@ -139,8 +244,8 @@ public final class BaseVehicleConstraintPatch {
|
|||||||
.equals(methodDesc);
|
.equals(methodDesc);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int patchAddPointConstraint(MethodNode method) {
|
private static AddPointPatchStats patchAddPointConstraint(MethodNode method) {
|
||||||
int patched = 0;
|
AddPointPatchStats stats = new AddPointPatchStats();
|
||||||
InsnList insns = method.instructions;
|
InsnList insns = method.instructions;
|
||||||
|
|
||||||
for (AbstractInsnNode node = insns.getFirst(); node != null; ) {
|
for (AbstractInsnNode node = insns.getFirst(); node != null; ) {
|
||||||
@@ -160,12 +265,27 @@ public final class BaseVehicleConstraintPatch {
|
|||||||
replacement.add(new InsnNode(Opcodes.POP));
|
replacement.add(new InsnNode(Opcodes.POP));
|
||||||
insns.insert(node, replacement);
|
insns.insert(node, replacement);
|
||||||
insns.remove(node);
|
insns.remove(node);
|
||||||
patched++;
|
stats.removedBreakCalls++;
|
||||||
|
} else if (node instanceof MethodInsnNode call
|
||||||
|
&& BULLET_OWNER.equals(call.owner)
|
||||||
|
&& BULLET_ADD_ROPE.equals(call.name)
|
||||||
|
&& BULLET_ADD_ROPE_DESC.equals(call.desc)) {
|
||||||
|
// Drop the rope-length float and call Bullet.addPointConstraint(...) instead.
|
||||||
|
insns.insertBefore(call, new InsnNode(Opcodes.POP));
|
||||||
|
MethodInsnNode replacement =
|
||||||
|
new MethodInsnNode(
|
||||||
|
Opcodes.INVOKESTATIC,
|
||||||
|
BULLET_OWNER,
|
||||||
|
BULLET_ADD_POINT,
|
||||||
|
BULLET_ADD_POINT_DESC,
|
||||||
|
false);
|
||||||
|
insns.set(call, replacement);
|
||||||
|
stats.forcedRigidCalls++;
|
||||||
}
|
}
|
||||||
node = next;
|
node = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
return patched;
|
return stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int patchConstraintChangedDriverCalls(MethodNode method) {
|
private static int patchConstraintChangedDriverCalls(MethodNode method) {
|
||||||
@@ -198,6 +318,45 @@ public final class BaseVehicleConstraintPatch {
|
|||||||
return patched;
|
return patched;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int patchMethodDelegateToHelper(
|
||||||
|
MethodNode method, String helperMethod, String helperDesc) {
|
||||||
|
InsnList insns = new InsnList();
|
||||||
|
|
||||||
|
int localIndex = 0;
|
||||||
|
int maxStack = 0;
|
||||||
|
if ((method.access & Opcodes.ACC_STATIC) == 0) {
|
||||||
|
insns.add(new VarInsnNode(Opcodes.ALOAD, 0));
|
||||||
|
localIndex = 1;
|
||||||
|
maxStack = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Type[] argumentTypes = Type.getArgumentTypes(method.desc);
|
||||||
|
for (Type argumentType : argumentTypes) {
|
||||||
|
insns.add(new VarInsnNode(argumentType.getOpcode(Opcodes.ILOAD), localIndex));
|
||||||
|
localIndex += argumentType.getSize();
|
||||||
|
maxStack += argumentType.getSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
insns.add(new MethodInsnNode(Opcodes.INVOKESTATIC, HELPER_OWNER, helperMethod, helperDesc, false));
|
||||||
|
|
||||||
|
Type returnType = Type.getReturnType(method.desc);
|
||||||
|
if (returnType.getSort() == Type.VOID) {
|
||||||
|
insns.add(new InsnNode(Opcodes.RETURN));
|
||||||
|
} else {
|
||||||
|
insns.add(new InsnNode(returnType.getOpcode(Opcodes.IRETURN)));
|
||||||
|
}
|
||||||
|
|
||||||
|
method.instructions.clear();
|
||||||
|
method.instructions.add(insns);
|
||||||
|
method.tryCatchBlocks.clear();
|
||||||
|
if (method.localVariables != null) {
|
||||||
|
method.localVariables.clear();
|
||||||
|
}
|
||||||
|
method.maxLocals = Math.max(method.maxLocals, localIndex);
|
||||||
|
method.maxStack = Math.max(method.maxStack, maxStack);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
private static int patchEnterBlockedCall(
|
private static int patchEnterBlockedCall(
|
||||||
MethodNode method,
|
MethodNode method,
|
||||||
String targetCallName,
|
String targetCallName,
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
package zombie.vehicles;
|
package zombie.vehicles;
|
||||||
|
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import zombie.characters.IsoGameCharacter;
|
import zombie.characters.IsoGameCharacter;
|
||||||
|
import zombie.characters.IsoPlayer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves the effective driver for constraint auth in chained towing.
|
* Resolves the effective driver for constraint auth in chained towing.
|
||||||
@@ -62,4 +66,133 @@ public final class LandtrainConstraintAuthHelper {
|
|||||||
private static boolean isVehicleBeingTowedInLandtrain(BaseVehicle vehicle) {
|
private static boolean isVehicleBeingTowedInLandtrain(BaseVehicle vehicle) {
|
||||||
return vehicle != null && vehicle.getVehicleTowedBy() != null;
|
return vehicle != null && vehicle.getVehicleTowedBy() != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void authorizationChangedLandtrain(BaseVehicle vehicle, IsoGameCharacter character) {
|
||||||
|
if (vehicle == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (character != null) {
|
||||||
|
applyAuthorizationAcrossChain(
|
||||||
|
vehicle, BaseVehicle.Authorization.Local, character.getOnlineID(), false);
|
||||||
|
} else {
|
||||||
|
applyAuthorizationAcrossChain(vehicle, BaseVehicle.Authorization.Server, -1, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void authorizationClientCollideLandtrain(BaseVehicle vehicle, IsoPlayer driver) {
|
||||||
|
if (vehicle == null || driver == null || vehicle.getDriver() != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
applyAuthorizationAcrossChain(
|
||||||
|
vehicle, BaseVehicle.Authorization.LocalCollide, driver.getOnlineID(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void authorizationServerCollideLandtrain(
|
||||||
|
BaseVehicle vehicle, short playerID, boolean isCollide) {
|
||||||
|
if (vehicle == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (vehicle.isNetPlayerAuthorization(BaseVehicle.Authorization.Local)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isCollide) {
|
||||||
|
applyAuthorizationAcrossChain(
|
||||||
|
vehicle, BaseVehicle.Authorization.LocalCollide, playerID, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseVehicle.Authorization auth =
|
||||||
|
playerID == -1 ? BaseVehicle.Authorization.Server : BaseVehicle.Authorization.Local;
|
||||||
|
applyAuthorizationAcrossChain(vehicle, auth, playerID, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void authorizationServerOnSeatLandtrain(
|
||||||
|
BaseVehicle vehicle, IsoPlayer player, boolean enter) {
|
||||||
|
if (vehicle == null || player == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseVehicle vehicleA = vehicle.getVehicleTowing();
|
||||||
|
BaseVehicle vehicleB = vehicle.getVehicleTowedBy();
|
||||||
|
if (vehicle.isNetPlayerId((short) -1) && enter) {
|
||||||
|
if (vehicleA != null && vehicleA.getDriver() == null) {
|
||||||
|
vehicle.addPointConstraint(
|
||||||
|
null, vehicleA, vehicle.getTowAttachmentSelf(), vehicleA.getTowAttachmentSelf());
|
||||||
|
} else if (vehicleB != null && vehicleB.getDriver() == null) {
|
||||||
|
vehicle.addPointConstraint(
|
||||||
|
null, vehicleB, vehicle.getTowAttachmentSelf(), vehicleB.getTowAttachmentSelf());
|
||||||
|
} else {
|
||||||
|
applyAuthorizationAcrossChain(
|
||||||
|
vehicle, BaseVehicle.Authorization.Local, player.getOnlineID(), false);
|
||||||
|
}
|
||||||
|
} else if (vehicle.isNetPlayerId(player.getOnlineID()) && !enter) {
|
||||||
|
if (vehicleA != null && vehicleA.getDriver() != null) {
|
||||||
|
vehicleA.addPointConstraint(
|
||||||
|
null, vehicle, vehicleA.getTowAttachmentSelf(), vehicle.getTowAttachmentSelf());
|
||||||
|
} else if (vehicleB != null && vehicleB.getDriver() != null) {
|
||||||
|
vehicleB.addPointConstraint(
|
||||||
|
null, vehicle, vehicleB.getTowAttachmentSelf(), vehicle.getTowAttachmentSelf());
|
||||||
|
} else {
|
||||||
|
applyAuthorizationAcrossChain(vehicle, BaseVehicle.Authorization.Server, -1, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<BaseVehicle> collectConnectedChain(BaseVehicle start) {
|
||||||
|
ArrayList<BaseVehicle> chain = new ArrayList<>();
|
||||||
|
if (start == null) {
|
||||||
|
return chain;
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayDeque<BaseVehicle> pending = new ArrayDeque<>();
|
||||||
|
Set<Integer> visitedIds = new HashSet<>();
|
||||||
|
pending.add(start);
|
||||||
|
|
||||||
|
while (!pending.isEmpty()) {
|
||||||
|
BaseVehicle cursor = pending.removeFirst();
|
||||||
|
if (cursor == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int id = cursor.getId();
|
||||||
|
if (!visitedIds.add(id)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
chain.add(cursor);
|
||||||
|
BaseVehicle front = cursor.getVehicleTowedBy();
|
||||||
|
BaseVehicle rear = cursor.getVehicleTowing();
|
||||||
|
if (front != null && front != cursor) {
|
||||||
|
pending.add(front);
|
||||||
|
}
|
||||||
|
if (rear != null && rear != cursor) {
|
||||||
|
pending.add(rear);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return chain;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void applyAuthorizationAcrossChain(
|
||||||
|
BaseVehicle start,
|
||||||
|
BaseVehicle.Authorization authorization,
|
||||||
|
int playerId,
|
||||||
|
boolean refreshSimulation) {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
for (BaseVehicle vehicle : collectConnectedChain(start)) {
|
||||||
|
if (vehicle == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
vehicle.setNetPlayerAuthorization(authorization, playerId);
|
||||||
|
if (refreshSimulation) {
|
||||||
|
vehicle.authSimulationTime = now;
|
||||||
|
if (vehicle.interpolation != null) {
|
||||||
|
vehicle.interpolation.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user