View Issue Details

IDProjectCategoryView StatusLast Update
0003052FSSCPSEXPspublic2014-06-09 01:22
ReporterThe_E Assigned ToMageKing17  
PrioritynormalSeverityminorReproducibilityalways
Status resolvedResolutionfixed 
Product Version3.7.2 
Target Version3.7.2 
Summary0003052: "targeted" sexp returns KNOWN_FALSE in conjunction with argument list
DescriptionIn the BP WiH mission "Her Finest Hour", we're using a sexp with an argument list as part of the mechanism that allows the player to call in artillery strikes. That system works for some time, but stops soon.
Thanks to diligent debugging work by MageKing17, we finally found the cause. The "targeted" sexp returns "KNOWN_FALSE" when asked to evaluate a target that is already dead, this causes the entire (when-argument controlled) event to go into "KNOWN_FALSE", thus stopping the event from working.

A patch to change this behaviour (So that targeted returns "CANT_EVAL" in cases like this) is attached.
TagsNo tags attached.

Activities

The_E

2014-05-30 18:53

administrator  

sexp.cpp.patch (399 bytes)   
Index: sexp.cpp
===================================================================
--- sexp.cpp	(revision 10726)
+++ sexp.cpp	(working copy)
@@ -14911,9 +14911,7 @@
 	ship_subsys *ptr;
 
 	z = ship_query_state(CTEXT(node));
