Page MenuHomeWildfire Games

Prevent NaN resource amounts if a non-natural batch train size was chosen
ClosedPublic

Authored by elexis on Jan 14 2017, 6:57 PM.

Details

Summary

As reported by user1 and some other lobby players, if a non-natural number is used in the batch-train-size option,
the simulation becomes broken in various ways.

Test Plan

Don't apply the patch, start a game, open the options, set a string that isn't a number and observe
the GUI breaking when pressing shift and training something, as well as
having NaN resources, that avoid the resource cost restriction.
A non-natural number as a string will train N % 1 units and the remaining fraction can't be trained.
The population counter can also becomes a non-natural number.

Apply only the simulation patch to verify that even malicious multiplayer clients can't break the simulation anymore when controlling their GUI.
Apply the entire patch to verify that casual player can't break anything, even if they type a non-natural number or non-number in that option field.

Diff Detail

Repository
rP 0 A.D. Public Repository
Branch
batchtrainNaN
Lint
No Linters Available
Unit
No Unit Test Coverage
Build Status
Buildable 242
Build 385: Vulcan BuildJenkins
Build 384: arc lint + arc unit

Event Timeline

elexis updated this revision to Diff 226.Jan 14 2017, 6:57 PM
elexis retitled this revision from to Prevent NaN resource amounts if a non-natural batch train size was chosen.
elexis updated this object.
elexis edited the test plan for this revision. (Show Details)
elexis updated the Trac tickets for this revision.
wraitii requested changes to this revision.Jan 14 2017, 7:17 PM
wraitii added a reviewer: wraitii.
wraitii added a subscriber: wraitii.
wraitii added inline comments.
binaries/data/mods/public/gui/session/input.js
1365

It'd be nice to force this to be >= 1, just in case something changes in the future in the underlying code.

1448

It'd be nice to force this to be >= 1, just in case something changes in the future in the following code.

This revision now requires changes to proceed.Jan 14 2017, 7:17 PM

The GUI already prevents entering numbers smaller than 1 and that's not going to change as the number of entities will always be a natural number.
By editing the user.cfg, this can still be set to 0 or a negative number. However the simulation and GUI still work correctly. The user can only shoot himself in the foot, noone else.

Vulcan added a subscriber: Vulcan.Jan 14 2017, 9:09 PM

Build is green

Updating workspaces.
Build (release)...
Build (debug)...
Running debug tests...
Running cxxtest tests (302 tests)..............................................................................................................................................................................................................................................................................................................OK!

http://jw:8080/job/phabricator/165/ for more details.

We'll have to check that a slightly malicious host can't set up the game in a way that makes his player an AI and thus enables him to bypass that one check. I guess that could even be something wanted for a trainer AI, but meh.

binaries/data/mods/public/simulation/helpers/Commands.js
269

Why not floor? User code that supplies something that isn't an int is broken anyway. Actually why not

