[rc2] Korolov raid attacking dead station.
I just had a Korolov mission to take out the Charon fortress in the system and the Korolov gunships are not registering the station as dead and going home. I think they didn't get the attack order until the station was dead. They wait a bit at a distance from the target, presumably for slow players, but then don't check if the station's still active before dedicating themselves to blasting the wreck. This came up using a plain vanilla un-augmented PK-25 on a Freyr.
Literally is the new Figuratively
-
- Developer
- Posts: 2997
- Joined: Thu Jul 24, 2003 9:53 pm
- Contact:
This is caused by a subtle bug that is too risky to fix for 1.2, but which definitely should be fixed right afterwards.
The way the gunships are told that the station is destroyed is by the normal method of registering for events (objRegisterForEvents) and a call to <OnObjDestroyed>.
The bug happens because BOTH the Korolov station and the lead gunship register for events on the Charon Stronghold. The way this is implemented, the Charon Stronghold keeps a list of all the objects that have subscribed to its events. So once the mission is set, the Stronghold has a list with two elements:
Subscribers:
[0] = Korolov Station
[1] = Lead Gunship
When the Stronghold is destroyed, it loops over its list and fires events:
So far so good. But what happens if, inside <OnObjDestroyed> the code decides to UNSUBSCRIBE from the event? This is exactly what the Korolov Station code does.
The unsubscribe code naturally removes the object from the list. This means that the list now looks like this:
Subscribers:
[0] = Lead Gunship
In the middle of iterating over the list we've deleted one of the elements! After the first iteration, GetCount() return 1 (instead of 2) and since we've already iterated once, the loop exits--it thinks it's done. The result is that we never call <OnObjDestroyed> for the lead gunship and so it never realizes that the station was destroyed.
To fix this I have to make a COPY of the subscription list before firing events. Then the loop can iterate over the copy and not worry about having it change. I'll have to do this for all loops that deal with registered events (OnObjDestroyed, OnObjDocked, etc.) Unfortunately, this is risky enough that I'd rather wait until after 1.2.
I believe this bug has been around since the 1.2 Alpha, so it should be relatively rare.
The way the gunships are told that the station is destroyed is by the normal method of registering for events (objRegisterForEvents) and a call to <OnObjDestroyed>.
The bug happens because BOTH the Korolov station and the lead gunship register for events on the Charon Stronghold. The way this is implemented, the Charon Stronghold keeps a list of all the objects that have subscribed to its events. So once the mission is set, the Stronghold has a list with two elements:
Subscribers:
[0] = Korolov Station
[1] = Lead Gunship
When the Stronghold is destroyed, it loops over its list and fires events:
Code: Select all
// pseudocode
for (i = 0; i < Subscribers.GetCount(); i++)
Subscribers[i].FireOnObjDestroyed();
The unsubscribe code naturally removes the object from the list. This means that the list now looks like this:
Subscribers:
[0] = Lead Gunship
In the middle of iterating over the list we've deleted one of the elements! After the first iteration, GetCount() return 1 (instead of 2) and since we've already iterated once, the loop exits--it thinks it's done. The result is that we never call <OnObjDestroyed> for the lead gunship and so it never realizes that the station was destroyed.
To fix this I have to make a COPY of the subscription list before firing events. Then the loop can iterate over the copy and not worry about having it change. I'll have to do this for all loops that deal with registered events (OnObjDestroyed, OnObjDocked, etc.) Unfortunately, this is risky enough that I'd rather wait until after 1.2.
I believe this bug has been around since the 1.2 Alpha, so it should be relatively rare.
I don't think this is that problem. It's not relatively rare, it's repeatable. If you kill the station while the gunships are waiting before their attack they will attack the dead station.
If you get out in front of them and have a good in-depth weapon this isn't difficult.
If you get out in front of them and have a good in-depth weapon this isn't difficult.
Literally is the new Figuratively
-
- Developer
- Posts: 2997
- Joined: Thu Jul 24, 2003 9:53 pm
- Contact:
I agree that it is repeatable. I could reproduce it easily and I saw exactly where the call failed (in the debugger). I probably should have said "relatively harmless" instead.Atarlost wrote:I don't think this is that problem. It's not relatively rare, it's repeatable. If you kill the station while the gunships are waiting before their attack they will attack the dead station.
If you get out in front of them and have a good in-depth weapon this isn't difficult.
In any case, I agree it has to be fixed--I'm just too nervous to fix it in 1.2 because we won't have enough time to test the fix.
- Aury
- Fleet Admiral
- Posts: 5421
- Joined: Tue Feb 05, 2008 1:10 am
- Location: Somewhere in the Frontier on a Hycrotan station, working on new ships.
Is this possible:
Just replace the unsubscribe with a timer event for 1 tick (the next tick), which then triggers a dummy event containing only the unsubscribe?
Just replace the unsubscribe with a timer event for 1 tick (the next tick), which then triggers a dummy event containing only the unsubscribe?
(shpOrder gPlayership 'barrelRoll)
(plySetGenome gPlayer (list 'Varalyn 'nonBinary))
Homelab Servers: Xeon Silver 4110, 16GB | Via Quadcore C4650, 16GB | Athlon 200GE, 8GB | i7 7800X, 32GB | Threadripper 1950X, 32GB | Atom x5 8350, 4GB | Opteron 8174, 16GB | Xeon E5 2620 v3, 8GB | 2x Xeon Silver 4116, 96GB, 2x 1080ti | i7 8700, 32GB, 6500XT
Workstations & Render machines: Threadripper 3990X, 128GB, 6900XT | Threadripper 2990WX, 32GB, 1080ti | Xeon Platinum 8173M, 48GB, 1070ti | R9 3900X, 16GB, Vega64 | 2x E5 2430L v2, 24GB, 970 | R7 3700X, 32GB, A6000
Gaming Systems: R9 5950X, 32GB, 6700XT
Office Systems: Xeon 5318Y, 256GB, A4000
Misc Systems: R5 3500U, 20GB | R5 2400G, 16GB | i5 7640X, 16GB, Vega56 | E5 2620, 8GB, R5 260 | P4 1.8ghz, 0.75GB, Voodoo 5 5500 | Athlon 64 x2 4400+, 1.5GB, FX 5800 Ultra | Pentium D 3.2ghz, 4GB, 7600gt | Celeron g460, 8GB, 730gt | 2x Athlon FX 74, 8GB, 8800gts 512 | FX 9590, 16GB, R9 295x2 | E350, 8GB | Phenom X4 2.6ghz, 16GB, 8800gt | random core2 duo/atom/i5/i7 laptops
(plySetGenome gPlayer (list 'Varalyn 'nonBinary))
Homelab Servers: Xeon Silver 4110, 16GB | Via Quadcore C4650, 16GB | Athlon 200GE, 8GB | i7 7800X, 32GB | Threadripper 1950X, 32GB | Atom x5 8350, 4GB | Opteron 8174, 16GB | Xeon E5 2620 v3, 8GB | 2x Xeon Silver 4116, 96GB, 2x 1080ti | i7 8700, 32GB, 6500XT
Workstations & Render machines: Threadripper 3990X, 128GB, 6900XT | Threadripper 2990WX, 32GB, 1080ti | Xeon Platinum 8173M, 48GB, 1070ti | R9 3900X, 16GB, Vega64 | 2x E5 2430L v2, 24GB, 970 | R7 3700X, 32GB, A6000
Gaming Systems: R9 5950X, 32GB, 6700XT
Office Systems: Xeon 5318Y, 256GB, A4000
Misc Systems: R5 3500U, 20GB | R5 2400G, 16GB | i5 7640X, 16GB, Vega56 | E5 2620, 8GB, R5 260 | P4 1.8ghz, 0.75GB, Voodoo 5 5500 | Athlon 64 x2 4400+, 1.5GB, FX 5800 Ultra | Pentium D 3.2ghz, 4GB, 7600gt | Celeron g460, 8GB, 730gt | 2x Athlon FX 74, 8GB, 8800gts 512 | FX 9590, 16GB, R9 295x2 | E350, 8GB | Phenom X4 2.6ghz, 16GB, 8800gt | random core2 duo/atom/i5/i7 laptops