-	if (z == 1){
-		return SEXP_KNOWN_FALSE;  // ship isn't around, nor will it ever be
-	} else if (z == -1) {
+	if (z == 1 || z == -1) {
 		return SEXP_CANT_EVAL;
 	}
 
sexp.cpp.patch (399 bytes)   

MageKing17

2014-05-31 04:43

developer   ~0015775

Out of curiosity, I looked into the SEXP code to see exactly what it was doing, and I'm scratching my head, because eval_any_of isn't supposed to return SEXP_KNOWN_FALSE unless every single valid argument returns SEXP_KNOWN_FALSE. Whether or not "targeted" should return SEXP_KNOWN_FALSE, it shouldn't actually be locking out "any-of" in this case, so the bug would seem to be with "any-of", not "targeted".

niffiwan

2014-05-31 08:43

developer   ~0015777

From reading MageKing17's note I don't believe this is ready for review yet.

MageKing17

2014-06-02 05:29

developer  

sexp.cpp.mk17.patch (2,009 bytes)   
Index: sexp.cpp
===================================================================
--- sexp.cpp	(revision 10739)
+++ sexp.cpp	(working copy)
@@ -8468,13 +8468,13 @@
 		// only eval this argument if it's valid
 		if (Sexp_nodes[n].flags & SNF_ARGUMENT_VALID)
 		{
-			// flush conditional to avoid short-circuiting
-			flush_sexp_tree(condition_node);
-
 			// evaluate conditional for current argument
 			Sexp_replacement_arguments.push_back(Sexp_nodes[n].text);
 			val = eval_sexp(condition_node);
 
+			// flush conditional to avoid short-circuiting
+			flush_sexp_tree(condition_node);
+
 			switch (val)
 			{
 				case SEXP_TRUE:
@@ -8542,13 +8542,13 @@
 	{
 		// since we can't see or modify the validity, assume all are valid
 		{
-			// flush conditional to avoid short-circuiting
-			flush_sexp_tree(condition_node);
-
 			// evaluate conditional for current argument
 			Sexp_replacement_arguments.push_back(argument_vector[i]);
 			val = eval_sexp(condition_node);
 
+			// flush conditional to avoid short-circuiting
+			flush_sexp_tree(condition_node);
+
 			switch (val)
 			{
 				case SEXP_TRUE:
@@ -8741,12 +8741,14 @@
 	{
 		// flush stuff
 		Sexp_applicable_argument_list.clear_nesting_level();
-		flush_sexp_tree(condition_node);
 
 		// evaluate conditional for current argument
 		Sexp_replacement_arguments.push_back(Sexp_nodes[n].text);
 		val = eval_sexp(condition_node);
 
+		// flush conditional to avoid short-circuiting
+		flush_sexp_tree(condition_node);
+
 		// true?
 		if (val == SEXP_TRUE || val == SEXP_KNOWN_TRUE)
 		{
@@ -8790,12 +8792,14 @@
 	{
 		// flush stuff
 		Sexp_applicable_argument_list.clear_nesting_level();
-		flush_sexp_tree(condition_node);
 
 		// evaluate conditional for current argument
 		Sexp_replacement_arguments.push_back(Sexp_nodes[n].text);
 		val = eval_sexp(condition_node);
 
+		// flush conditional to avoid short-circuiting
+		flush_sexp_tree(condition_node);
+
 		// true?
 		if (val == SEXP_TRUE || val == SEXP_KNOWN_TRUE)
 		{
sexp.cpp.mk17.patch (2,009 bytes)   

MageKing17

2014-06-02 05:30

developer   ~0015781

I've attached a patch (which I have tested with Her Finest Hour) that should fix the root problem.

Goober5000

2014-06-03 02:12

administrator   ~0015783

Whoa, this should not have been committed without a lot more review. I've rolled it back.

First of all, this patch violates the assumption of the multiple argument technique, which is that the evaluation starts off with a clean slate. Moving the tree flushing to *after* the evaluation causes the evaluation to occur with whatever values the sexp tree previously held. Now one might argue that the effect is the same in either case, but the fact that the patch changes the behavior indicates that it is not a no-op.

Second of all, the "targeted" sexp is a retail sexp. Likewise, "known-false" is a retail condition. Returning known-false when the ship is destroyed is precisely the correct behavior for this sexp. I am curious as to what exactly is going on with ships that apparently need to be evaluated even after they are destroyed, but in any case, if the evaluation needs to happen repeatedly without regard to known-x, the proper solution is to use the every-time or every-time-argument conditional. So this is a FRED bug, not a code bug.

All that said, MageKing17's first comment on this bug ticket is an appropriate concern. I would like to talk to him to get more details on his bug hunting efforts. It is certainly possible that there is indeed a bug, albeit not the one assumed by this ticket report.

Axem

2014-06-03 02:33

reporter   ~0015784

I made a mission that reproduces the original issue from BP:WiH. There's an when any-of argument that sends a message when one of the three capital ships is targetted. Press 1 to self destruct the Deimos (the last argument). When the Deimos finishes blowing up, the messages stop being sent when the remaining 2 ships are targetted.

Axem

2014-06-03 02:33

reporter  

TargettingArgs.fs2 (12,143 bytes)

Goober5000

2014-06-03 03:38

administrator  

revised_sexp.cpp.patch (1,015 bytes)   
Index: code/parse/sexp.cpp
===================================================================
--- code/parse/sexp.cpp	(revision 10747)
+++ code/parse/sexp.cpp	(working copy)
@@ -8294,7 +8294,7 @@
  */
 int eval_when(int n, int when_op_num)
 {
-	int cond, val, actions;
+	int arg_handler = -1, cond, val, actions;
 	Assert( n >= 0 );
 	arg_item *ptr;
 
@@ -8301,7 +8301,7 @@
 	// get the parts of the sexp and evaluate the conditional
 	if (is_blank_argument_op(when_op_num))
 	{
-		int arg_handler = CAR(n);
+		arg_handler = CAR(n);
 		cond = CADR(n);
 		actions = CDDR(n);
 
@@ -8393,7 +8393,10 @@
 		Sexp_current_argument_nesting_level--;
 	}
 
-	if (Sexp_nodes[cond].value == SEXP_KNOWN_FALSE)
+	// thanks to MageKing17, we need to short-circuit on the correct node!
+	int short_circuit_node = (arg_handler >= 0) ? arg_handler : cond;
+
+	if (Sexp_nodes[short_circuit_node].value == SEXP_KNOWN_FALSE)
 		return SEXP_KNOWN_FALSE;  // no need to waste time on this anymore
 
 	if (val == SEXP_KNOWN_FALSE)
revised_sexp.cpp.patch (1,015 bytes)   

Goober5000

2014-06-03 04:04

administrator   ~0015785

Fix committed to trunk@10748.

Goober5000

2014-06-03 04:06

administrator   ~0015786

This fix should be credited to MageKing17 aka AdmiralRalwood, so I'm unassigning it.

Goober5000

2014-06-03 04:07

administrator   ~0015787

The actual cause of the bug was a loose end that escaped notice in the original design of when-argument. A function was using the most recent conditional value evaluated by an argument handler (such as any-of) instead of the value of the argument handler itself.

Related Changesets

fs2open: trunk r10741

2014-06-02 11:07

The_E


Ported: N/A

Details Diff
From MageKing17: Fix for Mantis 3502 (Flush the sexp result tree after argument evaluation to avert accidental short-circuit results) Affected Issues
0003052
mod - /trunk/fs2_open/code/parse/sexp.cpp Diff File

fs2open: trunk r10743

2014-06-02 21:28

Goober5000


Ported: N/A

Details Diff
roll back r10741 pending further code review; see Mantis 0003052 Affected Issues
0003052
mod - /trunk/fs2_open/code/parse/sexp.cpp Diff File

fs2open: trunk r10748

2014-06-02 23:21

Goober5000


Ported: N/A

Details Diff
fix a bug in the when-argument sexp that managed to escape detection from its original design!
spotted by MageKing17, fixes Mantis 0003052
Affected Issues
0003052
mod - /trunk/fs2_open/code/parse/sexp.cpp Diff File

Issue History

Date Modified Username Field Change
2014-05-30 18:53 The_E New Issue
2014-05-30 18:53 The_E Status new => assigned
2014-05-30 18:53 The_E Assigned To => The_E
2014-05-30 18:53 The_E File Added: sexp.cpp.patch
2014-05-31 04:43 MageKing17 Note Added: 0015775
2014-05-31 07:22 Echelon9 Status assigned => code review
2014-05-31 08:43 niffiwan Note Added: 0015777
2014-06-02 05:29 MageKing17 File Added: sexp.cpp.mk17.patch
2014-06-02 05:30 MageKing17 Note Added: 0015781
2014-06-03 02:12 Goober5000 Note Added: 0015783
2014-06-03 02:12 Goober5000 Changeset attached => fs2open trunk r10743
2014-06-03 02:12 Goober5000 Assigned To The_E => Goober5000
2014-06-03 02:12 Goober5000 Status code review => assigned
2014-06-03 02:13 Goober5000 Changeset attached => fs2open trunk r10741
2014-06-03 02:33 Axem Note Added: 0015784
2014-06-03 02:33 Axem File Added: TargettingArgs.fs2
2014-06-03 03:38 Goober5000 File Added: revised_sexp.cpp.patch
2014-06-03 04:04 Goober5000 Changeset attached => fs2open trunk r10748
2014-06-03 04:04 Goober5000 Note Added: 0015785
2014-06-03 04:04 Goober5000 Status assigned => resolved
2014-06-03 04:04 Goober5000 Resolution open => fixed
2014-06-03 04:06 Goober5000 Note Added: 0015786
2014-06-03 04:06 Goober5000 Assigned To Goober5000 =>
2014-06-03 04:07 Goober5000 Note Added: 0015787
2014-06-09 01:22 Goober5000 Assigned To => MageKing17