if (Math.round(+cmd.count) != +cmd.count)
{
    warn("fix that gui of yours.");
    return;
}
```? (Yes, that most likely breaks on `NaN`, but so does the current code.)
wraitii accepted this revision.Jan 15 2017, 7:13 AM
wraitii edited edge metadata.

This would be a good spot for a regression test on second thought, but otherwise imo you can commit this.

This revision is now accepted and ready to land.Jan 15 2017, 7:13 AM
elexis planned changes to this revision.Jan 15 2017, 6:30 PM

Obviously this is broken all over the place in the simulation, for example we can also send NaN resource amounts to a player when sending a custom command via F9.

elexis marked an inline comment as done.Jan 17 2017, 10:24 AM
In D66#2061, @leper wrote:

We'll have to check that a slightly malicious host can't set up the game in a way that makes his player an AI and thus enables him to bypass that one check. I guess that could even be something wanted for a trainer AI, but meh.

(That must have been meant for D65)

For the wiki page, suggesting to add

=== Batch simulating games ===
The behavior of the AI can be tested by running multiple games consecutively,
for example by analyzing the summary screen data at the end of the game from the replay menu.

As described in source:/ps/trunk/binaries/system/readme.txt, a new match can be started directly by passing the mapname and player assignments as command line arguments.

Add an `API3.exit()` statement to an arbitrary place of the AI code or
an `Engine.ExitProgram()` statement to the GUI code (for example if all players have been defeated or won in `messages.js`).

Using a unix shell or windows batch script allows to repeatedly start matches after the previous pyrogenesis instance exited.
binaries/data/mods/public/simulation/helpers/Commands.js
269

We should avoid adding warnings to every place where a modder could break the GUI, since they'd be everywhere.
Also it can become broken by editing the user.cfg.

Adding a fix to test for all cases.

In D66#2362, @elexis wrote:
In D66#2061, @leper wrote:

We'll have to check that a slightly malicious host can't set up the game in a way that makes his player an AI and thus enables him to bypass that one check. I guess that could even be something wanted for a trainer AI, but meh.

(That must have been meant for D65)

Indeed, same for your wiki addition ;-)

binaries/data/mods/public/simulation/helpers/Commands.js
269

Then we just encourage them to write broken code (which I do appreciate for testing the simulation sanity checks, but I'd rather have them not write broken code).

I guess we might need some sanity checking for places where we take numbers from the GUI.

And I think I still prefer floor here.

elexis updated this revision to Diff 2668.Jun 23 2017, 5:45 PM
elexis marked 3 inline comments as done.

Number.isInteger check.
ProductionQueue check.
Merge duplication.

1 check.

Probably nicer to read if the other places are fixed in separate patches.

This revision is now accepted and ready to land.Jun 23 2017, 5:45 PM

Build is green

Updating workspaces.
Build (release)...
Build (debug)...
Running release tests...
Running cxxtest tests (306 tests)..................................................................................................................................................................................................................................................................................................................OK!
Running debug tests...
Running cxxtest tests (306 tests)..................................................................................................................................................................................................................................................................................................................OK!
Checking XML files...

http://jw:8080/job/phabricator/1614/ for more details.

Executing section Default...
Executing section Source...
Executing section JS...
|    | [NORMAL] ESLintBear (no-else-return):
|    | Unnecessary 'else' after 'return'.
|----|    | /mnt/data/jenkins-phabricator/workspace/phabricator_lint/binaries/data/mods/public/simulation/components/ProductionQueue.js
|    |++++| /mnt/data/jenkins-phabricator/workspace/phabricator_lint/binaries/data/mods/public/simulation/components/ProductionQueue.js
| 240| 240| 		return (cmpTechnologyManager.IsTechnologyResearched(template.top) || cmpTechnologyManager.IsInProgress(template.top)
| 241| 241| 			|| cmpTechnologyManager.IsTechnologyResearched(template.bottom) || cmpTechnologyManager.IsInProgress(template.bottom));
| 242| 242| 	}
| 243|    |-	else
| 244|    |-	{
|    | 243|+	
| 245| 244| 		return (cmpTechnologyManager.IsTechnologyResearched(tech) || cmpTechnologyManager.IsInProgress(tech));
| 246|    |-	}
|    | 245|+	
| 247| 246| };
| 248| 247| 
| 249| 248| /*
|    | [NORMAL] ESLintBear (no-multi-spaces):
|    | Multiple spaces found before '+'.
|----|    | /mnt/data/jenkins-phabricator/workspace/phabricator_lint/binaries/data/mods/public/simulation/components/ProductionQueue.js
|    |++++| /mnt/data/jenkins-phabricator/workspace/phabricator_lint/binaries/data/mods/public/simulation/components/ProductionQueue.js
| 299| 299| 				totalCosts[res] = Math.floor(count * costs[res]);
| 300| 300| 			}
| 301| 301| 
| 302|    |-			var population = ApplyValueModificationsToTemplate("Cost/Population",  +template.Cost.Population, cmpPlayer.GetPlayerID(), template);
|    | 302|+			var population = ApplyValueModificationsToTemplate("Cost/Population", +template.Cost.Population, cmpPlayer.GetPlayerID(), template);
| 303| 303| 
| 304| 304| 			// TrySubtractResources should report error to player (they ran out of resources)
| 305| 305| 			if (!cmpPlayer.TrySubtractResources(totalCosts))
|    | [NORMAL] ESLintBear (no-multi-spaces):
|    | Multiple spaces found before 'techCostMultiplier'.
|----|    | /mnt/data/jenkins-phabricator/workspace/phabricator_lint/binaries/data/mods/public/simulation/components/ProductionQueue.js
|    |++++| /mnt/data/jenkins-phabricator/workspace/phabricator_lint/binaries/data/mods/public/simulation/components/ProductionQueue.js
| 349| 349| 			}
| 350| 350| 
| 351| 351| 			let techCostMultiplier = this.GetTechCostMultiplier();
| 352|    |-			let time =  techCostMultiplier.time * template.researchTime * cmpPlayer.GetCheatTimeMultiplier();
|    | 352|+			let time = techCostMultiplier.time * template.researchTime * cmpPlayer.GetCheatTimeMultiplier();
| 353| 353| 
| 354| 354| 			let cost = {};
| 355| 355| 			for (let res in template.cost)
|    | [NORMAL] ESLintBear (no-undef-init):
|    | It's not necessary to initialize 'cmpAutoGarrison' to undefined.
|----|    | /mnt/data/jenkins-phabricator/workspace/phabricator_lint/binaries/data/mods/public/simulation/components/ProductionQueue.js
|    |++++| /mnt/data/jenkins-phabricator/workspace/phabricator_lint/binaries/data/mods/public/simulation/components/ProductionQueue.js
| 596| 596| 		}
| 597| 597| 	}
| 598| 598| 
| 599|    |-	var cmpAutoGarrison = undefined;
|    | 599|+	var cmpAutoGarrison;
| 600| 600| 	if (cmpRallyPoint)
| 601| 601| 	{
| 602| 602| 		var data = cmpRallyPoint.GetData()[0];
|    | [NORMAL] ESLintBear (no-multi-spaces):
|    | Multiple spaces found before '+'.
|----|    | /mnt/data/jenkins-phabricator/workspace/phabricator_lint/binaries/data/mods/public/simulation/components/ProductionQueue.js
|    |++++| /mnt/data/jenkins-phabricator/workspace/phabricator_lint/binaries/data/mods/public/simulation/components/ProductionQueue.js
| 706| 706| 			{
| 707| 707| 				// If something change population cost
| 708| 708| 				var template = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager).GetTemplate(item.unitTemplate);
| 709|    |-				item.population = ApplyValueModificationsToTemplate("Cost/Population",  +template.Cost.Population, item.player, template);
|    | 709|+				item.population = ApplyValueModificationsToTemplate("Cost/Population", +template.Cost.Population, item.player, template);
| 710| 710| 
| 711| 711| 				// Batch's training hasn't started yet.
| 712| 712| 				// Try to reserve the necessary population slots

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 352| »   »   »   let·time·=··techCostMultiplier.time·*·template.researchTime·*·cmpPlayer.GetCheatTimeMultiplier();
|    | [NORMAL] ESLintBear (no-shadow):
|    | 'time' is already declared in the upper scope.

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 189| »   for·(var·i·in·techList)
|    | [NORMAL] JSHintBear:
|    | 'i' is already defined.

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 191| »   »   var·tech·=·techList[i];
|    | [NORMAL] JSHintBear:
|    | 'tech' is already defined.

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 203| »   for·(var·i·=·0;·i·<·techList.length;·i++)
|    | [NORMAL] JSHintBear:
|    | 'i' is already defined.

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 205| »   »   var·tech·=·techList[i];
|    | [NORMAL] JSHintBear:
|    | 'tech' is already defined.

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 212| »   »   var·template·=·cmpTechnologyManager.GetTechnologyTemplate(tech);
|    | [NORMAL] JSHintBear:
|    | 'template' is already defined.

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 241| »   »   »   ||·cmpTechnologyManager.IsTechnologyResearched(template.bottom)·||·cmpTechnologyManager.IsInProgress(template.bottom));
|    | [NORMAL] JSHintBear:
|    | Misleading line break before '||'; readers may interpret this as an expression boundary.

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 279| »   »   »   »   if·(requiredXp·==·0)
|    | [NORMAL] JSHintBear:
|    | Use '===' to compare with '0'.

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 337| »   »   »   var·template·=·cmpDataTemplateManager.GetTechnologyTemplate(templateName);
|    | [NORMAL] JSHintBear:
|    | 'template' is already defined.

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 366| »   »   »   if·(this.queue.length·==·0)
|    | [NORMAL] JSHintBear:
|    | Use '===' to compare with '0'.

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 381| »   »   »   var·cmpTrigger·=·Engine.QueryInterface(SYSTEM_ENTITY,·IID_Trigger);
|    | [NORMAL] JSHintBear:
|    | 'cmpTrigger' is already defined.

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 420| »   for·(var·i·=·0;·i·<·this.queue.length;·++i)
|    | [NORMAL] JSHintBear:
|    | 'i' is already defined.

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 577| »   if·(this.entityCache.length·==·0)
|    | [NORMAL] JSHintBear:
|    | Use '===' to compare with '0'.

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 599| »   var·cmpAutoGarrison·=·undefined;
|    | [NORMAL] JSHintBear:
|    | It's not necessary to initialize 'cmpAutoGarrison' to 'undefined'.

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 607| »   for·(var·i·=·0;·i·<·count;·++i)
|    | [NORMAL] JSHintBear:
|    | 'i' is already defined.

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 609| »   »   var·ent·=·this.entityCache[0];
|    | [NORMAL] JSHintBear:
|    | 'ent' is already defined.

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 642| »   »   if·(createdEnts.length·==·0)
|    | [NORMAL] JSHintBear:
|    | Use '===' to compare with '0'.

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 779| »   »   »   »   »   var·cmpPlayer·=·QueryOwnerInterface(this.entity);
|    | [NORMAL] JSHintBear:
|    | 'cmpPlayer' is already defined.

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 790| »   »   »   var·cmpTechnologyManager·=·QueryOwnerInterface(this.entity,·IID_TechnologyManager);
|    | [NORMAL] JSHintBear

http://jw:8080/job/phabricator_lint/231/ for more details.

elexis changed the visibility from "All Users" to "Public (No Login Required)".Jun 24 2017, 4:36 PM
In D66#2076, @wraitii wrote:

This would be a good spot for a regression test on second thought, but otherwise imo you can commit this.

If we write a test AddBatch in test_ProductionQueue by passing a NaN we also run into the issue of having to silence warnings for a specific test (because that should definitely show a warning the code before returning).

binaries/data/mods/public/gui/session/input.js
1448

Might just as well nuke the duplication then. A new function will also force this to be >= 1, in case something changes in the future in the following code.

binaries/data/mods/public/simulation/helpers/Commands.js
269

Then the shortest and most accurate check would be https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger

for (let value of [1, 1.1, "1", -1, 0, NaN, ({}).nope, undefined])
	warn(uneval(value) + " isNumber: " + uneval(Number.isInteger(value)));
WARNING: 1 isNumber: true
WARNING: 1.1 isNumber: false
WARNING: "1" isNumber: false
WARNING: -1 isNumber: true
WARNING: 0 isNumber: true
WARNING: NaN isNumber: false
WARNING: (void 0) isNumber: false
WARNING: (void 0) isNumber: false
elexis updated this revision to Diff 2673.Jun 24 2017, 5:31 PM

As fatherbushido mentioned, isNumber also returns false for Infinity, whereas Math.floor(x) == x returns true (and we want to return false).
Also error out on negative numbers.
In particular prevent display of warnings for all players if someone set a non-numerical string by accident.

Build is green

Updating workspaces.
Build (release)...
Build (debug)...
Running release tests...
Running cxxtest tests (306 tests)..................................................................................................................................................................................................................................................................................................................OK!
Running debug tests...
Running cxxtest tests (306 tests)..................................................................................................................................................................................................................................................................................................................OK!
Checking XML files...

http://jw:8080/job/phabricator/1618/ for more details.

Executing section Default...
Executing section Source...
Executing section JS...

binaries/data/mods/public/simulation/helpers/InitGame.js
|  51| »   »   if·(settings.PlayerData[i]·&&·settings.PlayerData[i].AI·&&·settings.PlayerData[i].AI·!=·"")
|    | [NORMAL] JSHintBear:
|    | Use '!==' to compare with ''.
|    | [NORMAL] ESLintBear (no-else-return):
|    | Unnecessary 'else' after 'return'.
|----|    | /mnt/data/jenkins-phabricator/workspace/phabricator_lint/binaries/data/mods/public/simulation/components/ProductionQueue.js
|    |++++| /mnt/data/jenkins-phabricator/workspace/phabricator_lint/binaries/data/mods/public/simulation/components/ProductionQueue.js
| 240| 240| 		return (cmpTechnologyManager.IsTechnologyResearched(template.top) || cmpTechnologyManager.IsInProgress(template.top)
| 241| 241| 			|| cmpTechnologyManager.IsTechnologyResearched(template.bottom) || cmpTechnologyManager.IsInProgress(template.bottom));
| 242| 242| 	}
| 243|    |-	else
| 244|    |-	{
|    | 243|+	
| 245| 244| 		return (cmpTechnologyManager.IsTechnologyResearched(tech) || cmpTechnologyManager.IsInProgress(tech));
| 246|    |-	}
|    | 245|+	
| 247| 246| };
| 248| 247| 
| 249| 248| /*
|    | [NORMAL] ESLintBear (no-multi-spaces):
|    | Multiple spaces found before '+'.
|----|    | /mnt/data/jenkins-phabricator/workspace/phabricator_lint/binaries/data/mods/public/simulation/components/ProductionQueue.js
|    |++++| /mnt/data/jenkins-phabricator/workspace/phabricator_lint/binaries/data/mods/public/simulation/components/ProductionQueue.js
| 299| 299| 				totalCosts[res] = Math.floor(count * costs[res]);
| 300| 300| 			}
| 301| 301| 
| 302|    |-			var population = ApplyValueModificationsToTemplate("Cost/Population",  +template.Cost.Population, cmpPlayer.GetPlayerID(), template);
|    | 302|+			var population = ApplyValueModificationsToTemplate("Cost/Population", +template.Cost.Population, cmpPlayer.GetPlayerID(), template);
| 303| 303| 
| 304| 304| 			// TrySubtractResources should report error to player (they ran out of resources)
| 305| 305| 			if (!cmpPlayer.TrySubtractResources(totalCosts))
|    | [NORMAL] ESLintBear (no-multi-spaces):
|    | Multiple spaces found before 'techCostMultiplier'.
|----|    | /mnt/data/jenkins-phabricator/workspace/phabricator_lint/binaries/data/mods/public/simulation/components/ProductionQueue.js
|    |++++| /mnt/data/jenkins-phabricator/workspace/phabricator_lint/binaries/data/mods/public/simulation/components/ProductionQueue.js
| 349| 349| 			}
| 350| 350| 
| 351| 351| 			let techCostMultiplier = this.GetTechCostMultiplier();
| 352|    |-			let time =  techCostMultiplier.time * template.researchTime * cmpPlayer.GetCheatTimeMultiplier();
|    | 352|+			let time = techCostMultiplier.time * template.researchTime * cmpPlayer.GetCheatTimeMultiplier();
| 353| 353| 
| 354| 354| 			let cost = {};
| 355| 355| 			for (let res in template.cost)
|    | [NORMAL] ESLintBear (no-undef-init):
|    | It's not necessary to initialize 'cmpAutoGarrison' to undefined.
|----|    | /mnt/data/jenkins-phabricator/workspace/phabricator_lint/binaries/data/mods/public/simulation/components/ProductionQueue.js
|    |++++| /mnt/data/jenkins-phabricator/workspace/phabricator_lint/binaries/data/mods/public/simulation/components/ProductionQueue.js
| 596| 596| 		}
| 597| 597| 	}
| 598| 598| 
| 599|    |-	var cmpAutoGarrison = undefined;
|    | 599|+	var cmpAutoGarrison;
| 600| 600| 	if (cmpRallyPoint)
| 601| 601| 	{
| 602| 602| 		var data = cmpRallyPoint.GetData()[0];
|    | [NORMAL] ESLintBear (no-multi-spaces):
|    | Multiple spaces found before '+'.
|----|    | /mnt/data/jenkins-phabricator/workspace/phabricator_lint/binaries/data/mods/public/simulation/components/ProductionQueue.js
|    |++++| /mnt/data/jenkins-phabricator/workspace/phabricator_lint/binaries/data/mods/public/simulation/components/ProductionQueue.js
| 706| 706| 			{
| 707| 707| 				// If something change population cost
| 708| 708| 				var template = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager).GetTemplate(item.unitTemplate);
| 709|    |-				item.population = ApplyValueModificationsToTemplate("Cost/Population",  +template.Cost.Population, item.player, template);
|    | 709|+				item.population = ApplyValueModificationsToTemplate("Cost/Population", +template.Cost.Population, item.player, template);
| 710| 710| 
| 711| 711| 				// Batch's training hasn't started yet.
| 712| 712| 				// Try to reserve the necessary population slots

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 352| »   »   »   let·time·=··techCostMultiplier.time·*·template.researchTime·*·cmpPlayer.GetCheatTimeMultiplier();
|    | [NORMAL] ESLintBear (no-shadow):
|    | 'time' is already declared in the upper scope.

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 189| »   for·(var·i·in·techList)
|    | [NORMAL] JSHintBear:
|    | 'i' is already defined.

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 191| »   »   var·tech·=·techList[i];
|    | [NORMAL] JSHintBear:
|    | 'tech' is already defined.

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 203| »   for·(var·i·=·0;·i·<·techList.length;·i++)
|    | [NORMAL] JSHintBear:
|    | 'i' is already defined.

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 205| »   »   var·tech·=·techList[i];
|    | [NORMAL] JSHintBear:
|    | 'tech' is already defined.

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 212| »   »   var·template·=·cmpTechnologyManager.GetTechnologyTemplate(tech);
|    | [NORMAL] JSHintBear:
|    | 'template' is already defined.

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 241| »   »   »   ||·cmpTechnologyManager.IsTechnologyResearched(template.bottom)·||·cmpTechnologyManager.IsInProgress(template.bottom));
|    | [NORMAL] JSHintBear:
|    | Misleading line break before '||'; readers may interpret this as an expression boundary.

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 279| »   »   »   »   if·(requiredXp·==·0)
|    | [NORMAL] JSHintBear:
|    | Use '===' to compare with '0'.

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 337| »   »   »   var·template·=·cmpDataTemplateManager.GetTechnologyTemplate(templateName);
|    | [NORMAL] JSHintBear:
|    | 'template' is already defined.

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 366| »   »   »   if·(this.queue.length·==·0)
|    | [NORMAL] JSHintBear:
|    | Use '===' to compare with '0'.

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 381| »   »   »   var·cmpTrigger·=·Engine.QueryInterface(SYSTEM_ENTITY,·IID_Trigger);
|    | [NORMAL] JSHintBear:
|    | 'cmpTrigger' is already defined.

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 420| »   for·(var·i·=·0;·i·<·this.queue.length;·++i)
|    | [NORMAL] JSHintBear:
|    | 'i' is already defined.

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 577| »   if·(this.entityCache.length·==·0)
|    | [NORMAL] JSHintBear:
|    | Use '===' to compare with '0'.

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 599| »   var·cmpAutoGarrison·=·undefined;
|    | [NORMAL] JSHintBear:
|    | It's not necessary to initialize 'cmpAutoGarrison' to 'undefined'.

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 607| »   for·(var·i·=·0;·i·<·count;·++i)
|    | [NORMAL] JSHintBear:
|    | 'i' is already defined.

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 609| »   »   var·ent·=·this.entityCache[0];
|    | [NORMAL] JSHintBear:
|    | 'ent' is already defined.

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 642| »   »   if·(createdEnts.length·==·0)
|    | [NORMAL] JSHintBear:
|    | Use '===' to compare with '0'.

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 779| »   »   »   »   »   var·cmpPlayer·=·QueryOwnerInterface(this.entity);
|    | [NORMAL] JSHintBear:
|    | 'cmpPlayer' is already defined.

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 790| »   »   »   var·cmpTechnologyManager·=·QueryOwnerInterface(this.entity,·IID_TechnologyManager);
|    | [NORMAL] JSHintBear:
|    | 'cmpTechnologyManager' is already defined.

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 793| »   »   »   var·template·=·cmpTechnologyManager.GetTechnologyTemplate(item.technologyTemplate);
|    | [NORMAL] JSHintBear:
|    | 'template' is already defined.

binaries/data/mods/public/simulation/components/ProductionQueue.js
| 811| »   if·(this.queue.length·==·0)
|    | [NORMAL] JSHintBear:
|    | Use '===' to compare with '0'.
|    | [NORMAL] ESLintBear (no-undef-init):
|    | It's not necessary to initialize 'target' to undefined.
|----|    | /mnt/data/jenkins-phabricator/workspace/phabricator_lint/binaries/data/mods/public/gui/session/input.js
|    |++++| /mnt/data/jenkins-phabricator/workspace/phabricator_lint/binaries/data/mods/public/gui/session/input.js
| 271| 271| 	if (!g_DevSettings.controlAll && !allOwnedByPlayer)
| 272| 272| 		return undefined;
| 273| 273| 
| 274|    |-	var target = undefined;
|    | 274|+	var target;
| 275| 275| 	if (!fromMinimap)
| 276| 276| 	{
| 277| 277| 		var ent = Engine.PickEntityAtPoint(x, y);
|    | [NORMAL] ESLintBear (no-undef-init):
|    | It's not necessary to initialize 'actionInfo' to undefined.
|----|    | /mnt/data/jenkins-phabricator/workspace/phabricator_lint/binaries/data/mods/public/gui/session/input.js
|    |++++| /mnt/data/jenkins-phabricator/workspace/phabricator_lint/binaries/data/mods/public/gui/session/input.js
| 285| 285| 	var actions = Object.keys(unitActions).slice();
| 286| 286| 	actions.sort((a, b) => unitActions[a].specificness - unitActions[b].specificness);
| 287| 287| 
| 288|    |-	var actionInfo = undefined;
|    | 288|+	var actionInfo;
| 289| 289| 	if (preSelectedAction != ACTION_NONE)
| 290| 290| 	{
| 291| 291| 		for (var action of actions)

binaries/data/mods/public/gui/session/input.js
| 267| »   »   var·entState·=·GetEntityState(ent);
|    | [NORMAL] ESLintBear (no-shadow):
|    | 'entState' is already declared in the upper scope.

binaries/data/mods/public/gui/session/input.js
| 492| »   switch·(ev.type)
|    | [NORMAL] ESLintBear (default-case):
|    | Expected a default case.

binaries/data/mods/public/gui/session/input.js
| 519| »   switch·(inputState)
|    | [NORMAL] ESLintBear (default-case):
|    | Expected a default case.

binaries/data/mods/public/gui/session/input.js
| 523| »   »   switch·(ev.type)
|    | [NORMAL] ESLintBear (default-case):
|    | Expected a default case.

binaries/data/mods/public/gui/session/input.js
| 578| »   »   switch·(ev.type)
|    | [NORMAL] ESLintBear (default-case):
|    | Expected a default case.

binaries/data/mods/public/gui/session/input.js
| 585| »   »   »   var·maxDragDelta·=·16;
|    | [NORMAL] ESLintBear (no-shadow):
|    | 'maxDragDelta' is already declared in the upper scope.

binaries/data/mods/public/gui/session/input.js
| 628| »   »   switch·(ev.type)
|    | [NORMAL] ESLintBear (default-case):
|    | Expected a default case.

binaries/data/mods/public/gui/session/input.js
| 657| »   »   switch·(ev.type)
|    | [NORMAL] ESLintBear (default-case):
|    | Expected a default case.

binaries/data/mods/public/gui/session/input.js
| 726| »   »   switch·(ev.type)
|    | [NORMAL] ESLintBear (default-case):
|    | Expected a default case.

binaries/data/mods/public/gui/session/input.js
| 843| »   switch·(inputState)
|    | [NORMAL] ESLintBear (default-case):
|    | Expected a default case.

binaries/data/mods/public/gui/session/input.js
| 846| »   »   switch·(ev.type)
|    | [NORMAL] ESLintBear (default-case):
|    | Expected a default case.

binaries/data/mods/public/gui/session/input.js
| 948| »   »   switch·(ev.type)
|    | [NORMAL] ESLintBear (default-case):
|    | Expected a default case.

binaries/data/mods/public/gui/session/input.js
|1040| »   »   switch·(ev.type)
|    | [NORMAL] ESLintBear (default-case):
|    | Expected a default case.

binaries/data/mods/public/gui/session/input.js
|1109| »   »   »   switch·(ev.hotkey)
|    | [NORMAL] ESLintBear (default-case):
|    | Expected a default case.

binaries/data/mods/public/gui/session/input.js
|1339| »   return·Number.isInteger(num)·&&·num·>·0·?·num·:·5;
|    | [NORMAL] ESLintBear (no-unreachable):
|    | Unreachable code.

binaries/data/mods/public/gui/session/input.js
|1533| »   switch·(action)
|    | [NORMAL] ESLintBear (default-case):
|    | Expected a default case.

binaries/data/mods/public/gui/session/input.js
| 232| »   »   var·entState·=·GetExtendedEntityState(entityID);
|    | [NORMAL] JSHintBear:
|    | 'entState' is already defined.

binaries/data/mods/public/gui/session/input.js
| 274| »   var·target·=·undefined;
|    | [NORMAL] JSHintBear:
|    | It's not necessary to initialize 'target' to 'undefined'.

binaries/data/mods/public/gui/session/input.js
| 288| »   var·actionInfo·=·undefined;
|    | [NORMAL] JSHintBear:
|    | It's not necessary to initialize 'actionInfo' to 'undefined'.

binaries/data/mods/public/gui/session/input.js
| 302| »   for·(var·action·of·actions)
|    | [NORMAL] JSHintBear:
|    | 'action' is already defined.

binaries/data/mods/public/gui/session/input.js
| 305| »   »   »   var·r·=·unitActions[action].hotkeyActionCheck(target,·selection);
|    | [NORMAL] JSHintBear:
|    | 'r' is already defined.

binaries/data/mods/public/gui/session/input.js
| 310| »   for·(var·action·of·actions)
|    | [NORMAL] JSHintBear:
|    | 'action' is already defined.

binaries/data/mods/public/gui/session/input.js
| 313| »   »   »   var·r·=·unitActions[action].actionCheck(target,·selection);
|    | [NORMAL] JSHintBear:
|    | 'r' is already defined.

binaries/data/mods/public/gui/session/input.js
| 503| »   mouseIsOverObject·=·(hoveredObject·!=·null);
|    | [NORMAL] JSHintBear:
|    | Use '!==' to compare with 'null'.

binaries/data/mods/public/gui/session/input.js
| 507| »   »   &&·(ev.button·==·SDL_BUTTON_LEFT·||·ev.button·==·SDL_BUTTON_RIGHT))
|    | [NORMAL] JSHintBear:
|    | Misleading line break before '&&'; readers may interpret this as an expression boundary.

binaries/data/mods/public/gui/session/input.js
| 537| »   »   »   »   var·rect·=·updateBandbox(bandbox,·ev,·true);
|    | [NORMAL] JSHintBear:
|    | 'rect' is already defined.

binaries/data/mods/public/gui/session/input.js
| 540| »   »   »   »   var·ents·=·getPreferredEntities(Engine.PickPlayerEntitiesInRect(rect[0],·rect[1],·rect[2],·rect[3],·g_ViewedPlayer));
|    | [NORMAL] JSHintBear:
|    | 'ents' is already defined.

binaries/data/mods/public/gui/session/input.js
| 689| »   »   »   »   »   var·queued·=·Engine.HotkeyIsPressed("session.queue");
|    | [NORMAL] JSHintBear:
|    | 'queued' is already defined.

binaries/data/mods/public/gui/session/input.js
| 729| »   »   »   var·dragDeltaX·=·ev.x·-·dragStart[0];
|    | [NORMAL] JSHintBear:
|    | 'dragDeltaX' is already defined.

binaries/data/mods/public/gui/session/input.js
| 730| »   »   »   var·dragDeltaY·=·ev.y·-·dragStart[1];
|    | [NORMAL] JSHintBear:
|    | 'dragDeltaY' is already defined.

binaries/data/mods/public/gui/session/input.js
| 731| »   »   »   var·maxDragDelta·=·16;
|    | [NORMAL] JSHintBear:
|    | 'maxDragDelta' is already defined.

binaries/data/mods/public/gui/session/input.js
| 763| »   »   »   »   var·queued·=·Engine.HotkeyIsPressed("session.queue");
|    | [NORMAL] JSHintBear:
|    | 'queued' is already defined.

binaries/data/mods/public/gui/session/input.js
| 879| »   »   »   »   if·(ev.hotkey.indexOf("selection.group.")·==·0)
|    | [NORMAL] JSHintBear:
|    | Use '===' to compare with '0'.

binaries/data/mods/public/gui/session/input.js
| 884| »   »   »   »   »   »   if·(ev.hotkey.indexOf("selection.group.select.")·==·0)
|    | [NORMAL] JSHintBear:
|    | Use '===' to compare with '0'.

binaries/data/mods/public/gui/session/input.js
| 892| »   »   »

http://jw:8080/job/phabricator_lint/234/ for more details.

This revision was automatically updated to reflect the committed changes.