From 9420d76a495c66d1c61c094cf9ea5ee21e83126e Mon Sep 17 00:00:00 2001 From: Peter Vaiko Date: Fri, 7 Nov 2025 17:45:12 -0500 Subject: [PATCH 1/4] test: trace AI events --- scripts/ai/tasks/CpAITaskFieldWork.lua | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/scripts/ai/tasks/CpAITaskFieldWork.lua b/scripts/ai/tasks/CpAITaskFieldWork.lua index ed23af430..83c80c5c1 100644 --- a/scripts/ai/tasks/CpAITaskFieldWork.lua +++ b/scripts/ai/tasks/CpAITaskFieldWork.lua @@ -61,6 +61,19 @@ end --- Makes sure the cp fieldworker gets started. function CpAITaskFieldWork:start() + + self.vehicle.raiseAIEvent = function(vehicle, event1, event2, ...) + CpUtil.infoVehicle(vehicle, "raiseAIEvent %s %s", event1, event2) + AIVehicle.raiseAIEvent(vehicle, event1, event2, ...) + end + + if self.vehicle.actionController ~= nil then + self.vehicle.actionController.onAIEvent = function(actionController, sourceVehicle, eventName) + CpUtil.infoVehicle(self.vehicle, " onAIEvent %s, source %s", eventName, CpUtil.getName(sourceVehicle)) + VehicleActionController.onAIEvent(actionController, sourceVehicle, eventName) + end + end + self:debug("Field work task started.") local spec = self.vehicle.spec_aiFieldWorker spec.isActive = true From 2173159c9f4aad5508a3db90a09db2559d452e27 Mon Sep 17 00:00:00 2001 From: Peter Vaiko Date: Sat, 8 Nov 2025 08:15:05 -0500 Subject: [PATCH 2/4] fix: MP starts (still has the trace) --- .../AIDriveStrategyFieldWorkCourse.lua | 18 +++++++----------- scripts/ai/tasks/CpAITaskFieldWork.lua | 10 ++++++++-- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/scripts/ai/strategies/AIDriveStrategyFieldWorkCourse.lua b/scripts/ai/strategies/AIDriveStrategyFieldWorkCourse.lua index 19170f635..de4694f43 100644 --- a/scripts/ai/strategies/AIDriveStrategyFieldWorkCourse.lua +++ b/scripts/ai/strategies/AIDriveStrategyFieldWorkCourse.lua @@ -24,6 +24,7 @@ AIDriveStrategyFieldWorkCourse = CpObject(AIDriveStrategyCourse) AIDriveStrategyFieldWorkCourse.myStates = { WORKING = {}, + PREPARING = {}, WAITING_FOR_LOWER = {}, WAITING_FOR_LOWER_DELAYED = {}, WAITING_FOR_STOP = {}, @@ -89,19 +90,11 @@ function AIDriveStrategyFieldWorkCourse:start(course, startIx, jobParameters) self:debug('Close enough to start waypoint %d, no alignment course needed', startIx) self:startCourse(course, startIx) self.state = self.states.INITIAL - self:prepareForFieldWork() end --- Store a reference to the original generated course self.originalGeneratedFieldWorkCourse = self.vehicle:getFieldWorkCourse() end ---- If the strategy needs a field polygon to work, it won't transition out of the INITIAL state ---- until the field detection, an asynchronous process that may have started only when the job was started, is finished. ----@return boolean true if the strategy needs the field polygon to work -function AIDriveStrategyFieldWorkCourse:needsFieldPolygon() - return false -end - --- Make sure all implements are in the working state function AIDriveStrategyFieldWorkCourse:prepareForFieldWork() self.vehicle:raiseAIEvent('onAIFieldWorkerPrepareForWork', 'onAIImplementPrepareForWork') @@ -164,6 +157,10 @@ function AIDriveStrategyFieldWorkCourse:getDriveData(dt, vX, vY, vZ) end ---------------------------------------------------------------- if self.state == self.states.INITIAL then + self:setMaxSpeed(0) + self:prepareForFieldWork() + self.state = self.states.PREPARING + elseif self.state == self.states.PREPARING then self:setMaxSpeed(0) self:startWaitingForLower() self:lowerImplements() @@ -328,7 +325,7 @@ function AIDriveStrategyFieldWorkCourse:onWaypointChange(ix, course) self:calculateTightTurnOffset() if not self.state ~= self.states.TURNING and self.course:isTurnStartAtIx(ix) then - if self.state == self.states.INITIAL then + if self.state == self.states.INITIAL or self.state == self.states.PREPARING then self:debug('Waypoint change (%d) to turn start right after starting work, lowering implements.', ix) self:startWaitingForLower() self:lowerImplements() @@ -483,8 +480,8 @@ function AIDriveStrategyFieldWorkCourse:startAlignmentTurn(fieldWorkCourse, star alignmentCourse = self:createAlignmentCourse(fieldWorkCourse, startIx) end self.ppc:setShortLookaheadDistance() - self:prepareForFieldWork() if alignmentCourse then + self:prepareForFieldWork() local fm, bm = self:getFrontAndBackMarkers() self.turnContext = RowStartOrFinishContext(self.vehicle, fieldWorkCourse, startIx, startIx, self.turnNodes, self:getWorkWidth(), fm, bm, self:getTurnEndSideOffset(false), self:getTurnEndForwardOffset()) @@ -495,7 +492,6 @@ function AIDriveStrategyFieldWorkCourse:startAlignmentTurn(fieldWorkCourse, star self:debug('Could not create alignment course to first up/down row waypoint, continue without it') self:startCourse(fieldWorkCourse, startIx) self.state = self.states.INITIAL - self:prepareForFieldWork() end end diff --git a/scripts/ai/tasks/CpAITaskFieldWork.lua b/scripts/ai/tasks/CpAITaskFieldWork.lua index 83c80c5c1..f05b5673f 100644 --- a/scripts/ai/tasks/CpAITaskFieldWork.lua +++ b/scripts/ai/tasks/CpAITaskFieldWork.lua @@ -63,13 +63,19 @@ end function CpAITaskFieldWork:start() self.vehicle.raiseAIEvent = function(vehicle, event1, event2, ...) - CpUtil.infoVehicle(vehicle, "raiseAIEvent %s %s", event1, event2) + if vehicle.cpLastRaiseAIEvent ~= event1 and vehicle.cpLastRaiseAIEvent2 ~= event2 then + CpUtil.infoVehicle(vehicle, "raiseAIEvent %s %s", event1, event2) + end + vehicle.cpLastRaiseAIEvent1 = event1 + vehicle.cpLastRaiseAIEvent2 = event2 AIVehicle.raiseAIEvent(vehicle, event1, event2, ...) end if self.vehicle.actionController ~= nil then self.vehicle.actionController.onAIEvent = function(actionController, sourceVehicle, eventName) - CpUtil.infoVehicle(self.vehicle, " onAIEvent %s, source %s", eventName, CpUtil.getName(sourceVehicle)) + if eventName ~= 'onAIFieldWorkerActive' and eventName ~= 'onAIImplementActive' then + CpUtil.infoVehicle(self.vehicle, " onAIEvent %s, source %s", eventName, CpUtil.getName(sourceVehicle)) + end VehicleActionController.onAIEvent(actionController, sourceVehicle, eventName) end end From ff8cad96bd34b4a2df91792156a764a0fcdeba25 Mon Sep 17 00:00:00 2001 From: Peter Vaiko Date: Sat, 8 Nov 2025 08:49:55 -0500 Subject: [PATCH 3/4] doc: PREPARING phase explanation comments --- scripts/ai/strategies/AIDriveStrategyFieldWorkCourse.lua | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scripts/ai/strategies/AIDriveStrategyFieldWorkCourse.lua b/scripts/ai/strategies/AIDriveStrategyFieldWorkCourse.lua index de4694f43..28dfbf6be 100644 --- a/scripts/ai/strategies/AIDriveStrategyFieldWorkCourse.lua +++ b/scripts/ai/strategies/AIDriveStrategyFieldWorkCourse.lua @@ -157,6 +157,13 @@ function AIDriveStrategyFieldWorkCourse:getDriveData(dt, vX, vY, vZ) end ---------------------------------------------------------------- if self.state == self.states.INITIAL then + -- We need a separate phase for preparing to make sure that the + -- * onAIFieldWorkerStart, + -- * onAIFieldWorkerPrepareForWork, + -- * onAIImplementStartLine + -- events are generated one by one, one in each update loop so that at the end of the loop, + -- AIFieldWorker:updateAIFieldWorker() triggers a onAIFieldWorkerActive event. + -- This makes sure that all implements are lowered and started correctly in multiplayer as well. self:setMaxSpeed(0) self:prepareForFieldWork() self.state = self.states.PREPARING From 458d912c3feb7658652fa2c6dc80296138e6c447 Mon Sep 17 00:00:00 2001 From: Peter Vaiko Date: Tue, 11 Nov 2025 06:04:44 -0500 Subject: [PATCH 4/4] test: deactivate AI event trace --- scripts/ai/tasks/CpAITaskFieldWork.lua | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/scripts/ai/tasks/CpAITaskFieldWork.lua b/scripts/ai/tasks/CpAITaskFieldWork.lua index f05b5673f..331a5fde0 100644 --- a/scripts/ai/tasks/CpAITaskFieldWork.lua +++ b/scripts/ai/tasks/CpAITaskFieldWork.lua @@ -59,9 +59,18 @@ function CpAITaskFieldWork:update(dt) end ---- Makes sure the cp fieldworker gets started. -function CpAITaskFieldWork:start() - +--- This function can be used to trace the triggering of AI events. The timing of these is critical for multiplayer +--- to work properly, these traces help to determine if this timing is correct. Bad timing will result in implements +--- not lowering or not turning on in multiplayer, while there are no symptoms in single player. +--- +--- The game engine needs a separate phase for preparing to make sure that the +--- * onAIFieldWorkerStart, +--- * onAIFieldWorkerPrepareForWork, +--- * onAIImplementStartLine +--- events are generated one by one, one in each update loop so that at the end of the loop, +--- AIFieldWorker:updateAIFieldWorker() triggers a onAIFieldWorkerActive event. +--- +function CpAITaskFieldWork:turnOnAIEventTrace() self.vehicle.raiseAIEvent = function(vehicle, event1, event2, ...) if vehicle.cpLastRaiseAIEvent ~= event1 and vehicle.cpLastRaiseAIEvent2 ~= event2 then CpUtil.infoVehicle(vehicle, "raiseAIEvent %s %s", event1, event2) @@ -79,7 +88,10 @@ function CpAITaskFieldWork:start() VehicleActionController.onAIEvent(actionController, sourceVehicle, eventName) end end - +end + +--- Makes sure the cp fieldworker gets started. +function CpAITaskFieldWork:start() self:debug("Field work task started.") local spec = self.vehicle.spec_aiFieldWorker spec.isActive = true