Compare commits

...

544 Commits

Author SHA1 Message Date
abcdefg30
82acdbc32a Fix RA assets installation from the Steam C&C:R version 2023-08-01 22:29:52 +03:00
Gustas
462a3ef3c0 Make yaml node resolving optional 2023-07-30 20:39:21 +02:00
Gustas
d4e6815f64 Fix AutoTarget ignoring frozen actor bot targeting hack
Mirrors check of the function Recalculate which is found it TargetExtensions class
2023-07-30 20:32:18 +02:00
Gustas
723ffdf33d Revert Hunt to move within 2 cells of the target
Otherwise infantry get stuck within weapon range but outside of vision range
2023-07-30 20:26:41 +02:00
Gustas
8376b09129 Normalise depth charges 2023-07-29 14:22:30 -05:00
Gustas
fa9ce3e2ac Normalise TD anti-air values 2023-07-29 14:22:30 -05:00
Gustas
605681b252 Normalise RA anti-air values 2023-07-29 14:22:30 -05:00
dnqbob
a3c5945f2a Set BackwardDuration to -1 means ignore the time and set MaxBackwardCells to -1 means ignore the distance. 2023-07-29 18:01:40 +03:00
dnqbob
d7ef22d64f Add MaxBackwardCells for moving backward control 2023-07-29 18:01:40 +03:00
michaeldgg2
d907192be0 Added GrantConditionOnMinelaying trait
Uses INotifyMineLaying callbacks
2023-07-28 12:55:06 +03:00
michaeldgg2
74f8db0578 LayMines: render minefield cells only if the planned minefield has more than 1 cell 2023-07-28 12:55:06 +03:00
Smittytron
9dca6ef1c3 Add Soviet13a 2023-07-27 22:09:30 +03:00
Gustas
c093e7c90b Fix hunt incorrectly pathing to uncrushable targets 2023-07-27 16:14:29 +03:00
michaeldgg2
66cf912da0 LayMines: fixed occasional incorrect mine position when using BeginMinefield order
When laying mine with PreLayDelay > 0, end activity's tick immediately. That means don't try to immediately move to next cell.

This change unifies the behavior with scenario when a mine is laid without any PreLayDelay.
2023-07-26 22:05:42 +03:00
Gustas
42baa0c42f Rename update rule folder to 20230225 2023-07-26 20:00:54 +02:00
Gustas
305ba1c567 Add missing rule to UpdatePath 2023-07-26 20:00:54 +02:00
Gustas
a84b7591f6 Fix invalid color adjuster not considering saturation and V as mutable 2023-07-26 20:00:54 +02:00
Gustas
9d8f0634b1 Revert color validator 2023-07-26 20:00:54 +02:00
Gustas
4cd4e1f8ea Move PlayerExperience from Infiltrates to InfiltrateFor 2023-07-25 21:15:14 +02:00
Gustas
3207d01cf2 Consider AutoTarget ScanRadius when attack moving 2023-07-25 19:48:57 +02:00
Gustas
9fc0f79703 Add a description for AttackBomber 2023-07-25 13:11:17 +02:00
michaeldgg2
1a2d43fc99 WorldRenderer: use string.IsNullOrEmpty for check in Palette method
Unified usage of WorldRenderer.Palette method when it comes to appending player name (in case of player palette)
2023-07-25 13:33:11 +03:00
EoralMilk
b944b21325 Aircraft won't take off on terrain height change when idle 2023-07-25 12:48:50 +03:00
Gustas
c7e0bc4c08 Add missing carryall checks 2023-07-25 10:02:46 +03:00
Gustas
a69417f0a6 Fix caryall not removing influence when cargo dies 2023-07-25 10:02:46 +03:00
Gustas
c36609cc9f Don't call DetachCarryable every tick 2023-07-25 10:02:46 +03:00
Gustas
1edf313090 Don't calculate range when it is unused 2023-07-25 10:02:46 +03:00
RoosterDragon
813d48dd70 Ensure save file is closed after saving.
If you attempt to load after saving, this prevents an exception from the file being "in use" due to the unclosed file handle.
2023-07-18 23:44:40 +02:00
Gustas
9caf12d133 Add an option to order maps by size 2023-07-17 23:34:38 +02:00
Gustas
de5bcbbca5 Add an option to order maps by title 2023-07-17 23:34:38 +02:00
michaeldgg2
8aa548f70c Minelayer: extract creating BeginMinefield order so it can be triggered from outside 2023-07-17 20:18:52 +02:00
michaeldgg2
ce6e73dc92 Minelayer: supports specifying both mine laying and post laying delays 2023-07-17 20:18:52 +02:00
RoosterDragon
4a02e6c6cc Improve Exts.GetOrAdd method to avoid multiple lookups.
Use CollectionsMarshal to hold a ref to the dictionary entry. When the value needs to be added this allows us to set the value directly into it without having to locate the entry a second time.
2023-07-17 20:12:48 +02:00
RoosterDragon
d6a31bb0cc MiniYaml performance tweaks.
- Seal the classes, and make SourceLocation a readonly struct.
- In ToDictionary, use TryAdd to avoid a try-catch.
- In Merge, use ToList to ensure sources is only enumerated once.
2023-07-17 20:12:48 +02:00
RoosterDragon
f5daa19a1c Improve MiniYaml MergePartial performance.
- Track plain keys in a set, to avoid quadratic searches for plain node keys.
- Avoid the Concat iterator by looping twice instead.
2023-07-17 20:12:48 +02:00
RoosterDragon
a96e445e4d Handle duplicate nodes key checks in MiniYaml in a better place.
Moving the key duplication check allows a redundant check on top-level nodes to be avoided. Add tests to ensure key checks are functioning as expected.
2023-07-17 20:12:48 +02:00
RoosterDragon
30b1f926f2 Improve performance of MiniYaml inheritance tree tracking.
Use an ImmutableDictionary to avoid having to clone the inheritance tree in ResolveInherits. This avoids a lot of dictionary clones.
2023-07-17 20:12:48 +02:00
RoosterDragon
58e8b123db Avoid some allocations during loading.
- In FieldLoader, cache boxed bools and some boxed ints.
- In FieldLoader, presize collections when parsing a List, HashSet or Dictionary.
- In FieldLoader, don't allocate a list of missing items until required.
- In FieldLoader, when a string value is passed, avoid wrapping this in a MiniYaml object by allowing both strings and yaml to be passed in the GetValue overload that does the real work.
- In Animation, avoid allocating no-op actions.
- In VxlReader, use EnsureCapcity to better size the Dictionary.
- In VxlReader change VxlElement to a struct.
- In Locomotor, presize TerrainSpeeds dictionary.
2023-07-16 23:21:20 +02:00
RoosterDragon
be04d232c0 Avoid some allocations on the large object heap during loading.
- In MixFile, the Distinct call doesn't presize the HashSet it uses internally. As we know we will enumerate all results, create the HashSet ourselves so that is it presized correctly.
- In ObjectCreator, stream the assembly when hashing rather than reading all bytes into memory.

These changes avoid some allocations on the large object heap, in turn this means the GC can avoid performing unnecessary Gen 2 collections just to clear down the LOH.
2023-07-16 23:21:20 +02:00
Gustas
659ec5e335 Make phase transport uncloak on loading cargo 2023-07-14 17:25:30 +02:00
Paul Chote
7f37454666 Include Linux DE in OS sysinfo string. 2023-07-13 14:34:38 +03:00
michaeldgg2
433d69af7a Make Voiced trait conditional 2023-07-10 18:05:56 +03:00
Matthias Mailänder
21c21e4963 Update OpenAL. 2023-07-07 23:47:32 +02:00
RoosterDragon
1c0885c636 Improve loading performance for loader/compression classes.
- In RLEZerosCompression use dedicated Array.Clear method instead of open-coded loop.
- In VoxelLoader.GenerateSlicePlanes.Get use TryGetValue to avoid repeated array and dictionary lookups.
- In TmpTSLoader.UnpackTileData use ReadBytes to populate array with less overhead compared to repeated one byte reads.
- Resolve TODO in VqaVideo.
2023-07-07 22:36:54 +03:00
michaeldgg2
dccb3ce9ce Added extensibility points to LayMines activity:
Provides two callbacks using INotifyMineLaying interface: MineLaying (just before laying a mine), MineLaid after mine actor has been created (in FrameEndTask)
2023-07-04 23:52:25 +02:00
michaeldgg2
22b39f35aa Mine + related classes are public 2023-07-04 23:52:25 +02:00
michaeldgg2
5ab3276a2d Moved Minelaying related traits and activity to Common 2023-07-04 23:52:25 +02:00
penev92
36420114e0 Fixed packaging for Windows missing assembly info
Apparently building/publishing/packaging for Windows on Linux using .NET 6 sets some of the assembly / Portable Executable information for the generated DLL, but nothing for the generated EXE (which isn't the case when building on Windows).
2023-07-04 20:53:49 +01:00
penev92
7cda031888 Added product version to assembly info 2023-07-04 20:53:49 +01:00
penev92
fc85a4864d Added project information in Directory.Build.props 2023-07-04 20:53:49 +01:00
Gustas
99226c3df5 Always have ActorReference string on optional arrays instantiated 2023-07-01 18:29:59 +02:00
Gustas
ed395c8ace Fix linter crashing on null actor array references 2023-07-01 18:29:59 +02:00
Vapre
edbded8f0a PerfTickLogger, reduce overhead of logging long ticks. 2023-07-01 18:07:27 +02:00
abcdefg30
c095690619 Fix combined sequences using frames being broken 2023-07-01 12:56:50 +02:00
dnqbob
628cc837ef Fix a crash when RallyPoint creates RallyPointIndicator 2023-07-01 12:51:54 +02:00
RoosterDragon
0c32fca6c0 Fix slow saving of map previews.
Avoid quadratic behaviour when searching through actors by creating a dictionary lookup outside the loop.
2023-07-01 12:48:32 +02:00
IceReaper
56b5ace109 WSA use a Framerate of 15, source: FFMPEG. 2023-06-26 19:49:51 +02:00
IceReaper
5572650da2 Video FrameRate is now taken into account when video has no audio. 2023-06-26 19:49:51 +02:00
IceReaper
41669d246f Videos no longer use more vram then required. 2023-06-26 19:49:51 +02:00
abcdefg30
49c837e7d0 Fix \r\n-style line endings not being properly handled for script errors 2023-06-26 19:36:47 +02:00
RoosterDragon
bc5e7d1497 Compress all pngs within oramap files.
Reduces size used for oramap files from 2,774,405 bytes to 2,614,332 bytes in total. The smaller files also improve loading times as the zlib decoder has less bytes to schlep through.

The PNG decoder shipped with OpenRA only supports a bit depth of 8 for images with a palette, so must must ensure that other depths (1,2 or 4) are not used.

zopfilpng is used for compression with the following command line:
'zopflipng.exe --keepcolortype -y -m image.png image.png'

The keepcolortype flag ensures the bit depth is not changed.

The oramap files were updated by:
- Saving as a folder, unpacking the data in loose files.
- Compressing the png files using the command above.
- Saving back as an oramap, except instead of regenerating the map preview on save we save the existing version on disk.
2023-06-26 19:28:06 +02:00
RoosterDragon
78bef8a98f Compress all pngs.
Reduces size used for png files from 19,034,446 bytes to 13,305,798 bytes in total. The smaller files also improve loading times as the zlib decoder has less bytes to schlep through.

The PNG decoder shipped with OpenRA only supports a bit depth of 8 for images with a palette, so must must ensure that other depths (1,2 or 4) are not used.

zopfilpng is used for compression with the following command line:
'zopflipng.exe --keepcolortype -y -m image.png image.png'

The keepcolortype flag ensures the bit depth is not changed.
2023-06-26 19:28:06 +02:00
RoosterDragon
4f3d8f4caa Fix Png parsing.
A regression from 06df75ffee causes this parsing to fail depending on how the image was compressed.
2023-06-26 19:28:06 +02:00
Paul Chote
0369f7516d Work around Gnome 44 titlebar bug. 2023-06-26 19:21:51 +02:00
Gustas
3b0415678c RA Naval balance 2023-06-26 19:13:41 +02:00
Gustas
69867b6c06 Fix EMP'ed units turning 2023-06-21 20:55:52 +02:00
RoosterDragon
231bf01f18 Fix CA1854 2023-06-20 17:57:40 +02:00
RoosterDragon
56fe08cb00 Disable CA1845 2023-06-20 17:57:40 +02:00
RoosterDragon
f752e04b03 Fix CA1816 2023-06-20 17:57:40 +02:00
RoosterDragon
a50e72f68d Fix CA1802 2023-06-20 17:57:40 +02:00
RoosterDragon
0958197df2 Fix CA1052 2023-06-20 17:57:40 +02:00
RoosterDragon
f336a956cf Fix CA1012 2023-06-20 17:57:40 +02:00
darkademic
19fa03435b Only consider non-paused armaments (if any exist) when determining min/max range. 2023-06-20 16:46:21 +02:00
RoosterDragon
fa65e7fd3f Bump Linguini.Bundle to 0.5.0.
This version contains performance improvements for the parser, improving the loading time of translations.
2023-06-15 17:48:37 +02:00
RoosterDragon
f794cf69f9 In TypeDictionary.TrimExcess, also TrimExcess on the internal data dictionary size.
As TypeDictionary instances tend to live a long time without edits after being initially populated, this will reduce their long term memory footprint.
2023-06-13 23:52:44 +02:00
RoosterDragon
366dc5383c In HierarchicalPathFinder.BuildGrid, presize and reuse accessible cell set.
Most cells are accessible, so presizing to the full size of the grid is sensible to avoid allocations to resize as it is filled up. The set can also be reused across all layers to avoid allocating it many times.
2023-06-13 23:52:44 +02:00
michaeldgg2
fd2b14f464 DrawLineToTarget: made palette for rendering sprites customizable (and thus optional too) 2023-06-12 21:10:52 +03:00
Matthias Mailänder
590976a8e7 Add support for Wayland. 2023-06-12 19:57:35 +02:00
Paul Chote
bdbb651b98 Remove unused sequences and an awkward Combine. 2023-06-11 22:04:43 +02:00
Paul Chote
703618be19 Remove obsolete x64 and x64process sysinfo columns. 2023-06-11 13:18:49 +02:00
Paul Chote
1f37728ecf Return proper sysinfo OS names for Linux/macOS. 2023-06-11 13:18:49 +02:00
penev92
d955efff14 Updated configure-system-libraries for new OpenAL 2023-06-11 10:41:02 +02:00
penev92
3fdee06dc7 Updated other referenced NuGet packages 2023-06-11 10:41:02 +02:00
penev92
c0cd7259b3 Updated native dependencies NuGet packages 2023-06-11 10:41:02 +02:00
Matthias Mailänder
c31f2abfc9 Add sanity checks to the Lua script trait. 2023-06-11 11:03:38 +03:00
RoosterDragon
06df75ffee Improve PNG parsing performance.
Switch on the filter once per row rather than once per byte. This allows each row to be processed with a much tighter loop.
2023-06-10 16:20:02 +02:00
Matthias Mailänder
855e839b77 Try to fix chocolatey. 2023-06-09 19:25:36 +02:00
Gustas
06437df9b0 Fix CA1852 2023-06-06 14:13:04 +03:00
RoosterDragon
f4af5c1764 Fix CA1852 2023-06-06 11:51:47 +03:00
RoosterDragon
277699cbd5 Fix CA1822 2023-06-06 11:51:47 +03:00
Gustas
e4cac1fffc Add more varied pre-selected colors to player colors palette 2023-06-05 13:16:26 +02:00
Matthias Mailänder
e164e48aae Fix invalid integer expression syntax. 2023-06-04 21:29:15 +03:00
Matthias Mailänder
5eec9d29cb Add a lint check for invalid integer expression syntax. 2023-06-04 21:29:15 +03:00
Matthias Mailänder
94abd8a928 Revert "Revert "Replace legacy Evaluator with IntegerExpressions.""
This reverts commit 4f16b0d464.
2023-06-04 21:29:15 +03:00
JovialFeline
b18c2fe855 Add Mousetrap (scu33ea). 2023-06-03 21:45:18 +02:00
Gustas
02a7ff87db Fix MoveAdjacent activities cancelling queued activities 2023-06-03 13:43:44 +02:00
Vapre
7c0f6ead3a ActorMap, avoid IPositionable trait lookup. 2023-06-03 13:41:14 +02:00
Vapre
d72b1ffd49 ActorMap, do not look up influence node up to three times in cell layer. 2023-06-03 13:06:38 +02:00
Gustas
c82be175e1 Add RemoveNegativeSequenceLength update rule 2023-06-02 11:59:22 +02:00
michaeldgg2
dac35a60ad WithDecoration: fixed crash when Palette is null 2023-06-02 11:49:59 +02:00
abcdefg30
ee02af3605 Don't play low power notifications in the beginning of twist-of-fate 2023-06-01 20:42:39 +03:00
abcdefg30
74ed202b29 Support enabling and disabling the low power notification via Lua 2023-06-01 20:42:39 +03:00
RoosterDragon
300281695a Deserialize mod rules only once when loading all maps.
This avoids loading, parsing and merging YAML rules for the mod during loading of each individual map. This saves significant time resolving custom rules on each map loaded.
2023-06-01 12:59:53 +02:00
Matthias Mailänder
de22556153 Move unit testing into the make script. 2023-06-01 12:43:10 +02:00
Matthias Mailänder
5bcb1a678c Fix fluent plural forms. 2023-06-01 12:43:10 +02:00
Matthias Mailänder
8a9426a0d4 Add a test case for Fluent plural forms. 2023-06-01 12:43:10 +02:00
Matthias Mailänder
dd9ab16401 Run unit tests on Linux. 2023-06-01 12:43:10 +02:00
abcdefg30
12e6932930 Fix dedicated servers crashing on startup due to missing translations 2023-06-01 10:04:06 +03:00
abcdefg30
95f18d4bc3 Fix the ExplicitSequenceFilenames updating sequences twice 2023-05-30 16:31:49 +03:00
abcdefg30
8c9cc93185 Fix the ExplicitSequenceFilenames rule breaking when updating single maps 2023-05-30 16:31:49 +03:00
Matthias Mailänder
b58a8aaa0f Fix benchmark CSV export on non-US systems. 2023-05-28 23:11:25 +03:00
Gustas
c5b7728ac9 RA Naval balance 2023-05-25 23:35:34 +02:00
Matthias Mailänder
c9dddc342c Extract editor brush texts. 2023-05-23 19:45:15 +02:00
Matthias Mailänder
8433bc0948 Throw early when Lua function parameters are null. 2023-05-23 17:30:03 +03:00
abcdefg30
52a916012f Remove references to the deleted Lua directory from Windows packaging 2023-05-21 18:23:04 +03:00
RoosterDragon
2fe7e1bff9 Fix HierarchicalPathFinder pathing from inaccessible source locations.
When a search is initiated from an inaccessible source location, a path is still allowed if there is an adjacent, accessible location the unit can move into. The local pathfinder and HierarchicalPathFinder already account for this logic, but HPF has some bugs.

Firstly, when the HierarchicalPathFinder heuristic is being evaluated, it assumes all cells being explored are accessible - this is important for performance as it avoids constantly rechecking the accessibility of cells. Although this fact holds true for cells explored by the path search, it does not hold true for cells being added as the initial starting points of the search.

Secondly, when checking for adjacent locations to an inaccessible source cell, we checked only if these were still on the map. This is insufficient - we need to know if movement between the source cell and the adjacent cell is possible.

The fixes resolve this by:
- Teaching the heuristic an extra parameter to know if the location is known to be accessible. This allow an accessibility check to be performed for starting locations which stops HPF mistakenly assuming the abstract node for that location is the one we need to consider, and to correctly check the adjacent locations and their abstract nodes instead. The parameter means will can still skip the accessibility check in the typical case where the path search is being expanded, preserving performance.
- When adjacent cells are considered we now check if movement to them is possible. This stops HPF from allowing jumps over height discontinuities (i.e. no magically jumping up or down cliffs) and thinking a path is possible when it is not.
2023-05-21 16:56:16 +02:00
abcdefg30
0788e5ff3e Adjust the ValidTargets of DuplicateUnitCrateAction in RA 2023-05-21 16:34:58 +02:00
Matthias Mailänder
0fb5853b7a Fallback to normal difficulty when none is set. 2023-05-21 15:46:20 +03:00
Matthias Mailänder
b30285e38d Officially deprecate this function. 2023-05-21 15:46:20 +03:00
Brenton Horne
02e4bfba95 Remove reference to deleted lua directory
Lua directory was deleted a few commits ago and this just updates packaging/functions.sh to reflect this change.
2023-05-21 13:13:13 +02:00
abcdefg30
b5f5d5f9d5 Fix ScriptContext crashing without a WorldLoaded function 2023-05-21 13:11:17 +02:00
abcdefg30
f2b3a9f837 Fix ScriptContext crashing without a Tick function 2023-05-21 13:11:17 +02:00
dnqbob
6af14c16c9 Fix a rare crash when actor in IBotRespondToAttack is dead 2023-05-21 10:40:21 +02:00
Gustas
211f7160dc Remove \r
we use \n everywhere else in the engine
2023-05-20 20:47:56 +03:00
Smittytron
8a1463a471 Add Allies10b 2023-05-20 20:36:54 +03:00
abcdefg30
b623214e04 Fix IdlingUnits definitions for multiple reinforcements in D2k missions 2023-05-20 18:56:58 +02:00
abcdefg30
6b536ca88a Add a Lua function to concat two tables 2023-05-20 18:56:58 +02:00
Matthias Mailänder
445b736885 Replace sandbox wrapper scripts. 2023-05-20 13:19:48 +02:00
dnqbob
e8dd85419f add Autocrusher for baby visc 2023-05-20 12:53:29 +02:00
yamismo
b1fd392486 Add GDI Covert Operations - Twist of Fate - scg41ea 2023-05-19 18:55:17 +02:00
Gustas
e487c3366d Allow EMP cannon to EMP itself 2023-05-19 17:29:17 +02:00
Gustas
ade27ad8b9 Fail CI on lint warnings 2023-05-19 17:25:03 +02:00
Gustas
34bcae9abb Translation keys should not be required 2023-05-19 17:25:03 +02:00
Gustas
8894fdeaf9 Add map translation parse error linting 2023-05-19 17:25:03 +02:00
Gustas
ff14b75e1b Fix duplicate translations 2023-05-19 17:25:03 +02:00
Gustas
b5ef9c29cf Improve spawnpoint lint 2023-05-19 17:25:03 +02:00
Gustas
f344ccb714 Improve visibility lint 2023-05-19 17:25:03 +02:00
Gustas
9b71317280 Improve lint error wording 2023-05-19 17:25:03 +02:00
Gustas
1ac6912c2a Fix lint error formatting 2023-05-19 17:25:03 +02:00
Gustas
3188532e59 Add punctuation to lint comments 2023-05-19 17:25:03 +02:00
abcdefg30
d9d8c23c63 Enable the restart button when we encounter a script error 2023-05-19 17:02:06 +02:00
abcdefg30
dfe0c15399 Remove wrong definitions from TD's notifications.yaml 2023-05-19 16:39:42 +02:00
IceReaper
0751b30d33 Fixed duplicate asset selection in asset browser. 2023-05-19 10:26:32 +03:00
Gustas
f4dc29f9db Add missing balance changes 2023-05-18 18:30:53 -05:00
abcdefg30
ce7f9e71c1 DefaultSpriteSequence: Avoid an extra allocation and LINQ when adding shadow frames 2023-05-16 22:57:33 +03:00
abcdefg30
05f21fcbe2 DefaultSpriteSequence: Merge if statements for readability 2023-05-16 22:57:33 +03:00
abcdefg30
00f2ba1a53 DefaultSpriteSequence: Remove redundant modulo operations
frame is already bounded by length.Value
2023-05-16 22:57:33 +03:00
abcdefg30
991e0a4c9a DefaultSpriteSequence: Use .Count == 0 over !.Any() 2023-05-16 22:57:33 +03:00
abcdefg30
aa28881726 DefaultSpriteSequence: Use List.Find instead of LINQ's FirstOrDefault 2023-05-16 22:57:33 +03:00
RoosterDragon
6d288aba2f Track keys during MiniYaml Merge.
This improves performance by avoiding repeated linear scans over the nodes to match the keys.
2023-05-15 23:48:39 +02:00
dnqbob
0980856072 Set up Hover MRLS moving style 2023-05-11 17:27:18 +02:00
dnqbob
0d98405bdc Make ActorFacingModifier privately set 2023-05-11 17:27:18 +02:00
dnqbob
69441a4fee Add TurnsWhileMoving to Mobile 2023-05-11 17:27:18 +02:00
dnqbob
a65bb17d68 Cache the notifyAttacks in AttackGarrisoned 2023-05-09 22:49:41 +02:00
Matthias Mailänder
6e6bf1ca81 Translate labels with parameters. 2023-05-09 20:14:52 +03:00
Matthias Mailänder
474463111f Remove unused CamelCase UI string. 2023-05-09 20:14:52 +03:00
RoosterDragon
0b4a54ab54 TechTree.GatherOwnedPrerequisites performance improvements.
- Consuming methods cared only about the count and not the actual actors, so only counts the actors rather that creating lists.
- ProvidesPrerequisites implementations return cached objects rather then allocating new enumerables on each call.
2023-05-06 20:07:35 +03:00
Matthias Mailänder
65c0cf1065 Deprecate string format shorthand. 2023-05-05 19:03:09 +02:00
Matthias Mailänder
1c2ce0dcc0 Deprecate string format log shorthand. 2023-05-05 19:03:09 +02:00
Matthias Mailänder
e251126dd4 Remove unused log proxy. 2023-05-05 19:03:09 +02:00
Matthias Mailänder
f2a4e7b984 Remove null sprite workaround from Tiberian Sun. 2023-05-04 22:13:59 +03:00
Matthias Mailänder
9e659cacf2 Fix rally point sprites not being truely optional. 2023-05-04 22:13:59 +03:00
Matthias Mailänder
c5e9567875 Fix a null reference exception upon empty Fluent strings. 2023-05-02 22:25:51 +02:00
Gustas
44f1af7059 Move TileScale to MapGrid 2023-05-02 16:37:30 +03:00
Nate Nichols
8f511a3bb6 Added ability to use Mouse 4 and Mouse 5 as hotkeys 2023-04-28 15:22:25 +03:00
Matthias Mailänder
4f7a01a291 Localize difficulty settings. 2023-04-25 21:33:02 +03:00
Matthias Mailänder
af6330b1bd Allow localisation of dictionary values. 2023-04-25 21:33:02 +03:00
Matthias Mailänder
55ff0ac1f4 Inline variables. 2023-04-25 21:33:02 +03:00
abcdefg30
51bbfc39b0 Fix DischargeableSupportPowerInstance crashing without instances 2023-04-25 21:15:06 +03:00
JovialFeline
90bb2db349 Fix OnAllKilled crash for allies05b and allies05c 2023-04-23 23:49:10 +02:00
Matthias Mailänder
0d36bc19c6 Document radar appear trait and fields. 2023-04-22 23:24:24 +02:00
Gustas
a9a7777293 Report linguini parse errors
Reports duplicate keys
2023-04-22 19:23:41 +02:00
Gustas
bf66068557 Add per map linting 2023-04-22 19:23:41 +02:00
Matthias Mailänder
68eec52cef Add TranslationProvider 2023-04-22 19:23:41 +02:00
Gustas
a065e6a47c Fix map level lobby options not being translated 2023-04-22 19:23:41 +02:00
Gustas
efe135e38b Add Translations to MapPreview 2023-04-22 19:23:41 +02:00
Gustas
07e47b6a28 Extract common rules translations 2023-04-22 19:23:41 +02:00
Gustas
8f5d8de1c2 Allow empty translation keys 2023-04-22 19:23:41 +02:00
Matthias Mailänder
2867334c00 Less verbose logging for untranslated strings. 2023-04-22 19:23:41 +02:00
Gustas
dc390a7301 Add IMove.MoveOntoTarget interface
In `TraitsInterfaces` we expose offset as WPos instead of CPos. In an upcoming PR we'll translate the same change to yaml.
2023-04-21 18:29:43 +02:00
Gustas
ad683d9226 Add MoveOnto Activity
No functional changes to `MoveWithinRange` nor `MoveAdjacentTo`. I've just
moved around code to for allow better overwriting.
2023-04-21 18:29:43 +02:00
Gustas
1c2eaa2654 Use nameof for ContrailEndColor description 2023-04-21 17:58:42 +02:00
Gustas
7ef1dccdcf Add ContrailEndWidth 2023-04-21 17:58:42 +02:00
Gustas
ff488b77b5 Implement elite radar invisibility 2023-04-21 16:59:11 +02:00
Gustas
92cceea2b8 Enemy veterancy should always be visible 2023-04-21 16:59:11 +02:00
yamismo
8b522680e3 Added Nod Covert Operations - Eviction Notice - scb30ea 2023-04-19 21:52:30 -05:00
RoosterDragon
7507333cd3 Fix missing Flags attribute on CABFlags enum. 2023-04-17 00:05:12 +02:00
RoosterDragon
f470f9ab91 Fix CA2216 2023-04-17 00:05:12 +02:00
RoosterDragon
a120b9d37e Fix CA2208 2023-04-17 00:05:12 +02:00
RoosterDragon
1b1b9dc29b Fix CA2215 2023-04-17 00:05:12 +02:00
RoosterDragon
ef04e2e1e8 Fix CA2019 2023-04-17 00:05:12 +02:00
RoosterDragon
321d4b8afd Fix CA1850 2023-04-17 00:05:12 +02:00
RoosterDragon
c3e6c4685f Fix CA1849 2023-04-17 00:05:12 +02:00
RoosterDragon
6362bbd176 Fix CA1846 2023-04-17 00:05:12 +02:00
RoosterDragon
07fb5e8027 Fix CA1841 2023-04-17 00:05:12 +02:00
RoosterDragon
8fe82ed976 Fix CA1839 2023-04-17 00:05:12 +02:00
RoosterDragon
25b8e7fefc Fix CA1834 2023-04-17 00:05:12 +02:00
RoosterDragon
ad4a443fc2 Fix CA1066 2023-04-17 00:05:12 +02:00
RoosterDragon
01eaa6b228 Fix CA1064 2023-04-17 00:05:12 +02:00
RoosterDragon
c442bd83f8 Fix CA1036 2023-04-17 00:05:12 +02:00
RoosterDragon
ff799303b0 Fix CA1018 2023-04-17 00:05:12 +02:00
RoosterDragon
f09241d263 Fix CA1010 2023-04-17 00:05:12 +02:00
Gustas
1db982276a Fix actors being added to world while world actors are being iterated 2023-04-15 17:26:02 +02:00
Paul Chote
718c6d03cc Downgrade SDL to 2.0.22. 2023-04-15 16:14:12 +02:00
Thomas Christlieb
57fba4e18e Rename JoinChatDelay 2023-04-15 12:50:06 +02:00
ThomasChr
b219731173 Allow overriding of Server.Map via the dedicated server scripts 2023-04-15 12:50:06 +02:00
ThomasChr
a0eea7bcc0 fix path to wiki page 2023-04-15 12:50:06 +02:00
RoosterDragon
0c3071b9c6 Adjust MiniYaml node merging when removals are present.
# Example Scenario
MockString:
	CollectionOfStrings:
		StringA: A

MockString:
	CollectionOfStrings:
		StringB: B

MockString:
	-CollectionOfStrings:

MockString:
	CollectionOfStrings:
		StringC: C

MockString:
	CollectionOfStrings:
		StringD: D

# Previous MergePartial result
# The CollectionOfStrings is merged into a single unit, so the C and D items are dragged upwards and jump ahead of the Removal
# When this is processed, the final result removes CollectionOfStrings entirely

MockString:
	CollectionOfStrings:
		StringA: A
		StringB: B
		StringC: C
		StringD: D
	-CollectionOfStrings:

# New MergePartial result
# When merging nodes, we no longer allow merges to jump an intervening removal node.
# This means we can have multiple of a certain key (CollectionOfStrings in this example) which was not the case previously.
# When this is processed, the final result includes C/D but not A/B.

MockString:
	CollectionOfStrings:
		StringA: A
		StringB: B
	-CollectionOfStrings:
	CollectionOfStrings:
		StringC: C
		StringD: D
2023-04-14 23:57:26 +03:00
RoosterDragon
0066010792 Added MiniYAML merging unit tests
MockString:
	CollectionOfStrings:
		StringA: A

MockString:
	CollectionOfStrings:
		StringB: B

MockString:
	-CollectionOfStrings:

MockString:
	CollectionOfStrings:
		StringC: C

MockString:
	CollectionOfStrings:
		StringD: D

MockString:
	CollectionOfStrings:
		StringA: A
		StringB: B
		StringC: C
		StringD: D
	-CollectionOfStrings:

MockString:
	CollectionOfStrings:
		StringA: A
		StringB: B
	-CollectionOfStrings:
	CollectionOfStrings:
		StringC: C
		StringD: D
2023-04-14 23:57:26 +03:00
penev92
0b61954e39 Reimported some TS maps
Could not find the source for most of the maps, so reimported only those that were part of the original game's "official map pool" + 3 custom ones.
2023-04-14 20:50:08 +03:00
Paul Chote
a0cd008da6 Fixed gen2 map importer Bounds calculations 2023-04-14 20:50:08 +03:00
penev92
3d2ba9d5bf Added abstract base class ImportGen2MapCommand
Split out ImportGen2MapCommand from ImportTSMapCommand, to also serve as a base for a RA2 map importer.
Also renamed TS importer to ImportTiberianSunMapCommand.
2023-04-14 20:50:08 +03:00
penev92
01e6babd54 Moved ImportLegacyMapCommand to OpenRA.Mods.Cnc
Also renamed to `ImportGen1MapCommand`.
Also moved Extensions.DistinctBy().
2023-04-14 20:50:08 +03:00
Ivaylo Draganov
96d023de87 Don't change cursor when mousing over label widgets
Labels don't handle input so the cursor should not change over them.
2023-04-12 12:09:50 +03:00
Gustas
3ca2bb1d23 Fix IDE0074, SA1316 and followup CS8141 2023-04-09 10:26:04 +01:00
RoosterDragon
595717fff0 Enable Code Quality Rules
Enforces a variety of CAxxxx rules that do not have existing violations.

For the benefit of dotnet_code_quality.CA2241.try_determine_additional_string_formatting_methods_automatically = true, rename parameters of methods that forward to string.Format so format issues will get detected automatically.
2023-04-08 23:15:40 +02:00
Gustas
d838d08570 Add IColorPickerManagerInfo interface 2023-04-08 18:05:20 +03:00
darkademic
265f915442 Resurrected old colour picker. 2023-04-08 18:05:20 +03:00
RoosterDragon
dcac966d49 Don't enable IDE0063, IDE0078. 2023-04-08 16:51:51 +03:00
RoosterDragon
4110c199fb Enable IDE0150, CA1845 2023-04-08 16:51:51 +03:00
RoosterDragon
14c0d011ea Fix SA1414 2023-04-08 16:51:51 +03:00
RoosterDragon
a167f9680f Fix SA1316 2023-04-08 16:51:51 +03:00
RoosterDragon
062dc2bd40 Fix SA1141 2023-04-08 16:51:51 +03:00
RoosterDragon
1ce9acd442 Fix IDE0110 2023-04-08 16:51:51 +03:00
RoosterDragon
8a285f9b19 Fix IDE0090 2023-04-08 16:51:51 +03:00
RoosterDragon
164abfdae1 Fix IDE0083 2023-04-08 16:51:51 +03:00
RoosterDragon
bd2b3d9793 Fix IDE0074 2023-04-08 16:51:51 +03:00
RoosterDragon
cbd0583289 Fix IDE0062 2023-04-08 16:51:51 +03:00
RoosterDragon
023d80b94d Fix IDE0057 2023-04-08 16:51:51 +03:00
RoosterDragon
5254348819 Fix IDE0056 2023-04-08 16:51:51 +03:00
RoosterDragon
4ec5a4b34a Fix reversed path searches from inaccessible locations.
The Harvester trait and MoveAdjacentTo activity called the pathfinder but had a single source and multiple targets. The pathfinder interface only allows for the opposite: multiple sources and a single target. To work around this they would swap the inputs. This works in most cases but not all cases. One aspect of asymmetry is that an actor may move out of an inaccessible source cell, but not onto an inaccessible target cell.

Searches that involved an inaccessible source cell and that applied this swapping method would therefore fail to return a path, when a valid path was possible. Although a rare case, once good way to reproduce is to use a production building that spawns actors on inaccessible cells around it, such as the RA naval yard. A move order uses the pathfinder correctly and the unit will move out. Using a force attack causes the unit to use the broken "swapped" mechanism in MoveAdjacentTo and it will be stuck.

This asymmetry has been longstanding but the pathfinding infrastructure only sporadically accounted for it. It is now documented and applied consistently. Create a new overload on the pathfinder trait that allows a single source and multiple targets, so callers have an overload that does what they need and won't be tempted to swap the positions and run into this issue.

Internally, this requires us to teach Locomotor to ignore the self actor when performing movement cost checks for these "in reverse" searches so the unit doesn't consider the cell blocked by itself.
2023-04-07 16:38:37 +01:00
Gustas
e4ba9733fe Add sequences linting to ingame lobby 2023-04-07 16:23:30 +01:00
IceReaper
a332fba702 IDFiles should be optional. 2023-04-05 21:13:11 +02:00
RoosterDragon
83561d639d Update LangVersion to C# 9.
mono was the bottleneck restricting our ability to use a newer C# version. mono 6.12 is currently available. Although poorly documented on their website, this supports C# 9. https://www.mono-project.com/docs/about-mono/versioning/#mono-source-versioning indicates mono 6.12 uses Roslyn 3.9.0. https://github.com/dotnet/roslyn/blob/main/docs/wiki/NuGet-packages.md#versioning indicates Roslyn 3.9.0 supports C# 9.

This unlocks C# 8 and C# 9 features previously unavailable to us.
- https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-80
- https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-9

A newer version of StyleCop is required to avoid rules tripping up on the new syntax. Currently only prerelease versions are available but their use is encouraged https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3420#issuecomment-994899135

Fix style rule violations on existing rules where the newer language version makes some existing casts redundant or allows use of the null coalescing assignment operator.
2023-04-05 15:27:41 +03:00
RoosterDragon
9dd4f938da Cache reflection calls when running utility lints and commands.
Reduces runtime of --check-yaml command to 70% of original.
2023-04-05 00:25:32 +02:00
Gustas
1a2aafa17c Fix backwards movement not canceling when BackwardDuration times out 2023-04-04 20:25:04 +02:00
Gustas
a4e80e1153 Fix backwards movement not working correctly when turning in an arc 2023-04-04 20:25:04 +02:00
dnqbob
a261abbc3f Using support power uncloak TS superweapon 2023-04-04 18:26:11 +03:00
dnqbob
27f780c5d1 Using support power can uncloak actor 2023-04-04 18:26:11 +03:00
IceReaper
11ab4a7935 Support dos paths in registry. 2023-04-04 16:20:05 +02:00
IceReaper
4dd532f60e Implemented MP3 detection. 2023-04-03 20:05:52 +02:00
IceReaper
138715b509 Added Zip support to installer source actions. 2023-04-03 17:57:16 +02:00
IceReaper
d97df78f4f Steam and Gog resolvers now take IDFiles into account. 2023-04-03 17:57:16 +02:00
IceReaper
56c0680685 Fixed a typo. 2023-04-03 17:57:16 +02:00
RoosterDragon
95f675becd Improve performance of ActorInfo.TraitsInConstructOrder.
Avoid redundant enumerations of the more and unresolved enumerables. This gives an approx 3x speedup for this function.
2023-04-03 17:29:08 +03:00
ThomasChr
a5ef6801c9 fix two crashes in hotkey menu, trying to select not visible widgets 2023-03-31 16:52:54 +03:00
abcdefg30
75a47aabb5 Don't always display the overwrite dialogue
When a save was selected but the filename text was changed,
we don't overwrite the existing save, so we don't need a dialogue
2023-03-31 16:32:09 +03:00
abcdefg30
73f29005bd Allow saving games by pressing enter 2023-03-31 16:32:09 +03:00
abcdefg30
f1d439a07f Make the save game title field take focus 2023-03-31 16:32:09 +03:00
penev92
9ce4ef0bff Updated ExtractEmmyLuaAPI command to fix issues
Issues:
 - The generated API file caused a ton of diagnostics warnings.
 - Perhaps due to EmmyLua moving forward since this was created, we were missing out on some features.

Changes:
 - Disabled diagnostics about missing return values for functions.
 - Added add operator definitions because by default CPos+CVec would be assumed to return a number and assignments would cause warnings about type mismatches.
 - Added explicit @enum annotations. This also fixed warnings in the generated API file about missing types.
 - Changed default type of properties from an empty table to nil. This fixed warnings in the generated API file about type mismatch.
 - Renamed local variable "required" -> "requiredTraits" for readability.
 - Disabled duplicate function/property  name warnings
 - Updated trait docs URL.
2023-03-31 11:46:28 +02:00
Thomas Christlieb
9e081763ad fix stance icon not updating when selecting units 2023-03-28 22:09:07 +03:00
Matthias Mailänder
378f66a1ff Resolve Lua language server diagnosed problems. 2023-03-28 11:45:15 +02:00
Matthias Mailänder
3d0efa1cbe Add EmmyLua notation to resolve the type. 2023-03-28 11:45:15 +02:00
Matthias Mailänder
cae07bb408 Remove uninitialized variable. 2023-03-28 11:45:15 +02:00
Matthias Mailänder
c94c1f8a6c Remove missing actors. 2023-03-28 11:45:15 +02:00
Matthias Mailänder
0847cd33bd Fix typos in mission scripts. 2023-03-28 11:45:15 +02:00
RoosterDragon
dc40442118 Fix doc comment on CanKickClient. 2023-03-27 20:55:44 +02:00
Gustas
2959a2c137 Fix warnings in LobbyCommand.cs 2023-03-27 20:15:17 +02:00
Gustas
bf00577d33 Allow kicking dead players 2023-03-27 20:15:17 +02:00
Gustas
c4bd9fb7aa Add quit button to connection lost panel 2023-03-27 20:02:54 +02:00
Gustas
482f2fc335 Fix inconsistent abort naming 2023-03-27 20:02:54 +02:00
Gustas
925e042455 Add Interactable lint test
Since # 19174 bounds are calculated from WDist instead of pixels. This causes many of the older maps to crash games, And this lint test makes it easier for map makers to detect these issues.

As calculating the bounds requires MapGrid, we need to write a separate lint test for Interactable instead of defaulting to RulesetLoaded checks.
2023-03-27 19:55:19 +02:00
Gustas
c525c48d25 Make sure submarines don't smoke underwater 2023-03-26 20:24:30 -05:00
Gustas
f90dba2c6b Add ^Submarine and make submarines inherit it 2023-03-26 20:24:30 -05:00
Gustas
e28f45f785 Use interpolated facings for submarines 2023-03-26 20:24:30 -05:00
Mustafa Alperen Seki
87eca8f2c0 Fix colors of paratroopers in the missions. 2023-03-26 17:49:02 -05:00
Mustafa Alperen Seki
960f829221 Move Soviets palette index definition to campaign-palettes.yaml. 2023-03-26 17:49:02 -05:00
RoosterDragon
8a4303cc94 Rework PriorityQueue for performance.
- Providing the comparer as a type argument that is a struct allows the calls to be devirtualised, leading to approx a 3x performance improvement.
- Use a single backing array, rather than a list of arrays.
2023-03-25 18:50:09 +01:00
penev92
7a4ac01348 Changed SequenceReferenceAttributes to use nameof 2023-03-24 18:51:36 +01:00
penev92
6dda4fa9f7 Fixed BuildableTerrainOverlay ignoring scale 2023-03-24 18:51:36 +01:00
Matthias Mailänder
af2b32e7ba Add particle smoke effects. 2023-03-23 12:15:16 +02:00
michaeldgg2
069b7c5500 RepairableBuilding: play/display notification when repair process is aborted. 2023-03-23 10:28:12 +01:00
michaeldgg2
ae1983faba ProductionQueue: make PauseProduction, CancelProduction virtual, CancelProductionInner protected 2023-03-20 12:17:55 +02:00
RoosterDragon
bcfa0c9ae9 Review StyleCop rules.
- Enforce SA1604 ElementDocumentationShouldHaveSummary.
- Enforce SA1629 DocumentationTextShouldEndWithAPeriod.
- Turn off some rules covered by IDExxxx rules.
- Remaining rules are treated as part of OpenRA style.
2023-03-18 12:46:10 +02:00
RoosterDragon
88ba974ea5 Reformat editorconfig
- Group style rules with their associated options in a way that matches the documentation. This makes it easier to pair rules and their options.
- Remove OpenRA.ruleset and move all rules into .editorconfig file.
- Centralise IDE0005 workaround in Directory.Build.props file.
2023-03-17 22:31:10 +01:00
RoosterDragon
721c03d9af Enforce additional style rules. 2023-03-17 22:31:10 +01:00
Gustas
384435f8eb Remove annoying autocomplete suffix 2023-03-16 21:07:21 +01:00
penev92
e808549637 Removed a redundant cast - IDE0004 2023-03-14 17:43:32 +01:00
penev92
bde13a8572 Fixed a bad merge 2023-03-14 17:43:32 +01:00
RoosterDragon
98c4eaca83 Fix IDE0032 2023-03-14 13:41:25 +02:00
michaeldgg2
e64c0a35c5 ProductionPaletteWidget: offset and align for queued count is now customizable 2023-03-12 15:10:26 +01:00
penev92
6bedc4697b Fix modcontent.chrome-3x.png
After it was changed to a version without padding.
2023-03-12 13:24:39 +02:00
Gustas
2dbefaf375 Cache font 2023-03-12 10:53:45 +01:00
Gustas
2050d55b21 Add player's name to super weapon timers 2023-03-12 10:53:45 +01:00
penev92
d61178de41 Added tooltips to ContentPackages 2023-03-11 21:43:18 +01:00
penev92
9cb2d19654 Added checkbox tick artwork to modcontent's chrome 2023-03-11 21:43:18 +01:00
penev92
8a59982420 Made Advanced installation offer optional packages
Previously all detected content would be installed. Now the user can choose whether they want to install the optional packages like music and videos.
2023-03-11 21:43:18 +01:00
penev92
c5aee7b2cf Added Title and Identifier as fields of ModPackage 2023-03-11 21:43:18 +01:00
penev92
d0285b058b Grouped installer SourceActions by ContentPackage
ContentPackages are defined in mod.yaml and list Installers that support them, but then the Installers and their SourceActions knew nothing about ContentPackages.
Also added BeforeInstall and AfterInstall sections for SourceActions in the Installers.
2023-03-11 21:43:18 +01:00
abcdefg30
64e84554d3 Fix empty mission objectives getting translated 2023-03-11 21:07:02 +01:00
Gustas
8a18c2e4b6 Add a CanDeploy check to Transform at FrameEndTask 2023-03-11 21:00:51 +01:00
abcdefg30
43bb9e4302 Fix the map.yaml formatting of some new D2k maps 2023-03-11 20:56:32 +01:00
abcdefg30
cb8921dc22 Fix the sequence filename update rule node placement 2023-03-11 20:56:32 +01:00
abcdefg30
64933ed04b Fix sequence inheritance defined in yaml
Nodes need to be placed after "Inherits" nodes so that they take
precedence over the inherited nodes.
2023-03-11 20:56:32 +01:00
abcdefg30
040fbf9694 Make Wanders and AttackWander queue activities instead of resolving orders 2023-03-11 20:47:34 +01:00
Wylli
4500d964b3 Update AUTHORS
Update AUTHORS
2023-03-10 20:35:56 +01:00
IceReaper
fceab4f388 Allow mods to override production widgets text colors. 2023-03-10 20:31:15 +01:00
Guillermoqnk
b3d468aca1 Fix return fire logic ignoring AutoAttack priotities
Added brand new feature

Unnecessary assignmet removed
2023-03-10 20:18:10 +02:00
Paul Chote
3b0b15abb9 Reduce duplication and allocations around CalculateFrameIndices. 2023-03-10 20:11:33 +02:00
Paul Chote
f0cf728825 Dispose SequenceSet when we're done with it.
Utility rules that do something on a map and exit
are left without explicit disposing, as they will
be cleaned up immediately anyway.
2023-03-10 20:11:33 +02:00
Paul Chote
c35ab081ff Rewrite sequence loading logic.
Multiple layers of Lazy<T>ness are replaced with
an explicit two-part loading scheme.

Sequences are parsed immediately, without the need
for the sprite assets, and tell the SpriteCache
which frames they need. Use-cases that want the
actual sprites can then tell the SpriteCache to
load the frames and the sequences to resolve the
sprites.
2023-03-10 20:11:33 +02:00
Paul Chote
1f3403717b Fix negative sequence lengths. 2023-03-10 20:11:33 +02:00
Paul Chote
a6f3db0a45 Allow all sprites to use interpolated facings. 2023-03-10 20:11:33 +02:00
Paul Chote
b051211842 Remove internal state from ISpriteSequence API. 2023-03-10 20:11:33 +02:00
Paul Chote
279869b4c5 Fix --dump-sequence-sheets utility command.
Also formats output filenames to specify
indexed sprite channels.
2023-03-10 20:11:33 +02:00
Paul Chote
b69adb518a Remove asset browser dependency on sequences. 2023-03-10 20:11:33 +02:00
Paul Chote
b7cdcf419f Add ILintSequencesPass. 2023-03-10 20:11:33 +02:00
Paul Chote
7cd4272350 Access sequences from Map. 2023-03-10 20:11:33 +02:00
Paul Chote
992ba1a9a2 Remove HasEmbeddedPalette from sequences. 2023-03-10 20:11:33 +02:00
Gustas
dab3ca0025 Add support for dark player colors 2023-03-10 15:43:24 +02:00
RoosterDragon
939f715e3c Fix IDE0053 2023-03-07 13:18:13 +02:00
Matthias Mailänder
37afd6094e Rename GiveMcvCrateAction to GiveBaseBuilderCrateAction. 2023-03-07 12:53:31 +02:00
RoosterDragon
c916a00624 Remove workaround for old versions of mono. 2023-03-07 12:51:06 +02:00
Matthias Mailänder
f6e5bee334 Move JumpJet traits to the C&C library. 2023-03-07 12:24:05 +02:00
Matthias Mailänder
63b9f18d05 Also document the launch parameters. 2023-03-05 12:15:32 +02:00
Matthias Mailänder
7b9dafcd19 Code cleanup. 2023-03-05 12:15:32 +02:00
RoosterDragon
53e9f44972 Spelling fixes 2023-03-02 20:11:54 +02:00
RoosterDragon
52fd564eac Fix some whitespace formatting issues: stray tabs or spaces.
Wrap some long lines on affected code.
2023-03-02 20:02:45 +02:00
Matthias Mailänder
edaf11cb89 Update thirdparty dependencies. 2023-03-01 23:52:59 +01:00
RoosterDragon
ad122c8e32 When running a server, don't load minimap previews into memory. 2023-03-02 00:13:46 +02:00
RoosterDragon
8ee6957e6a Fix IDE0048 2023-03-01 21:56:28 +02:00
abcdefg30
65e28d5562 Retire the release-20200202 update path 2023-03-01 20:02:01 +01:00
Gustas
422a228cea Fix PlayerColorRemap expecting colors in linear space
PlayerColorRemap expected colors in linear space yet we provided them in gamma. We fix this by instead expecting gamma space colors and then converting them into linear space ourselves.
2023-02-28 23:26:37 +02:00
RoosterDragon
0b01b73111 Fix IDE0060 2023-02-28 21:21:40 +02:00
RoosterDragon
555aac3f64 Fix IDE0042 2023-02-28 21:21:40 +02:00
RoosterDragon
5b70d344cc Fix IDE0038 2023-02-28 21:21:40 +02:00
RoosterDragon
71ce515d6d Fix IDE0004 2023-02-28 21:21:40 +02:00
Eonfge
a4f9ceaf09 Add PrefersNonDefaultGPU flag to Desktop Entry
This means that the application will automatically start with the dedicated GPU of laptops, instead of the iGPU. Based on the Linux's FreeDesktop spec: https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s06.html
2023-02-27 10:42:07 +01:00
RoosterDragon
d4135d608e Fix IDE0039 2023-02-27 10:09:11 +01:00
Gustas
4b3f7034b2 Silence rule "Use span-based 'string.Concat'" 2023-02-27 08:36:47 +02:00
Gustas
e64c77fdde Use pattern matching to avoid is check followed by a cast (with variable) 2023-02-27 08:36:47 +02:00
Gustas
8d0fe52dd8 Remove unnecessary parentheses 2023-02-27 08:36:47 +02:00
Gustas
157d1b32dc Use null propagation 2023-02-27 08:36:47 +02:00
Gustas
b06cbd7a95 Add missing punctuation to editorconfig 2023-02-27 08:36:47 +02:00
Gustas
2cccae96fe Change click InterruptType to Overlap 2023-02-25 14:46:53 +02:00
Orb370
e758678140 update TD map pool 2023 2023-02-24 22:01:15 +01:00
RoosterDragon
6c96405ab2 Fix CS1573 2023-02-24 22:00:25 +02:00
RoosterDragon
63aa34cb35 Fix CS1570 2023-02-24 22:00:25 +02:00
RoosterDragon
5a2a448c32 Fix IDE0250 2023-02-24 22:00:25 +02:00
RoosterDragon
5e52d067c8 Fix IDE0180 2023-02-24 22:00:25 +02:00
RoosterDragon
bf960b6eae Fix IDE0120 2023-02-24 22:00:25 +02:00
RoosterDragon
ede5412526 Fix IDE0100 2023-02-24 22:00:25 +02:00
RoosterDragon
78c41b84a1 Fix IDE0082 2023-02-24 22:00:25 +02:00
RoosterDragon
99c289e063 Fix IDE0075 2023-02-24 22:00:25 +02:00
RoosterDragon
3402031399 Fix IDE0071 2023-02-24 22:00:25 +02:00
RoosterDragon
837c70f857 Fix IDE0061 2023-02-24 22:00:25 +02:00
RoosterDragon
4f6095c3d4 Fix IDE0054 2023-02-24 22:00:25 +02:00
RoosterDragon
2d4119e88d Fix IDE0051 2023-02-24 22:00:25 +02:00
RoosterDragon
4991f2f892 Fix IDE0041 2023-02-24 22:00:25 +02:00
RoosterDragon
10def52ad9 Fix IDE0033 2023-02-24 22:00:25 +02:00
RoosterDragon
99c1a4448b Fix IDE0030 2023-02-24 22:00:25 +02:00
RoosterDragon
8223161959 Fix IDE0029 2023-02-24 22:00:25 +02:00
RoosterDragon
67ba3e55de Fix IDE0020 2023-02-24 22:00:25 +02:00
RoosterDragon
6d7c73d498 Fix IDE0019 2023-02-24 22:00:25 +02:00
RoosterDragon
80bb828fe5 Fix IDE0018 2023-02-24 22:00:25 +02:00
RoosterDragon
b0dca05e50 Fix IDE0002 2023-02-24 22:00:25 +02:00
RoosterDragon
8b4500146f Fix IDE0001 2023-02-24 22:00:25 +02:00
Matthias Mailänder
ee35cbc0d0 Let bot Deviators prioritize vehicles. 2023-02-22 23:09:08 +01:00
Matthias Mailänder
0b6f335c9f Reference trait name in description. 2023-02-22 23:09:08 +01:00
Matthias Mailänder
40e8061797 Removed unused water terrain type from Dune 2000. 2023-02-22 23:09:08 +01:00
penev92
bb8e6ab03c Fix sprites with no frames crashing AssetBrowser 2023-02-22 11:25:17 +02:00
penev92
807bd4d06a Make AssetBrowser not case sensitive to file types 2023-02-22 11:25:17 +02:00
penev92
e1a7fb1cc8 Fixed displayed audio length in the AssetBrowser 2023-02-20 13:07:32 +01:00
Matthias Mailänder
5032b2b872 Extract translation strings. 2023-02-19 23:46:41 +01:00
abcdefg30
640e9d68b7 Fix not all ammo pools getting rearmed during RearmTick 2023-02-19 15:11:05 +02:00
Gustas
049d0283f9 Remove TiberianSunRefinery
Also add IDockClientBody interface,
move WithDockingOverlay cnc -> common,
remove HarvesterDockSequence implementing classes
2023-02-18 16:35:15 +01:00
Andre Mohren
3f0c3a8b9c Allow mods to control how looped sounds are used. 2023-02-18 16:18:20 +01:00
Gustas
68fddf818c Polish encyclopedia descriptions 2023-02-18 16:18:01 +01:00
Gustas
6997c442c6 Add MacOS Utility executable 2023-02-18 17:14:22 +02:00
Gustas
78677fd8ab Fix replays continuing after desync 2023-02-18 15:53:17 +01:00
Gustas
20a16ad5f8 Move force pausing to EndGame method 2023-02-18 15:53:17 +01:00
penev92
19613ed833 Minor D2k encyclopedia UI polish
- Make the text area taller so all the current text fits without a scroll.
- Limit label width so text doesn't press against the scrollbar.
2023-02-18 15:39:19 +01:00
penev92
63bc73d9d2 Change the way the output directory is set during packaging 2023-02-18 15:18:35 +02:00
penev92
5c7b8955dc Added video player background for D2k 2023-02-18 14:10:24 +01:00
penev92
2c51e791ad Added VideoPlayerWidget.LoadAndPlayAsync method
Previous commits removed the async loading of videos, which can be a problem for videos played in the radar widget mid-game because it can cause a lag spike. This loads the video on a separate thread and runs it on the main thread whenever it is loaded, not blocking the main thread meanwhile and allowing the game to continue while the video loads.
Also add back cancelling of already playing video and add a check to not try to run onComplete if it is null.
2023-02-18 14:10:24 +01:00
penev92
c3fcbf77ed Rename some VideoPlayerWidget methods for clarity 2023-02-18 14:10:24 +01:00
penev92
6321432d97 Fix crash caused by VideoPlayerWidget.DrawOverlay
Somehow this would divide by 0 on the next line, not crashing but resulting in an integer overflow on `overlayHeight`, which would crash when trying to render.
2023-02-18 14:10:24 +01:00
penev92
4135079290 Unify MediaGlobal/Scripting.Media PlayFMV* methods
- Improve consistency
- Remove dead code
- Removed AsyncAction!
- Delegate.BeginInvoke is unsupported since .NET Core and throws an "Operation is not supported on this platform." exception.
2023-02-18 14:10:24 +01:00
penev92
287428b487 Did a slight refactor pass on MediaGlobal
- Unified onPlayComplete callback handling to avoid code duplication.
- Fixed code style issues.
2023-02-18 14:10:24 +01:00
Paul Chote
af23888b95 Remove dependency on VCRUNTIME140.dll. 2023-02-18 14:30:56 +02:00
abcdefg30
4a554431ff Remove redundant jumps and other style nits from the update rules 2023-02-18 00:41:07 +02:00
Paul Chote
947f53a991 Disable max order length check for local servers. 2023-02-14 10:15:21 +02:00
abcdefg30
835537fcc2 Adjust the encyclopedia descriptions of combat tanks 2023-02-14 10:09:11 +02:00
abcdefg30
08ba35763c Adjust the encyclopedia descriptions of light armored units 2023-02-14 10:09:11 +02:00
oldnaari
14ad4e168e Update DownloadPackageLogic.cs, to log missing files in package 2023-02-05 20:19:49 +02:00
penev92
a614375982 Fixed Makefile check of MacOS architecture 2023-02-05 00:19:05 +01:00
abcdefg30
e4bb13ea07 Throw an ArgumentException when trying to translate null keys 2023-02-03 08:31:28 +02:00
abcdefg30
84add8a03d Fix an NRE in ConnectionLogic 2023-02-03 08:31:28 +02:00
Gustas
f5aa2f153a Use Refinery reference in HarvesterDockSequence 2023-01-31 23:49:10 +01:00
Gustas
f56b0ea0d0 Moved HarvesterDockSequence notifications to the base class
And fix a minor oversight in VoxelHarvesterDockSequence.cs
2023-01-31 23:49:10 +01:00
Azarus
77b06ac9f7 Add an option to check if an actor can be captured via Lua 2023-01-30 13:46:29 +02:00
Gustas
3f2007cd2c Optimise TakeCover to have clear states 2023-01-28 20:18:48 +01:00
michaeldgg2
6f87c565ac AddFactionSuffixLogic: add suffix to ProductionTabsWidget.Decorations 2023-01-27 12:30:38 +02:00
michaeldgg2
949b993a4a ProductionTabsWidget: center left/right arrow image on left/right button background 2023-01-27 12:30:38 +02:00
michaeldgg2
ca7e7c2304 ProductionTabsWidget: allow specifying different panels for left/right scroll button and tab buttons. 2023-01-27 12:30:38 +02:00
michaeldgg2
38e52c062e ProductionTabsWidget: allow customizing left/right arrow buttons.
This moves initialization of Initialize getters for left/right arrow image from constructor Initialize method making it possible to override fields Decorations, DecorationScrollLeft and DecorationScrollRight after widget creation.
2023-01-27 12:30:38 +02:00
Paul Chote
8be3ac863b Only update changed files when saving maps. 2023-01-27 07:43:25 +01:00
Dean Simmons
e1b78c4821 Only return file paths when returning zip contents 2023-01-26 19:25:58 +02:00
penev92
c1da198f5d Fixed RA parabomb/paradrop support power tooltips
The AFLD's AirstrikePower@parabombs' description wasn't updated to the latest balance patch (3 Badgers -> 1 Badger)
2023-01-26 09:32:34 +01:00
penev92
474de014f8 Move RenameEngineerRepair to its proper folder 2023-01-25 12:00:57 +01:00
Matthias Mailänder
c3752d1c18 Remove engineer repairing completely. 2023-01-24 23:26:14 +01:00
Matthias Mailänder
f67b6f6cad Give EngineerRepair(able) more generic names 2023-01-24 23:26:14 +01:00
Matthias Mailänder
9b2e291a46 Extract translation strings. 2023-01-23 20:51:45 +01:00
Matthias Mailänder
867efcc6e8 Add Media.DisplayMessageToPlayer 2023-01-23 20:51:45 +01:00
Matthias Mailänder
847bbf5710 Use out parameter modifier code style. 2023-01-23 20:51:45 +01:00
Matthias Mailänder
f013a003a0 Don't translate enemy objectives. 2023-01-23 20:51:45 +01:00
Matthias Mailänder
7cdc8c4ec5 Add a quick save button to the map editor. 2023-01-23 14:13:19 +02:00
Paul Chote
3dd1fd6b00 Add support for Flatpak Steam installs. 2023-01-22 22:39:36 +01:00
Paul Chote
e13a7aed90 Manual yaml cleanup. 2023-01-22 22:10:48 +02:00
Paul Chote
5b8f148c50 Simplify tileset-specific sequence definitions.
All magic behaviour for constructing sprite filenames
has been removed in favour of an explicit Filename
(and TilesetFilenames for tileset-specific sequences)
property.
2023-01-22 22:10:48 +02:00
Paul Chote
04c3cd6ec5 Reimplement sequence Defaults parsing:
The previous MiniYaml.Merge implementation interacted
poorly with yaml inheritance, making it complicated
(or impossible) to override certain keys from Defaults.

The new implementation is simpler: If a key is defined
it will be used. If it isn't, the default (if defined)
will be used. Defaults can be masked by making sure
the same key is defined (even with an empty value)
in the sequence.

This also fixes naming within the sequence code to
distinguish between images (a group of sequences),
sequences (defining a specific sprite/animation),
and filenames for a specific sprite/animation.
2023-01-22 22:10:48 +02:00
Paul Chote
05c83a9dbb Fix ExtractSpriteSequenceDocsCommand crash on non-generic static fields. 2023-01-22 22:10:48 +02:00
abcdefg30
3ef0b3be95 Add the expected parameter(s) to the ExtractFilesCommand description 2023-01-22 18:31:32 +00:00
Gustas
84e7eb144b Move RearmTick to Rearmable 2023-01-20 11:43:12 +02:00
Gustas
75d65b3d20 Remove redundant INotifyRearm 2023-01-20 10:43:18 +02:00
Gustas
12af8506f8 Fix Rearmable using the wrong interface 2023-01-20 10:43:18 +02:00
Gustas
0d24f2c08b Merge INotifyBeingResupplied into INotifyDockClient 2023-01-20 10:43:18 +02:00
Gustas
dca07d240c Add INotifyDockHost and INotifyDockClient interfaces
Rename INotifyDocking to INotifyDockHost and extract INotifyDockClient from INotifyHarvesterAction
2023-01-20 10:43:18 +02:00
N.N
ba011ffc5f Fix deviator GrantExternalCondition Warhead
Warhead now applies only to enemy and neutral units.
2023-01-18 23:41:18 +01:00
RoosterDragon
faf12f93a4 Fix Locomotor cache coherency for disabled or paused mobile actors.
The cache in Locomotor that is populated via the UpdateCellBlocking method disagreed with the non-cached logic of IsBlockedBy when dealing with Mobile actors. The cache determined an actor to be moving if it was both movable and had horizontal movement types. IsBlockedBy determined an actor to be moving if it had horizontal movement types, but did not check if it was movable. This difference in checks could allow a mobile trait that was disabled or paused and which had horizontal movement to be treated differently be the two methods. UpdateCellBlocking would consider it not moving due to the disabled/paused trait. IsBlockedBy would consider it moving as it didn't care about the disabled/paused state of the trait.

Now, we unify the two methods to consider a mobile trait that is disabled/paused as not moving. This prevents HierarchicalPathFinder from crashing on the inconsistent state, i.e. when asked to path search through a cell of a mobile unit which has disabled or paused movement, but which has horizontal movement types from prior movement.
2023-01-17 18:58:22 +01:00
Andre Mohren
1dc44c4047 Looped sounds can now be unlooped to avoid cut-offs. 2023-01-14 13:56:20 +01:00
David Wilson
9cd3981b94 Fix to ensure Windows uninstaller removes reg entries from the 64 bit registry 2023-01-14 13:47:37 +01:00
Moath Altarawneh
44be6cea94 Update Program.cs
a small change to line 174 to use an easier way to format the string 👍
2023-01-14 13:02:28 +02:00
abcdefg30
5bf7fe852c Remove the copyright year numbers 2023-01-11 11:58:54 +02:00
penev92
5f80e93aee Added ITilesetSpecificPaletteInfo for linting 2023-01-10 16:36:36 +00:00
Gustas
80b92fb667 Fixed UnhardcodeBaseBuilderBotModule update rule
Update rules should should not read `modData.DefaultRules`
2023-01-10 18:02:24 +02:00
Gustas
29d21545a6 Fixed UnhardcodeSquadManager update rule
Update rules should should not read `modData.DefaultRules`
2023-01-10 18:02:24 +02:00
Gustas
326f8115a0 Exclude Plugs from TS protection types 2023-01-10 18:02:24 +02:00
Paul Chote
129db98a2f Add BeforeUpdate* methods for update rules.
These make it possible to write more advanced update
rules that query state across multiple actors, or
based on resolved state.
2023-01-10 18:02:24 +02:00
penev92
15fe2d5594 Added an update rule for adding ControlGroups 2023-01-10 00:15:00 +01:00
penev92
e3e012a9ed Added an update rule for DomainIndex removal
Also for adding the new HPF-related PathFinderOverlay and HierarchicalPathFinderOverlay that were added at the same time.
2023-01-10 00:15:00 +01:00
Ivaylo Draganov
04648a66e6 Clarify the wording and explain the meaning of some labels in settings 2023-01-09 21:47:11 +02:00
abcdefg30
edd0068d88 Remove an unuseful comment from mods/ra/installer/origin.yaml 2023-01-09 19:27:50 +01:00
Matthias Mailänder
79d786708b Fix URL button blocking the party button. 2023-01-08 23:07:19 +01:00
Matthias Mailänder
921da2f19e Bump DiscordRichPresence library. 2023-01-08 23:07:19 +01:00
Andre Mohren
0b94a0639e Implemented WestwoodCompression for AUD files. 2023-01-08 12:40:57 +00:00
Gustas
fb93281beb Fixed a spelling error in Discovery ant mission 2023-01-07 17:02:12 +01:00
Gustas
b75976b6d8 Remove bogus bridge tiles from temperate tileset 2023-01-07 17:02:12 +01:00
Matthias Mailänder
b0aea7b810 Bump checkout action. 2023-01-04 21:54:55 +00:00
Matthias Mailänder
f65c3a09db Bump setup .NET action. 2023-01-04 21:54:55 +00:00
abcdefg30
d4d6d5b7c0 Remove custom network frame counting from ReplayConnection 2023-01-04 09:33:55 +00:00
Matthias Mailänder
56ff98a2a3 Throw specific cursor sequence errors
instead of dividing by zero because length is 0.
2023-01-03 13:25:50 +01:00
Paul Chote
6d438a9d61 Allow mods to customise the default rendering scale. 2022-12-31 17:11:03 +01:00
Gustas
e21f94f36a Guarantee 1 riflemen on sell in mods
And engineer on RA and D2K construction yards
2022-12-31 16:51:26 +01:00
Gustas
34543e2952 Add an option to guarantee actors on sell 2022-12-31 16:51:26 +01:00
Unrud
b1ffe0edd5 Don't use parameter expansion for paths
It fails when the script is started with `bash package-all.sh`.
2022-12-31 16:20:55 +01:00
Matthias Mailänder
19ecddcd86 Enforce use of 'var' instead of explicit type. 2022-12-28 23:02:04 +01:00
abcdefg30
982c97dc6c Specify targetable offsets for the repair pad in Dune 2000 2022-12-28 17:11:31 +02:00
Paul Chote
27c602fc30 Downgrade OpenAL-soft to 1.10.1.
1.12.2 introduces noticeable behaviour changes
to source positioning and cash ticks. This also
fixes missing sound backends on Linux.
2022-12-28 00:19:32 +01:00
Matthias Mailänder
943751547e Don't hardcode enemy aircraft ignorance. 2022-12-25 22:24:57 +01:00
Matthias Mailänder
24ed5f7a1a Add linting to AI squad manager types. 2022-12-25 22:24:57 +01:00
abcdefg30
0d654d5e53 Change into the correct directory before trying to push
Each workflow step starts back in the default directory,
so we need to checkout the repository folder again
2022-12-24 16:02:46 +01:00
abcdefg30
ffa015dc21 Let the documentation workflow fetch the required git branch 2022-12-24 16:02:46 +01:00
Paul Chote
a6eb00f326 Fix pseudo-fullscreen window size on macs with a notch. 2022-12-24 12:10:26 +01:00
abcdefg30
5a12f44a25 Fix map actors not being spawned with the correct owner 2022-12-24 11:20:35 +02:00
Matthias Mailänder
5ffb564376 Fix invalid actors not spawning in-game. 2022-12-23 18:33:55 +01:00
Matthias Mailänder
6bcf2f718c In map editor replace invalid actor owners with neutral. 2022-12-23 18:33:55 +01:00
Paul Chote
7c2be4ce3c Implement state prediction for lobby ready checkbox. 2022-12-23 16:33:21 +02:00
Paul Chote
25935bbe99 Implement state prediction for debug menu checkboxes. 2022-12-23 16:33:21 +02:00
Paul Chote
2ba52f1b94 Implement state prediction for lobby checkboxes. 2022-12-23 16:33:21 +02:00
Paul Chote
e2e541a251 Package macOS releases as a universal binary.
* Minimum macOS version is raised to 10.11.
* App bundles ship 3 versions of the runtime and engine binaries,
  and a fat launcher that selects the appropriate runtime/apphost.
* Mono is used for macOS 10.11 - 10.14, or if OPENRA_PREFER_MONO
  environment variable has been set.
2022-12-23 12:54:06 +02:00
Paul Chote
363a0e1d1e Update native deps to include macOS and Linux arm64 binaries. 2022-12-23 12:54:06 +02:00
Paul Chote
80945cd08a Report CPU arch in logs and sysinfo. 2022-12-23 12:54:06 +02:00
Paul Chote
a17e1671f0 Fix MiniYaml source locations being lost when merging. 2022-12-23 10:44:54 +02:00
Gustas
b619dd14c5 Remove sniper 2022-12-21 21:56:24 -06:00
Gustas
55cf40ec52 Added carryalls to spectator Economy statistics 2022-12-21 21:06:38 +01:00
Gustas
f3e44094a1 Fix contrail using the wrong colors 2022-12-21 19:35:52 +02:00
N.N
43094742fb Change tile 9 to Rough TerrainType 2022-12-21 19:31:15 +02:00
abcdefg30
70549fce14 Run the CI workflow on PRs targeting prep 2022-12-21 13:28:14 +02:00
Paul Chote
360a5b293d Bubble unhandled double-click events to OnClick. 2022-12-20 23:01:41 +01:00
RoosterDragon
f4965915ee Fix PathFinderOverlay crash when deselecting actor moving along waypoints.
Set an actor moving along several waypoints whilst the /path-debug command is active, then deselect the actor. Each waypoint will add more information to the debugging overlay until it crashes with "Maximum two records permitted." This resolves the crash by no longer adding new debugging information once the actor is deselected.
2022-12-20 13:23:32 +13:00
RoosterDragon
39e48d9e8d HPF is aware of map projection changes.
An event is added to Map to indicate when the cell projection is changed. This is important as this can mean Map.Contains(CPos) could now return different results for the cell. The HierarchicalPathFinder is made aware of these changes so it can rebuild any out-of-date information. This fixes prevent a crash if a cell that was previously outside the map changes height and becomes inside the map. The local path search will explore the cell as it is inside the map - but if the HPF was unaware if had been updated, it will still consider the cell to be outside the map and unreachable, resulting in a crash.
2022-12-20 09:47:24 +13:00
RoosterDragon
d8ebb96077 HPF handles searches from unreachable source cells into cut off areas.
If a path search is performed by the HierarchicalPathFinder when the source cell is unreachable location, a path is still allowed and starts from one of the cells adjacent to the location. If moving into one of these cells results in the actor moving into an isolated area that cannot reach the target this would previously crash as no abstract path could be found. Now we handle such locations by giving them a unreachable cost so the path search will not attempt to explore them.

Imagine a map split into two by a one tile wide line of impassable cliffs. If an actor is on top of these cliffs it is allowed to path because it can "jump off" the cliff and move into the cell immediately either side of it. However it is important which side it chooses to jump into, as one it has moved off the cliff it loses access to the other side. The previous error came about because the path might try and search on the side that couldn't reach the target location. Now we avoid that being considered.
2022-12-20 09:47:24 +13:00
Paul Chote
11e5d19f32 Prioritise primary buildings in CycleBasesHotkeyLogic. 2022-12-19 11:55:45 +02:00
Alex
82d0546d16 build: harden workflow permissions
Signed-off-by: Alex <aleksandrosansan@gmail.com>
2022-12-19 22:40:19 +13:00
Ivaylo Draganov
a0f17b15ec Refactor translation files
- Add prefixes to all message keys to provide context
- Use messages with attributes for some UI elements (dropdowns, dialogs, checkboxes, menus)
- Rename some class fields for consistency with translation keys
2022-12-19 22:04:54 +13:00
Matthias Mailänder
46caa2d889 Make RenderShroud/JammerCircle conditional. 2022-12-19 20:03:40 +13:00
Gustas
475468ccc7 Fix Devil's Tongue attacking while submerged 2022-12-19 19:25:59 +13:00
Gustas
f3dc168dbd Don't cancel guard cursor when holding shift 2022-12-18 15:39:12 +01:00
Matthias Mailänder
4ffd81bd41 Fix documentation deployment. 2022-12-18 12:57:08 +13:00
abcdefg30
0f149f1143 Fix the actor edit panel not always getting closed properly 2022-12-18 12:24:36 +13:00
Ivaylo Draganov
8513de0b47 Fix error in PlayerResources trait documentation
This was missed in #19295
2022-12-16 12:14:54 +02:00
Ivaylo Draganov
614603089e Define and measure duration for text notifications in milliseconds
During a game notification duration should be the same regardless of
game speed. Switch to using wall-clock time defined in milliseconds 
instead of game ticks. Also use the opportunity to rename the field 
to "Duration" because "RemoveTime" is not so clear.
2022-12-15 23:28:46 +01:00
Ivaylo Draganov
e280e0f31c Use an overload that already passes the second argument as true 2022-12-15 23:28:46 +01:00
Gustas
c739447598 Fix map editor sliders stealing focus 2022-12-14 23:53:28 +01:00
Ivaylo Draganov
e60f7bb125 Remove unused common chrome yaml file 2022-12-12 23:51:46 +01:00
Ivaylo Draganov
18e36b96db Add HPF overlay controls to observer chrome 2022-12-12 23:51:46 +01:00
Gustas
2b57b6be1d Don't cancel attack move cursor when holding shift 2022-12-12 23:20:45 +01:00
dnqbob
45d4a2c7e2 Fix syncreport crash caused by getting LocalClient index from ReplayConnection 2022-12-11 23:32:59 +02:00
Gustas
6fccd6be84 Fix attack move lines not showing up for undeploy on force move units
DeployForGrantedCondition is wrapped around the Move activity, so the AttackMoveActivity thinks that DeployForGrantedCondition is the Move activity.

All it means is that we need to forward the target line request to the Move activity
2022-12-11 22:06:47 +01:00
Smittytron
767ac1c4e2 Remove MustDestroy from longbows in Soviet 11 2022-12-10 16:16:00 +02:00
Smittytron
b14caf004d Fix Siberian Conflict 3 win trigger 2022-12-10 16:16:00 +02:00
N.N
d72d25c369 Update D2k map pool 2022-12-07 23:39:35 +01:00
Matthias Mailänder
640e52d4b4 Report missing translation keys only once. 2022-12-07 18:40:26 +02:00
Matthias Mailänder
9ba51c6b51 Manually add game speeds to the linter. 2022-12-07 18:40:26 +02:00
Matthias Mailänder
8297fcff30 Expose lobby options to localisation. 2022-12-07 18:40:26 +02:00
Matthias Mailänder
aefa49a831 Add support for translating trait rules. 2022-12-07 18:40:26 +02:00
Matthias Mailänder
e251377f7c Add support for translation attributes. 2022-12-07 18:40:26 +02:00
Matthias Mailänder
760a1245c5 Mark non-moddable translation strings as constant. 2022-12-07 18:40:26 +02:00
Matthias Mailänder
4f016f149f Install libfuse2 in Ubuntu 22.04 2022-12-06 09:46:48 +02:00
abcdefg30
c14b585433 Remove a stray $ from infront of the version string when writing logs 2022-12-03 23:49:15 +02:00
Matthias Mailänder
2d78dae01a Execute self-contained binaries with runtime as backfall 2022-12-02 23:03:40 +01:00
Gustas
b0036a2d3e Add cell reference HPF crash messages 2022-12-02 17:46:46 +01:00
Matthias Mailänder
3091504c7e Unify file extensions. 2022-12-01 19:52:56 +02:00
Matthias Mailänder
bd678659a2 Update Ubuntu 2022-12-01 19:52:56 +02:00
Paul Chote
06edc3dff1 Remove redundant order copies. 2022-11-27 22:58:23 +01:00
Paul Chote
c2208ce8fe Always serialize orders. 2022-11-27 22:58:23 +01:00
Paul Chote
1add57e5ad Fix frame number not being included in pre-serialized order packets. 2022-11-27 22:58:23 +01:00
Matthias Mailänder
435c999abf Update notarisation to XCode 13 tooling. 2022-11-27 20:31:10 +01:00
Paul Chote
c82b8244e8 Fix display bounds when running on macbooks with a notch. 2022-11-26 20:43:57 +01:00
dnqbob
8703cfc4f4 Incease NumSyncReports to 7 2022-11-25 23:57:39 +01:00
dnqbob
63411f9938 Add Player is host infomation for a better debug of desync related with AI 2022-11-25 23:57:39 +01:00
dnqbob
60f35f779d SyncReport filename contains index of local client 2022-11-25 23:57:39 +01:00
dnqbob
1a2ef49100 SyncReport: Output the frames recorded for better debug. 2022-11-25 23:57:39 +01:00
dnqbob
6fbdc2c221 HarvesterBotModule should not command harvesters that cannot be ordered 2022-11-24 23:09:22 +01:00
Matthias Mailänder
14b5504ea7 Show the host in the download failed error message. 2022-11-23 23:35:53 +01:00
Matthias Mailänder
6f4f0c4e8f Fix unknown host not getting translated. 2022-11-23 23:35:53 +01:00
dnqbob
ac92162825 Weapon checks valid frozen actor 2022-11-22 21:49:12 +01:00
dnqbob
a25558b550 Weapon doesn't check warheads in IsValidAgainst 2022-11-22 21:49:12 +01:00
dnqbob
e8d9e2dfa9 Make weapon can target attacker itself an option. 2022-11-22 21:49:12 +01:00
Vapre
aa878c9dc8 ExceptionHandler, fix npe. 2022-11-22 12:35:28 +01:00
abcdefg30
7aa50d412c Remove the unused MONO_TAG variable from the MacOS buildpackage script 2022-11-20 22:08:38 +01:00
Matthias Mailänder
69949f9d53 Revert "Scripts: Check exit status of background process"
This reverts commit 3f106bef72.
2022-11-20 20:36:29 +02:00
Andre Mohren
0c8ae195ae Removed offset and length utility commands, obsolete now. 2022-11-19 12:27:13 +01:00
Andre Mohren
e6682d2108 Added C&C Remastered installer support (Origin and Steam) 2022-11-19 12:27:13 +01:00
IceReaper
9fa6c47dc5 Added GoG and Steam SourceResolver. 2022-11-19 12:27:13 +01:00
Andre Mohren
f5b169ab54 Added ExtractMixSourceAction. 2022-11-19 12:27:13 +01:00
IceReaper
09b32f7f98 Fixed the blast decompression. Fixes #13689 2022-11-19 12:27:13 +01:00
Matthias Mailänder
621c85059e Extract Tiberian Dawn campaign translations. 2022-11-17 22:28:07 +02:00
Matthias Mailänder
b62cf7ee9a Add translation arguments to the Lua API. 2022-11-17 22:28:07 +02:00
Matthias Mailänder
5d118e2634 Inline variables. 2022-11-17 22:28:07 +02:00
Matthias Mailänder
807e9b5496 Don't crash Lua when the translation attempt fails. 2022-11-17 22:28:07 +02:00
IceReaper
0c47a0a710 Bugfix: mod.yaml was listing downloads as source too! 2022-11-17 21:19:57 +01:00
IceReaper
35eb246080 Replaced hardcoded SourceType with custom defined ISourceResolver. 2022-11-17 21:19:57 +01:00
IceReaper
7188f88ba1 Replaced hardcoded source actions by user defined ISourceAction. 2022-11-17 21:19:57 +01:00
IceReaper
fcc8f53b59 Installer downloads now using specified IPackageLoader. 2022-11-17 21:19:57 +01:00
Gustas
a9da6bb2d8 Introduce MinDistance to AreaBeam projectile 2022-11-17 20:48:37 +01:00
Gustas
f7286b525c Move AbortOnResupply from AttackAircraft to AttackFollow 2022-11-17 20:42:47 +01:00
Julius Vitkauskas
4517734fbe Dispose DependencyContextJsonReader after using 2022-11-17 20:36:13 +02:00
ThomasChr
bd882c98c7 fix Issue #20373 - Aircraft hanging midair 2022-11-17 20:27:14 +02:00
Thomas Christlieb
ed9880f801 fix misclicks through sidebar 2022-11-16 23:20:50 +01:00
IceReaper
8ae5383698 Made installer asset resolving case insensitive. 2022-11-17 00:16:59 +02:00
IceReaper
98e7058486 ZipFile is now determined by a zip header, not the file extensions, as it may be a temp file. 2022-11-17 00:16:59 +02:00
IceReaper
0aced08204 Support for InstallShield CAB version <= 5. 2022-11-17 00:16:59 +02:00
2565 changed files with 53502 additions and 46735 deletions

View File

@@ -8,24 +8,444 @@ end_of_line = LF
insert_final_newline = true
trim_trailing_whitespace = true
; 4-column tab indentation
[*.yaml]
indent_style = tab
indent_size = 4
; 4-column tab indentation and .NET coding conventions
[*.cs]
indent_style = tab
indent_size = 4
dotnet_separate_import_directive_groups = false
dotnet_sort_system_directives_first = true
#### Code Style Rules
#### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/
csharp_style_var_elsewhere = true:suggestion
csharp_style_var_for_built_in_types = true:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
# Severity Levels: https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/configuration-options#severity-level
# Below we enable specific rules by setting severity to warning.
# Rules are disabled by setting severity to silent (to still allow use in IDE) or none (to prevent all use).
# Rules are listed below with any options available.
# Options are commented out if they match the defaults.
csharp_prefer_braces = when_multiline:suggestion
csharp_using_directive_placement = outside_namespace:suggestion
csharp_new_line_before_open_brace = all
csharp_space_around_binary_operators = before_and_after
### Language Rules
### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/language-rules
## Naming styles:
## this and Me preferences
# IDE0003/IDE0009 Remove 'this' or 'Me' qualification/Add 'this' or 'Me' qualification
#dotnet_style_qualification_for_field = false
#dotnet_style_qualification_for_property = false
#dotnet_style_qualification_for_method = false
#dotnet_style_qualification_for_event = false
dotnet_diagnostic.IDE0003.severity = warning
dotnet_diagnostic.IDE0009.severity = warning
## Use languages keywords for types
# IDE0049 Use language keywords instead of framework type names for type references
#dotnet_style_predefined_type_for_locals_parameters_members = true
#dotnet_style_predefined_type_for_member_access = true
dotnet_diagnostic.IDE0049.severity = warning
## Modifier preferences
# IDE0036 Order modifiers
#csharp_preferred_modifier_order = public, private, protected, internal, file, static, extern, new, virtual, abstract, sealed, override, readonly, unsafe, required, volatile, async
dotnet_diagnostic.IDE0036.severity = warning
# IDE0040 Add accessibility modifiers
dotnet_style_require_accessibility_modifiers = omit_if_default
dotnet_diagnostic.IDE0040.severity = warning
# IDE0044 Add readonly modifier
#dotnet_style_readonly_field = true
dotnet_diagnostic.IDE0044.severity = warning
# IDE0062 Make local function static
#csharp_prefer_static_local_function = true
dotnet_diagnostic.IDE0062.severity = warning
# IDE0064 Make struct fields writable
# No options
dotnet_diagnostic.IDE0064.severity = warning
## Parentheses preferences
# IDE0047/IDE0048 Remove unnecessary parentheses/Add parentheses for clarity
dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary
dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary
#dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
#dotnet_style_parentheses_in_other_operators = never_if_unnecessary
dotnet_diagnostic.IDE0047.severity = warning
dotnet_diagnostic.IDE0048.severity = warning
## Expression-level preferences
# IDE0010 Add missing cases to switch statement
# No options
dotnet_diagnostic.IDE0010.severity = silent
# IDE0017 Use object initializers
#dotnet_style_object_initializer = true
dotnet_diagnostic.IDE0017.severity = warning
# IDE0018 Inline variable declaration
#csharp_style_inlined_variable_declaration = true
dotnet_diagnostic.IDE0018.severity = warning
# IDE0028 Use collection initializers
#dotnet_style_collection_initializer = true
dotnet_diagnostic.IDE0028.severity = warning
# IDE0032 Use auto-implemented property
#dotnet_style_prefer_auto_properties = true
dotnet_diagnostic.IDE0032.severity = warning
# IDE0033 Use explicitly provided tuple name
#dotnet_style_explicit_tuple_names = true
dotnet_diagnostic.IDE0033.severity = warning
# IDE0034 Simplify 'default' expression
#csharp_prefer_simple_default_expression = true
dotnet_diagnostic.IDE0034.severity = warning
# IDE0037 Use inferred member name
#dotnet_style_prefer_inferred_tuple_names = true
#dotnet_style_prefer_inferred_anonymous_type_member_names = true
dotnet_diagnostic.IDE0037.severity = silent
# IDE0039 Use local function instead of lambda
#csharp_style_prefer_local_over_anonymous_function = true
dotnet_diagnostic.IDE0039.severity = warning
# IDE0042 Deconstruct variable declaration
#csharp_style_deconstructed_variable_declaration = true
dotnet_diagnostic.IDE0042.severity = warning
# IDE0045 Use conditional expression for assignment
#dotnet_style_prefer_conditional_expression_over_assignment = true
dotnet_diagnostic.IDE0045.severity = silent
# IDE0046 Use conditional expression for return
#dotnet_style_prefer_conditional_expression_over_return = true
dotnet_diagnostic.IDE0046.severity = silent
# IDE0050 Convert anonymous type to tuple
# No options
dotnet_diagnostic.IDE0050.severity = silent
# IDE0054/IDE0074 Use compound assignment/Use coalesce compound assignment
#dotnet_style_prefer_compound_assignment = true
dotnet_diagnostic.IDE0054.severity = warning
dotnet_diagnostic.IDE0074.severity = warning
# IDE0056 Use index operator
#csharp_style_prefer_index_operator = true
dotnet_diagnostic.IDE0056.severity = warning
# IDE0057 Use range operator
#csharp_style_prefer_range_operator = true
dotnet_diagnostic.IDE0057.severity = warning
# IDE0070 Use 'System.HashCode.Combine'
# No options
dotnet_diagnostic.IDE0070.severity = warning
# IDE0071 Simplify interpolation
#dotnet_style_prefer_simplified_interpolation = true
dotnet_diagnostic.IDE0071.severity = warning
# IDE0072 Add missing cases to switch expression
# No options
dotnet_diagnostic.IDE0072.severity = silent
# IDE0075 Simplify conditional expression
#dotnet_style_prefer_simplified_boolean_expressions = true
dotnet_diagnostic.IDE0075.severity = warning
# IDE0082 Convert 'typeof' to 'nameof'
# No options
dotnet_diagnostic.IDE0082.severity = warning
# IDE0090 Simplify 'new' expression
#csharp_style_implicit_object_creation_when_type_is_apparent = true
dotnet_diagnostic.IDE0090.severity = warning
# IDE0180 Use tuple to swap values
#csharp_style_prefer_tuple_swap = true
dotnet_diagnostic.IDE0180.severity = warning
## Namespace declaration preferences
# IDE0160/IDE0161 Use block-scoped namespace/Use file-scoped namespace
#csharp_style_namespace_declarations = block_scoped
dotnet_diagnostic.IDE0160.severity = warning
dotnet_diagnostic.IDE0161.severity = warning
## Null-checking preferences
# IDE0016 Use throw expression
#csharp_style_throw_expression = true
dotnet_diagnostic.IDE0016.severity = silent
# IDE0029/IDE0030/IDE0270 Use coalesce expression (non-nullable types)/Use coalesce expression (nullable types)/Use coalesce expression (if null)
#dotnet_style_coalesce_expression = true
dotnet_diagnostic.IDE0029.severity = warning
dotnet_diagnostic.IDE0030.severity = warning
dotnet_diagnostic.IDE0270.severity = silent
# IDE0031 Use null propagation
#dotnet_style_null_propagation = true
dotnet_diagnostic.IDE0031.severity = warning
# IDE0041 Use 'is null' check
#dotnet_style_prefer_is_null_check_over_reference_equality_method = true
dotnet_diagnostic.IDE0041.severity = warning
# IDE0150 Prefer 'null' check over type check
#csharp_style_prefer_null_check_over_type_check = true
dotnet_diagnostic.IDE0150.severity = warning
# IDE1005 Use conditional delegate call
csharp_style_conditional_delegate_call = true # true is the default, but the rule is not triggered if this is not specified.
dotnet_diagnostic.IDE1005.severity = warning
## var preferences
# IDE0007/IDE0008 Use 'var' instead of explicit type/Use explicit type instead of 'var'
csharp_style_var_for_built_in_types = true
csharp_style_var_when_type_is_apparent = true
csharp_style_var_elsewhere = true
dotnet_diagnostic.IDE0007.severity = warning
dotnet_diagnostic.IDE0008.severity = warning
## Expression-bodied-members
# IDE0021 Use expression body for constructors
#csharp_style_expression_bodied_constructors = false
dotnet_diagnostic.IDE0021.severity = silent
# IDE0022 Use expression body for methods
#csharp_style_expression_bodied_methods = false
dotnet_diagnostic.IDE0022.severity = silent
# IDE0023/IDE0024 Use expression body for conversion operators/Use expression body for operators
#csharp_style_expression_bodied_operators = false
dotnet_diagnostic.IDE0023.severity = silent
dotnet_diagnostic.IDE0024.severity = silent
# IDE0025 Use expression body for properties
#csharp_style_expression_bodied_properties = true
dotnet_diagnostic.IDE0025.severity = silent
# IDE0026 Use expression body for indexers
#csharp_style_expression_bodied_indexers = true
dotnet_diagnostic.IDE0026.severity = silent
# IDE0027 Use expression body for accessors
#csharp_style_expression_bodied_accessors = true
dotnet_diagnostic.IDE0027.severity = warning
# IDE0053 Use expression body for lambdas
# This rule is buggy and not enforced for builds. ':warning' will at least enforce it in the IDE.
csharp_style_expression_bodied_lambdas = when_on_single_line:warning
dotnet_diagnostic.IDE0053.severity = warning
# IDE0061 Use expression body for local functions
csharp_style_expression_bodied_local_functions = when_on_single_line
dotnet_diagnostic.IDE0061.severity = warning
## Pattern matching preferences
# IDE0019 Use pattern matching to avoid 'as' followed by a 'null' check
#csharp_style_pattern_matching_over_as_with_null_check = true
dotnet_diagnostic.IDE0019.severity = warning
# IDE0020/IDE0038 Use pattern matching to avoid 'is' check followed by a cast (with variable)/Use pattern matching to avoid 'is' check followed by a cast (without variable)
#csharp_style_pattern_matching_over_is_with_cast_check = true
dotnet_diagnostic.IDE0020.severity = warning
dotnet_diagnostic.IDE0038.severity = warning
# IDE0066 Use switch expression
#csharp_style_prefer_switch_expression = true
dotnet_diagnostic.IDE0066.severity = silent
# IDE0078 Use pattern matching
#csharp_style_prefer_pattern_matching = true
dotnet_diagnostic.IDE0078.severity = silent
# IDE0083 Use pattern matching ('not' operator)
#csharp_style_prefer_not_pattern = true
dotnet_diagnostic.IDE0083.severity = warning
# IDE0170 Simplify property pattern
#csharp_style_prefer_extended_property_pattern = true
dotnet_diagnostic.IDE0170.severity = silent # Requires C# 10
## Code block preferences
# IDE0011 Add braces
#csharp_prefer_braces = true
# No options match the style used in OpenRA.
dotnet_diagnostic.IDE0011.severity = none
# IDE0063 Use simple 'using' statement
#csharp_prefer_simple_using_statement = true
dotnet_diagnostic.IDE0063.severity = silent
## 'using' directive preferences
# IDE0065 'using' directive placement
#csharp_using_directive_placement = outside_namespace
dotnet_diagnostic.IDE0065.severity = silent
## File header preferences
# IDE0073 Require file header
#file_header_template = unset
# This rule does not allow us to enforce our desired header, as it prefixes the header lines with // comments, meaning we can't apply a region.
dotnet_diagnostic.IDE0073.severity = none
## Namespace naming preferences
# IDE0130 Namespace does not match folder structure
#dotnet_style_namespace_match_folder = true
# This rule doesn't appear to work (never reports violations)
dotnet_diagnostic.IDE0130.severity = none
### Unnecessary Code Rules
### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/unnecessary-code-rules
# IDE0001 Simplify name
# No options
dotnet_diagnostic.IDE0001.severity = warning
# IDE0002 Simplify member access
# No options
dotnet_diagnostic.IDE0002.severity = warning
# IDE0004 Remove unnecessary cast
# No options
dotnet_diagnostic.IDE0004.severity = warning
# IDE0005 Remove unnecessary import
# No options
# IDE0005 is only enabled in the IDE by default. https://github.com/dotnet/roslyn/issues/41640
# To enable it for builds outside the IDE the 'GenerateDocumentationFile' property must be enabled on the build.
# GenerateDocumentationFile generates additional warnings about XML docs, so disable any we don't care about.
dotnet_diagnostic.CS1591.severity = none # Missing XML comment for publicly visible type or member
dotnet_diagnostic.IDE0005.severity = warning
# IDE0035 Remove unreachable code
# No options
# Duplicates compiler warning CS0162
dotnet_diagnostic.IDE0035.severity = none
# IDE0051 Remove unused private member
# No options
dotnet_diagnostic.IDE0051.severity = warning
# IDE0052 Remove unread private member
# No options
dotnet_diagnostic.IDE0052.severity = warning
# IDE0058 Remove unnecessary expression value
#csharp_style_unused_value_expression_statement_preference = discard_variable
dotnet_diagnostic.IDE0058.severity = silent
# IDE0059 Remove unnecessary value assignment
#csharp_style_unused_value_assignment_preference = discard_variable
dotnet_diagnostic.IDE0059.severity = warning
# IDE0060 Remove unused parameter
dotnet_code_quality_unused_parameters = non_public
dotnet_diagnostic.IDE0060.severity = warning
# IDE0079 Remove unnecessary suppression
#dotnet_remove_unnecessary_suppression_exclusions = none
dotnet_diagnostic.IDE0079.severity = warning
# IDE0080 Remove unnecessary suppression operator
# No options
dotnet_diagnostic.IDE0080.severity = warning
# IDE0100 Remove unnecessary equality operator
# No options
dotnet_diagnostic.IDE0100.severity = warning
# IDE0110 Remove unnecessary discard
# No options
dotnet_diagnostic.IDE0110.severity = warning
### Miscellaneous Rules
### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/miscellaneous-rules
# IDE0076 Remove invalid global 'SuppressMessageAttribute'
# No options
dotnet_diagnostic.IDE0076.severity = warning
# IDE0077 Avoid legacy format target in global 'SuppressMessageAttribute'
# No options
dotnet_diagnostic.IDE0077.severity = warning
### Formatting Rules (IDE0055)
### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0055
# We may eventually wish to enforce this rule, however some existing formatting conflicts with the rule despite being reasonable.
# Additionally, the rule is buggy and likes to report spuriously after invoking Format Document in the IDE.
dotnet_diagnostic.IDE0055.severity = none
#dotnet_sort_system_directives_first = true
#dotnet_separate_import_directive_groups = false
#dotnet_style_namespace_match_folder = true
#csharp_new_line_before_open_brace = all
#csharp_new_line_before_else = true
#csharp_new_line_before_catch = true
#csharp_new_line_before_finally = true
#csharp_new_line_before_members_in_object_initializers = true
#csharp_new_line_before_members_in_anonymous_types = true
#csharp_new_line_between_query_expression_clauses = true
#csharp_indent_case_contents = true
#csharp_indent_switch_labels = true
#csharp_indent_labels = one_less_than_current
#csharp_indent_block_contents = true
#csharp_indent_braces = false
#csharp_indent_case_contents_when_block = true
#csharp_space_after_cast = false
#csharp_space_after_keywords_in_control_flow_statements = true
#csharp_space_between_parentheses =
#csharp_space_before_colon_in_inheritance_clause = true
#csharp_space_after_colon_in_inheritance_clause = true
#csharp_space_around_binary_operators = before_and_after
#csharp_space_between_method_declaration_parameter_list_parentheses = false
#csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
#csharp_space_between_method_declaration_name_and_open_parenthesis = false
#csharp_space_between_method_call_parameter_list_parentheses = false
#csharp_space_between_method_call_empty_parameter_list_parentheses = false
#csharp_space_between_method_call_name_and_opening_parenthesis = false
#csharp_space_after_comma = true
#csharp_space_before_comma = false
#csharp_space_after_dot = false
#csharp_space_before_dot = false
#csharp_space_after_semicolon_in_for_statement = true
#csharp_space_before_semicolon_in_for_statement = false
#csharp_space_around_declaration_statements = false
#csharp_space_before_open_square_brackets = false
#csharp_space_between_empty_square_brackets = false
#csharp_space_between_square_brackets = false
#csharp_preserve_single_line_statements = true
#csharp_preserve_single_line_blocks = true
### Naming Rules (IDE1006)
### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/naming-rules
dotnet_diagnostic.IDE1006.severity = warning
## Naming styles
dotnet_naming_style.camel_case.capitalization = camel_case
@@ -34,7 +454,7 @@ dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_naming_style.i_prefix_pascal_case.capitalization = pascal_case
dotnet_naming_style.i_prefix_pascal_case.required_prefix = I
## Symbol specifications:
## Naming Symbols
dotnet_naming_symbols.const_locals.applicable_kinds = local
dotnet_naming_symbols.const_locals.applicable_accessibilities = *
@@ -64,7 +484,7 @@ dotnet_naming_symbols.parameters_and_locals.applicable_accessibilities = *
dotnet_naming_symbols.most_symbols.applicable_kinds = namespace, class, struct, enum, field, property, method, local_function, event, delegate, type_parameter
dotnet_naming_symbols.most_symbols.applicable_accessibilities = *
## Naming rules:
## Naming Rules
dotnet_naming_rule.const_locals_should_be_pascal_case.symbols = const_locals
dotnet_naming_rule.const_locals_should_be_pascal_case.style = pascal_case
@@ -98,86 +518,378 @@ dotnet_naming_rule.most_symbols_should_be_pascal_case.symbols = most_symbols
dotnet_naming_rule.most_symbols_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.most_symbols_should_be_pascal_case.severity = warning
## Formatting:
# Also handled by StyleCopAnalyzers - SA1024: ColonsMustBeSpacedCorrectly.
csharp_space_after_colon_in_inheritance_clause = true
### StyleCop.Analyzers
### https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/DOCUMENTATION.md
# Also handled by StyleCopAnalyzers - SA1024: ColonsMustBeSpacedCorrectly.
csharp_space_before_colon_in_inheritance_clause = true
# Below we enable rule categories by setting severity to warning.
# We'll only list rules to disable.
# Individual rules we wish to disable are typically set to none severity.
# Also handled by StyleCopAnalyzers - SA1000: KeywordsMustBeSpacedCorrectly.
csharp_space_after_keywords_in_control_flow_statements = true
# Covers SAxxxx and SXxxxx rules
dotnet_analyzer_diagnostic.category-StyleCop.CSharp.DocumentationRules.severity = warning
dotnet_analyzer_diagnostic.category-StyleCop.CSharp.LayoutRules.severity = warning
dotnet_analyzer_diagnostic.category-StyleCop.CSharp.MaintainabilityRules.severity = warning
dotnet_analyzer_diagnostic.category-StyleCop.CSharp.NamingRules.severity = warning
dotnet_analyzer_diagnostic.category-StyleCop.CSharp.OrderingRules.severity = warning
dotnet_analyzer_diagnostic.category-StyleCop.CSharp.ReadabilityRules.severity = warning
dotnet_analyzer_diagnostic.category-StyleCop.CSharp.SpacingRules.severity = warning
dotnet_analyzer_diagnostic.category-StyleCop.CSharp.SpecialRules.severity = warning
# Leave code block on single line.
csharp_preserve_single_line_blocks = true
# Rules that are covered by IDE0001 Simplify name
dotnet_diagnostic.SA1125.severity = none # UseShorthandForNullableTypes
# Leave statements and member declarations on the same line.
csharp_preserve_single_line_statements = true
# Rules that are covered by IDE0047 Remove unnecessary parentheses
dotnet_diagnostic.SA1119.severity = none # StatementMustNotUseUnnecessaryParenthesis
# IDE0049, IDE-only counterpart of StyleCopAnalyzers - SA1121: UseBuiltInTypeAlias.
dotnet_style_predefined_type_for_member_access = true
# Rules that are covered by IDE0055 Formatting Rules
dotnet_diagnostic.SA1027.severity = none # UseTabsCorrectly
# IDE0049, IDE-only counterpart of StyleCopAnalyzers - SA1121: UseBuiltInTypeAlias.
dotnet_style_predefined_type_for_locals_parameters_members = true
# Rules that are covered by IDE1006 Naming Rules
dotnet_diagnostic.SA1300.severity = none # ElementMustBeginWithUpperCaseLetter
dotnet_diagnostic.SA1302.severity = none # InterfaceNamesMustBeginWithI
dotnet_diagnostic.SA1303.severity = none # ConstFieldNamesMustBeginWithUpperCaseLetter
dotnet_diagnostic.SA1304.severity = none # NonPrivateReadonlyFieldsMustBeginWithUpperCaseLetter
dotnet_diagnostic.SA1306.severity = none # FieldNamesMustBeginWithLowerCaseLetter
dotnet_diagnostic.SA1307.severity = none # AccessibleFieldsMustBeginWithUpperCaseLetter
dotnet_diagnostic.SA1311.severity = none # StaticReadonlyFieldsMustBeginWithUpperCaseLetter
dotnet_diagnostic.SA1312.severity = none # VariableNamesMustBeginWithLowerCaseLetter
dotnet_diagnostic.SA1313.severity = none # ParameterNamesMustBeginWithLowerCaseLetter
## Others:
# Rules that conflict with OpenRA project style conventions
dotnet_diagnostic.SA1101.severity = none # PrefixLocalCallsWithThis
dotnet_diagnostic.SA1107.severity = none # CodeMustNotContainMultipleStatementsOnOneLine
dotnet_diagnostic.SA1116.severity = none # SplitParametersMustStartOnLineAfterDeclaration
dotnet_diagnostic.SA1117.severity = none # ParametersMustBeOnSameLineOrSeparateLines
dotnet_diagnostic.SA1118.severity = none # ParameterMustNotSpanMultipleLines
dotnet_diagnostic.SA1122.severity = none # UseStringEmptyForEmptyStrings
dotnet_diagnostic.SA1124.severity = none # DoNotUseRegions
dotnet_diagnostic.SA1127.severity = none # GenericTypeConstraintsMustBeOnOwnLine
dotnet_diagnostic.SA1132.severity = none # DoNotCombineFields
dotnet_diagnostic.SA1135.severity = none # UsingDirectivesMustBeQualified
dotnet_diagnostic.SA1136.severity = none # EnumValuesShouldBeOnSeparateLines
dotnet_diagnostic.SA1200.severity = none # UsingDirectivesMustBePlacedCorrectly
dotnet_diagnostic.SA1201.severity = none # ElementsMustAppearInTheCorrectOrder
dotnet_diagnostic.SA1202.severity = none # ElementsMustBeOrderedByAccess
dotnet_diagnostic.SA1204.severity = none # StaticElementsMustAppearBeforeInstanceElements
dotnet_diagnostic.SA1214.severity = none # ReadonlyElementsMustAppearBeforeNonReadonlyElements
dotnet_diagnostic.SX1309.severity = none # FieldNamesMustBeginWithUnderscore
dotnet_diagnostic.SX1309S.severity = none # StaticFieldNamesMustBeginWithUnderscore
dotnet_diagnostic.SA1314.severity = none # TypeParameterNamesMustBeginWithT
dotnet_diagnostic.SA1400.severity = none # AccessModifierMustBeDeclared
dotnet_diagnostic.SA1401.severity = none # FieldsMustBePrivate
dotnet_diagnostic.SA1402.severity = none # FileMayOnlyContainASingleType
dotnet_diagnostic.SA1407.severity = none # ArithmeticExpressionsMustDeclarePrecedence
dotnet_diagnostic.SA1413.severity = none # UseTrailingCommasInMultiLineInitializers
dotnet_diagnostic.SA1501.severity = none # StatementMustNotBeOnSingleLine
dotnet_diagnostic.SA1502.severity = none # ElementMustNotBeOnSingleLine
dotnet_diagnostic.SA1503.severity = none # BracesMustNotBeOmitted
dotnet_diagnostic.SA1516.severity = none # ElementsMustBeSeparatedByBlankLine
dotnet_diagnostic.SA1519.severity = none # BracesMustNotBeOmittedFromMultiLineChildStatement
dotnet_diagnostic.SA1520.severity = none # UseBracesConsistently
dotnet_diagnostic.SA1600.severity = none # ElementsMustBeDocumented
dotnet_diagnostic.SA1601.severity = none # PartialElementsMustBeDocumented
dotnet_diagnostic.SA1602.severity = none # EnumerationItemsMustBeDocumented
dotnet_diagnostic.SA1611.severity = none # ElementParametersShouldBeDocumented
dotnet_diagnostic.SA1615.severity = none # ElementReturnValueShouldBeDocumented
dotnet_diagnostic.SA1618.severity = none # GenericTypeParametersShouldBeDocumented
dotnet_diagnostic.SA1623.severity = none # PropertySummaryDocumentationShouldMatchAccessors
dotnet_diagnostic.SA1633.severity = none # FileMustHaveHeader
dotnet_diagnostic.SA1642.severity = none # ConstructorSummaryDocumentationShouldBeginWithStandardText
dotnet_diagnostic.SA1649.severity = none # FileNameMustMatchTypeName
# Show an IDE warning when default access modifiers are explicitly specified.
dotnet_style_require_accessibility_modifiers = omit_if_default:warning
#### Code Quality Rules
#### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/
# Don't prefer braces (for one liners).
dotnet_diagnostic.IDE0011.severity = silent
# Below we enable specific rules by setting severity to warning.
# Rules are listed below with any options available.
# Options are commented out if they match the defaults.
# Object initialization can be simplified / Use object initializer.
dotnet_diagnostic.IDE0017.severity = warning
# Rule options that apply over multiple rules are set here.
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/code-quality-rule-options
dotnet_code_quality.api_surface = all
# Collection initialization can be simplified
dotnet_diagnostic.IDE0028.severity = warning
### Design Rules
### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/design-warnings
# Simplify 'default' expression
dotnet_diagnostic.IDE0034.severity = warning
# Collections should implement generic interface.
#dotnet_code_quality.CA1010.additional_required_generic_interfaces =
dotnet_diagnostic.CA1010.severity = warning
# Modifiers are not ordered.
dotnet_diagnostic.IDE0036.severity = warning
# Abstract types should not have public constructors.
dotnet_diagnostic.CA1012.severity = warning
# Raise a warning on build when default access modifiers are explicitly specified.
dotnet_diagnostic.IDE0040.severity = warning
# Mark attributes with 'AttributeUsageAttribute'.
dotnet_diagnostic.CA1018.severity = warning
# Make field readonly.
dotnet_diagnostic.IDE0044.severity = warning
# Override methods on comparable types.
dotnet_diagnostic.CA1036.severity = warning
# Unused private member.
dotnet_diagnostic.IDE0052.severity = warning
# Provide ObsoleteAttribute message.
dotnet_diagnostic.CA1041.severity = warning
# Unnecessary value assignment.
dotnet_diagnostic.IDE0059.severity = warning
# Do not declare protected members in sealed types.
dotnet_diagnostic.CA1047.severity = warning
# Unused parameter.
dotnet_diagnostic.IDE0060.severity = warning
# Declare types in namespaces.
dotnet_diagnostic.CA1050.severity = warning
# Naming rule violation.
dotnet_diagnostic.IDE1006.severity = warning
# Static holder types should be 'Static' or 'NotInheritable'.
dotnet_diagnostic.CA1052.severity = warning
# Avoid unnecessary zero-length array allocations.
# Do not hide base class methods.
dotnet_diagnostic.CA1061.severity = warning
# Exceptions should be public.
dotnet_diagnostic.CA1064.severity = warning
# Implement 'IEquatable' when overriding 'Equals'.
dotnet_diagnostic.CA1066.severity = warning
# Override 'Equals' when implementing 'IEquatable'.
dotnet_diagnostic.CA1067.severity = warning
# 'CancellationToken' parameters must come last.
dotnet_diagnostic.CA1068.severity = warning
# Do not declare event fields as virtual.
dotnet_diagnostic.CA1070.severity = warning
### Documentation Rules
### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/documentation-warnings
# Avoid using 'cref' tags with a prefix.
dotnet_diagnostic.CA1200.severity = warning
### Globalization Rules
### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/globalization-warnings
### Portability and Interoperability Rules
### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/interoperability-warnings
# Do not use 'OutAttribute' on string parameters for P/Invokes.
dotnet_diagnostic.CA1417.severity = warning
### Maintainability Rules
### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/maintainability-warnings
# Use 'nameof' in place of string.
dotnet_diagnostic.CA1507.severity = warning
### Naming Rules
### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/naming-warnings
# Do not prefix enum values with type name.
dotnet_code_quality.CA1712.enum_values_prefix_trigger = AnyEnumValue
dotnet_diagnostic.CA1712.severity = warning
# Flags enums should have plural names.
dotnet_diagnostic.CA1714.severity = warning
# Only 'FlagsAttribute' enums should have plural names.
dotnet_diagnostic.CA1717.severity = warning
### Performance Rules
### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/performance-warnings
# Use Literals Where Appropriate.
#dotnet_code_quality.CA1802.required_modifiers = static
dotnet_diagnostic.CA1802.severity = warning
# Remove empty finalizers.
dotnet_diagnostic.CA1821.severity = warning
# Mark members as static.
dotnet_code_quality.CA1822.api_surface = private,internal
dotnet_diagnostic.CA1822.severity = warning
# Avoid unused private fields.
dotnet_diagnostic.CA1823.severity = warning
# Avoid zero-length array allocations.
dotnet_diagnostic.CA1825.severity = warning
# Do not use Enumerable methods on indexable collections. Instead use the collection directly.
# Use property instead of Linq Enumerable method.
#dotnet_code_quality.CA1826.exclude_ordefault_methods = false
dotnet_diagnostic.CA1826.severity = warning
# Count() is used where Any() could be used instead to improve performance.
# Do not use Count/LongCount when Any can be used.
dotnet_diagnostic.CA1827.severity = warning
# Do not use CountAsync/LongCountAsync when AnyAsync can be used.
dotnet_diagnostic.CA1828.severity = warning
# Use Length/Count property instead of Enumerable.Count method.
dotnet_diagnostic.CA1829.severity = warning
# Prefer strongly-typed Append and Insert method overloads on StringBuilder.
dotnet_diagnostic.CA1830.severity = warning
# Use AsSpan instead of Range-based indexers for string when appropriate.
dotnet_diagnostic.CA1831.severity = warning
# Use AsSpan or AsMemory instead of Range-based indexers for getting ReadOnlySpan or ReadOnlyMemory portion of an array.
dotnet_diagnostic.CA1832.severity = warning
# Use AsSpan or AsMemory instead of Range-based indexers for getting Span or Memory portion of an array.
dotnet_diagnostic.CA1833.severity = warning
# Use StringBuilder.Append(char) for single character strings.
dotnet_diagnostic.CA1834.severity = warning
# Prefer the memory-based overloads of ReadAsync/WriteAsync methods in stream-based classes.
dotnet_diagnostic.CA1835.severity = warning
# Prefer IsEmpty over Count when available.
dotnet_diagnostic.CA1836.severity = warning
# Use Environment.ProcessId instead of Process.GetCurrentProcess().Id.
dotnet_diagnostic.CA1837.severity = warning
# Avoid StringBuilder parameters for P/Invokes.
dotnet_diagnostic.CA1838.severity = warning
# Use Environment.ProcessPath instead of Process.GetCurrentProcess().MainModule.FileName.
dotnet_diagnostic.CA1839.severity = warning
# Use Environment.CurrentManagedThreadId instead of Thread.CurrentThread.ManagedThreadId.
dotnet_diagnostic.CA1840.severity = warning
# Prefer Dictionary Contains methods.
dotnet_diagnostic.CA1841.severity = warning
# Do not use 'WhenAll' with a single task.
dotnet_diagnostic.CA1842.severity = warning
# Do not use 'WaitAll' with a single task.
dotnet_diagnostic.CA1843.severity = warning
# Provide memory-based overrides of async methods when subclassing 'Stream'.
dotnet_diagnostic.CA1844.severity = warning
# Use span-based 'string.Concat'. (Not available on mono)
dotnet_diagnostic.CA1845.severity = none
# Prefer AsSpan over Substring.
dotnet_diagnostic.CA1846.severity = warning
# Use string.Contains(char) instead of string.Contains(string) with single characters.
dotnet_diagnostic.CA1847.severity = warning
; 4-column tab indentation
[*.yaml]
indent_style = tab
indent_size = 4
# Call async methods when in an async method.
dotnet_diagnostic.CA1849.severity = warning
# Prefer static HashData method over ComputeHash. (Not available on mono)
dotnet_diagnostic.CA1850.severity = none
# Seal internal types.
dotnet_diagnostic.CA1852.severity = warning
# Unnecessary call to 'Dictionary.ContainsKey(key)'.
dotnet_diagnostic.CA1853.severity = warning
# Prefer the IDictionary.TryGetValue(TKey, out TValue) method.
dotnet_diagnostic.CA1854.severity = warning
# Use Span<T>.Clear() instead of Span<T>.Fill().
dotnet_diagnostic.CA1855.severity = warning
# Use StartsWith instead of IndexOf.
dotnet_diagnostic.CA1858.severity = warning
# Avoid using 'Enumerable.Any()' extension method.
dotnet_diagnostic.CA1860.severity = warning
### Reliability Rules
### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/reliability-warnings
# Do not assign property within its setter.
dotnet_diagnostic.CA2011.severity = warning
# Use ValueTasks correctly.
dotnet_diagnostic.CA2012.severity = warning
# Do not use ReferenceEquals with value types.
dotnet_diagnostic.CA2013.severity = warning
# Do not use stackalloc in loops.
dotnet_diagnostic.CA2014.severity = warning
# Forward the CancellationToken parameter to methods that take one.
dotnet_diagnostic.CA2016.severity = warning
# The 'count' argument to Buffer.BlockCopy should specify the number of bytes to copy.
dotnet_diagnostic.CA2018.severity = warning
# ThreadStatic fields should not use inline initialization.
dotnet_diagnostic.CA2019.severity = warning
### Security Rules
### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/security-warnings
# Do Not Use Broken Cryptographic Algorithms.
dotnet_diagnostic.CA5351.severity = warning
### Usage Rules
### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/usage-warnings
# Call GC.SuppressFinalize correctly.
dotnet_diagnostic.CA1816.severity = warning
# Rethrow to preserve stack details.
dotnet_diagnostic.CA2200.severity = warning
# Initialize value type static fields inline.
dotnet_diagnostic.CA2207.severity = warning
# Instantiate argument exceptions correctly.
dotnet_diagnostic.CA2208.severity = warning
# Dispose methods should call base class dispose.
dotnet_diagnostic.CA2215.severity = warning
# Disposable types should declare finalizer.
dotnet_diagnostic.CA2216.severity = warning
# Override GetHashCode on overriding Equals.
dotnet_diagnostic.CA2218.severity = warning
# Overload operator equals on overriding ValueType.Equals.
dotnet_diagnostic.CA2231.severity = warning
# Provide correct arguments to formatting methods.
#dotnet_code_quality.CA2241.additional_string_formatting_methods =
dotnet_code_quality.CA2241.try_determine_additional_string_formatting_methods_automatically = true
dotnet_diagnostic.CA2241.severity = warning
# Test for NaN correctly.
dotnet_diagnostic.CA2242.severity = warning
# Attribute string literals should parse correctly.
dotnet_diagnostic.CA2243.severity = warning
# Do not duplicate indexed element initializations.
dotnet_diagnostic.CA2244.severity = warning
# Do not assign a property to itself.
dotnet_diagnostic.CA2245.severity = warning
# Argument passed to TaskCompletionSource constructor should be TaskCreationOptions enum instead of TaskContinuationOptions enum.
dotnet_diagnostic.CA2247.severity = warning
# Provide correct enum argument to Enum.HasFlag.
dotnet_diagnostic.CA2248.severity = warning
# Use ThrowIfCancellationRequested.
dotnet_diagnostic.CA2250.severity = warning
# Ensure ThreadStatic is only used with static fields.
dotnet_diagnostic.CA2259.severity = warning
### Roslynator
### https://github.com/JosefPihrt/Roslynator/tree/main/docs/analyzers
# Below we enable specific rules by setting severity to warning.
# Use 'Count' property instead of 'Any' method.
dotnet_diagnostic.RCS1080.severity = warning

View File

@@ -3,39 +3,43 @@ name: Continuous Integration
on:
push:
pull_request:
branches: [ bleed ]
branches: [ bleed, 'prep-*' ]
permissions:
contents: read # to fetch code (actions/checkout)
jobs:
linux:
name: Linux (.NET 6.0)
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- name: Clone Repository
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Install .NET 6.0
uses: actions/setup-dotnet@v1
uses: actions/setup-dotnet@v3
with:
dotnet-version: '6.0.x'
- name: Check Code
run: |
make check
make tests
- name: Check Mods
run: |
sudo apt-get install lua5.1
make check-scripts
make test
make TREAT_WARNINGS_AS_ERRORS=true test
linux-mono:
name: Linux (mono)
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- name: Clone Repository
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Check Code
run: |
@@ -45,7 +49,7 @@ jobs:
- name: Check Mods
run: |
# check-scripts does not depend on .net/mono, so is not needed here
make RUNTIME=mono test
make RUNTIME=mono TREAT_WARNINGS_AS_ERRORS=true test
windows:
name: Windows (.NET 6.0)
@@ -53,10 +57,10 @@ jobs:
steps:
- name: Clone Repository
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Install .NET 6.0
uses: actions/setup-dotnet@v1
uses: actions/setup-dotnet@v3
with:
dotnet-version: '6.0.x'
@@ -66,12 +70,12 @@ jobs:
# Work around runtime failures on the GH Actions runner
dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org
.\make.ps1 check
dotnet build OpenRA.Test\OpenRA.Test.csproj -c Debug --nologo -p:TargetPlatform=win-x64
dotnet test bin\OpenRA.Test.dll --test-adapter-path:.
.\make.ps1 tests
- name: Check Mods
run: |
chocolatey install lua --version 5.1.5.52
choco install lua --version 5.1.5.52
$ENV:Path = $ENV:Path + ";C:\Program Files (x86)\Lua\5.1\"
$ENV:TREAT_WARNINGS_AS_ERRORS = "true"
.\make.ps1 check-scripts
.\make.ps1 test

View File

@@ -8,19 +8,22 @@ on:
required: true
default: 'release-xxxxxxxx'
permissions:
contents: read # to fetch code (actions/checkout)
jobs:
wiki:
name: Update Wiki
if: github.repository == 'openra/openra'
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- name: Clone Repository
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
ref: ${{ github.event.inputs.tag }}
- name: Install .NET 6
uses: actions/setup-dotnet@v1
uses: actions/setup-dotnet@v3
with:
dotnet-version: '6.0.x'
@@ -29,7 +32,7 @@ jobs:
make all
- name: Clone Wiki
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
repository: openra/openra.wiki
token: ${{ secrets.DOCS_TOKEN }}
@@ -63,15 +66,15 @@ jobs:
docs:
name: Update docs.openra.net
if: github.repository == 'openra/openra'
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- name: Clone Repository
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
ref: ${{ github.event.inputs.tag }}
- name: Install .NET 6
uses: actions/setup-dotnet@v1
uses: actions/setup-dotnet@v3
with:
dotnet-version: '6.0.x'
@@ -79,19 +82,29 @@ jobs:
run: |
make all
- name: Clone docs.openra.net
uses: actions/checkout@v2
- name: Clone docs.openra.net (Playtest)
if: startsWith(github.event.inputs.tag, 'playtest-')
uses: actions/checkout@v3
with:
repository: openra/docs
token: ${{ secrets.DOCS_TOKEN }}
path: docs
ref: playtest
- name: Clone docs.openra.net (Release)
if: startsWith(github.event.inputs.tag, 'release-')
uses: actions/checkout@v3
with:
repository: openra/docs
token: ${{ secrets.DOCS_TOKEN }}
path: docs
ref: release
- name: Update docs.openra.net (Playtest)
if: startsWith(github.event.inputs.tag, 'playtest-')
env:
GIT_TAG: ${{ github.event.inputs.tag }}
run: |
git checkout playtest
./utility.sh all --docs "${GIT_TAG}" | python3 ./packaging/format-docs.py > "docs/api/traits.md"
./utility.sh all --weapon-docs "${GIT_TAG}" | python3 ./packaging/format-docs.py > "docs/api/weapons.md"
./utility.sh all --sprite-sequence-docs "${GIT_TAG}" | python3 ./packaging/format-docs.py > "docs/api/sprite-sequences.md"
@@ -102,7 +115,6 @@ jobs:
env:
GIT_TAG: ${{ github.event.inputs.tag }}
run: |
git checkout release
./utility.sh all --docs "${GIT_TAG}" | python3 ./packaging/format-docs.py > "docs/api/traits.md"
./utility.sh all --weapon-docs "${GIT_TAG}" | python3 ./packaging/format-docs.py > "docs/api/weapons.md"
./utility.sh all --sprite-sequence-docs "${GIT_TAG}" | python3 ./packaging/format-docs.py > "docs/api/sprite-sequences.md"
@@ -115,15 +127,17 @@ jobs:
cd docs
git config --local user.email "actions@github.com"
git config --local user.name "GitHub Actions"
git add *.md
git add api/*.md
git commit -m "Update auto-generated documentation for ${GIT_TAG}"
- name: Push docs.openra.net (Release)
if: startsWith(github.event.inputs.tag, 'release-')
run: |
cd docs
git push origin release
- name: Push docs.openra.net (Playtest)
if: startsWith(github.event.inputs.tag, 'playtest-')
run: |
cd docs
git push origin playtest

View File

@@ -8,10 +8,11 @@ on:
required: true
default: 'release-xxxxxxxx'
permissions: {}
jobs:
itch:
name: Deploy to itch.io
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
if: github.repository == 'openra/openra'
steps:
- name: Download Packages

View File

@@ -7,13 +7,16 @@ on:
- 'playtest-*'
- 'devtest-*'
permissions:
contents: write # for release creation (svenstaro/upload-release-action)
jobs:
source:
name: Source Tarball
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- name: Clone Repository
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Prepare Environment
run: echo "GIT_TAG=${GITHUB_REF#refs/tags/}" >> ${GITHUB_ENV}
@@ -34,13 +37,13 @@ jobs:
linux:
name: Linux AppImages
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- name: Clone Repository
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Install .NET 6.0
uses: actions/setup-dotnet@v1
uses: actions/setup-dotnet@v3
with:
dotnet-version: '6.0.x'
@@ -50,6 +53,7 @@ jobs:
- name: Package AppImages
run: |
mkdir -p build/linux
sudo apt install libfuse2
./packaging/linux/buildpackage.sh "${GIT_TAG}" "${PWD}/build/linux"
- name: Upload Packages
@@ -62,21 +66,21 @@ jobs:
file: build/linux/*
macos:
name: macOS Disk Images
name: macOS Disk Image
runs-on: macos-11
steps:
- name: Clone Repository
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Install .NET 6.0
uses: actions/setup-dotnet@v1
uses: actions/setup-dotnet@v3
with:
dotnet-version: '6.0.x'
- name: Prepare Environment
run: echo "GIT_TAG=${GITHUB_REF#refs/tags/}" >> ${GITHUB_ENV}
- name: Package Disk Images
- name: Package Disk Image
env:
MACOS_DEVELOPER_IDENTITY: ${{ secrets.MACOS_DEVELOPER_IDENTITY }}
MACOS_DEVELOPER_CERTIFICATE_BASE64: ${{ secrets.MACOS_DEVELOPER_CERTIFICATE_BASE64 }}
@@ -87,7 +91,7 @@ jobs:
mkdir -p build/macos
./packaging/macos/buildpackage.sh "${GIT_TAG}" "${PWD}/build/macos"
- name: Upload Packages
- name: Upload Package
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
@@ -98,13 +102,13 @@ jobs:
windows:
name: Windows Installers
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- name: Clone Repository
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Install .NET 6.0
uses: actions/setup-dotnet@v1
uses: actions/setup-dotnet@v3
with:
dotnet-version: '6.0.x'

View File

@@ -74,6 +74,7 @@ Also thanks to:
* Glenn Martin Jensen (Baxxster)
* Gordon Martin (Happy0)
* Guido Lipke (LipkeGu)
* Guillermo Cuenca (Wylli)
* Hervé Matysiak (Herve-M)
* Huw Pascoe
* Ian T. Jacobsen (Smilex)
@@ -119,6 +120,7 @@ Also thanks to:
* Mike Gagné (AngryBirdz)
* Muh
* Mustafa Alperen Seki (MustaphaTR)
* Nathan Nichols (cracksmoka420)
* Neil Shivkar (havok13888)
* Nikolay Fomin (netnazgul)
* Nooze

View File

@@ -5,15 +5,16 @@
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Optimize>true</Optimize>
<LangVersion>7.3</LangVersion>
<LangVersion>9</LangVersion>
<DebugSymbols>true</DebugSymbols>
<EngineRootPath Condition="'$(EngineRootPath)' == ''">..</EngineRootPath>
<OutputPath>$(EngineRootPath)/bin</OutputPath>
<PlatformTarget>AnyCPU</PlatformTarget>
<ExternalConsole>false</ExternalConsole>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
<CodeAnalysisRuleSet>$(EngineRootPath)/OpenRA.ruleset</CodeAnalysisRuleSet>
<Nullable>disable</Nullable>
<Product>OpenRA</Product>
<Copyright>Copyright (c) The OpenRA Developers and Contributors</Copyright>
</PropertyGroup>
<PropertyGroup>
@@ -30,6 +31,12 @@
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<DefineConstants>DEBUG;TRACE</DefineConstants>
<Optimize>false</Optimize>
<!-- Enable only for Debug builds to improve compile-time performance for Release builds -->
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<!-- Enabling GenerateDocumentationFile is required for IDE0005 (Remove unnecessary import)
rule to run in command line builds. https://github.com/dotnet/roslyn/issues/41640
Enable only for Debug builds to improve compile-time performance for Release builds -->
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
@@ -46,7 +53,6 @@
<!-- StyleCop -->
<ItemGroup>
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.2" PrivateAssets="All" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" />
</ItemGroup>
</Project>

View File

@@ -17,7 +17,7 @@ Run the game with `launch-game.cmd`. It can be handed arguments that specify the
Linux
=====
.NET 6 or Mono (version 6.4 or later) is required to compile OpenRA. We recommend using .NET 6 when possible, as Mono is poorly packaged by most Linux distributions (e.g. missing the required `msbuild` toolchain), and has been deprecated as a standalone project.
.NET 6 or Mono (version 6.12 or later) is required to compile OpenRA. We recommend using .NET 6 when possible, as Mono is poorly packaged by most Linux distributions (e.g. missing the required `msbuild` toolchain), and has been deprecated as a standalone project.
The [.NET 6 download page](https://dotnet.microsoft.com/download/dotnet/6.0) provides repositories for various package managers and binary releases for several architectures. If you prefer to use Mono, we suggest adding the [upstream repository](https://www.mono-project.com/download/stable/#download-lin) for your distro to obtain the latest version and the `msbuild` toolchain.
@@ -78,6 +78,6 @@ Type `sudo make install` for system-wide installation. Run `sudo make install-li
macOS
=====
[.NET 6](https://dotnet.microsoft.com/download/dotnet/6.0) or [Mono](https://www.mono-project.com/download/stable/#download-mac) (version 6.4 or later) is required to compile OpenRA. We recommend using .NET 6 unless you are running a very old version of macOS (10.9 through 10.14).
[.NET 6](https://dotnet.microsoft.com/download/dotnet/6.0) or [Mono](https://www.mono-project.com/download/stable/#download-mac) (version 6.12 or later) is required to compile OpenRA. We recommend using .NET 6 unless you are running a very old version of macOS (10.9 through 10.14).
To compile OpenRA, run `make` from the command line (or `make RUNTIME=mono` if using Mono). Run with `./launch-game.sh`.

View File

@@ -3,7 +3,7 @@
# to compile, run:
# make
#
# to compile using Mono (version 6.4 or greater) instead of .NET 6, run:
# to compile using Mono (version 6.12 or greater) instead of .NET 6, run:
# make RUNTIME=mono
#
# to compile using system libraries for native dependencies, run:
@@ -58,6 +58,8 @@ RM_RF = $(RM) -rf
RUNTIME ?= net6
CONFIGURATION ?= Release
DOTNET_RID = $(shell ${DOTNET} --info | grep RID: | cut -w -f3)
ARCH_X64 = $(shell echo ${DOTNET_RID} | grep x64)
# Only for use in target version:
VERSION := $(shell git name-rev --name-only --tags --no-undefined HEAD 2>/dev/null || (c=$$(git rev-parse --short HEAD 2>/dev/null) && echo git-$$c))
@@ -67,22 +69,30 @@ ifndef TARGETPLATFORM
UNAME_S := $(shell uname -s)
UNAME_M := $(shell uname -m)
ifeq ($(UNAME_S),Darwin)
ifeq ($(ARCH_X64),)
TARGETPLATFORM = osx-arm64
else
TARGETPLATFORM = osx-x64
endif
else
ifeq ($(UNAME_M),x86_64)
TARGETPLATFORM = linux-x64
else
ifeq ($(UNAME_M),aarch64)
TARGETPLATFORM = linux-arm64
else
TARGETPLATFORM = unix-generic
endif
endif
endif
endif
##################### DEVELOPMENT BUILDS AND TESTS #####################
#
all:
@echo "Compiling in ${CONFIGURATION} mode..."
ifeq ($(RUNTIME), mono)
@command -v $(firstword $(MSBUILD)) >/dev/null || (echo "OpenRA requires the '$(MSBUILD)' tool provided by Mono >= 6.4."; exit 1)
@command -v $(firstword $(MSBUILD)) >/dev/null || (echo "OpenRA requires the '$(MSBUILD)' tool provided by Mono >= 6.12."; exit 1)
@$(MSBUILD) -t:Build -restore -p:Configuration=${CONFIGURATION} -p:TargetPlatform=$(TARGETPLATFORM)
else
@$(DOTNET) build -c ${CONFIGURATION} -nologo -p:TargetPlatform=$(TARGETPLATFORM)
@@ -102,11 +112,10 @@ check:
@echo
@echo "Compiling in Debug mode..."
ifeq ($(RUNTIME), mono)
# Enabling EnforceCodeStyleInBuild and GenerateDocumentationFile as a workaround for some code style rules (in particular IDE0005) being bugged and not reporting warnings/errors otherwise.
@$(MSBUILD) -t:build -restore -p:Configuration=Debug -warnaserror -p:TargetPlatform=$(TARGETPLATFORM) -p:EnforceCodeStyleInBuild=true -p:GenerateDocumentationFile=true
@$(MSBUILD) -t:clean\;build -restore -p:Configuration=Debug -warnaserror -p:TargetPlatform=$(TARGETPLATFORM)
else
# Enabling EnforceCodeStyleInBuild and GenerateDocumentationFile as a workaround for some code style rules (in particular IDE0005) being bugged and not reporting warnings/errors otherwise.
@$(DOTNET) build -c Debug -nologo -warnaserror -p:TargetPlatform=$(TARGETPLATFORM) -p:EnforceCodeStyleInBuild=true -p:GenerateDocumentationFile=true
@$(DOTNET) clean -c Debug --nologo --verbosity minimal
@$(DOTNET) build -c Debug -nologo -warnaserror -p:TargetPlatform=$(TARGETPLATFORM)
endif
ifeq ($(TARGETPLATFORM), unix-generic)
@./configure-system-libraries.sh
@@ -121,7 +130,7 @@ endif
check-scripts:
@echo
@echo "Checking for Lua syntax errors..."
@find lua/ mods/*/{maps,scripts}/ -iname "*.lua" -print0 | xargs -0n1 luac -p
@find mods/*/maps/ mods/*/scripts/ -iname "*.lua" -print0 | xargs -0n1 luac -p
test: all
@echo
@@ -137,6 +146,11 @@ test: all
@echo "Testing Red Alert mod MiniYAML..."
@./utility.sh ra --check-yaml
tests:
@dotnet build OpenRA.Test/OpenRA.Test.csproj -c Debug --nologo -p:TargetPlatform=$(TARGETPLATFORM)
@echo
@dotnet test bin/OpenRA.Test.dll --test-adapter-path:.
############# LOCAL INSTALLATION AND DOWNSTREAM PACKAGING ##############
#
version: VERSION mods/ra/mod.yaml mods/cnc/mod.yaml mods/d2k/mod.yaml mods/ts/mod.yaml mods/modcontent/mod.yaml mods/all/mod.yaml
@@ -164,14 +178,14 @@ help:
@echo 'to compile, run:'
@echo ' make'
@echo
@echo 'to compile using Mono (version 6.4 or greater) instead of .NET 6, run:'
@echo 'to compile using Mono (version 6.12 or greater) instead of .NET 6, run:'
@echo ' make RUNTIME=mono'
@echo
@echo 'to compile using system libraries for native dependencies, run:'
@echo ' make [RUNTIME=net6] TARGETPLATFORM=unix-generic'
@echo
@echo 'to check the official mods for erroneous yaml files, run:'
@echo ' make [RUNTIME=net6] test'
@echo ' make [RUNTIME=net6] [TREAT_WARNINGS_AS_ERRORS=false] test'
@echo
@echo 'to check the engine and official mod dlls for code style violations, run:'
@echo ' make [RUNTIME=net6] check'

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -86,7 +86,7 @@ namespace OpenRA.Activities
bool firstRunCompleted;
bool lastRun;
public Activity()
protected Activity()
{
IsInterruptible = true;
ChildHasPriority = true;

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -35,6 +35,9 @@ namespace OpenRA
public sealed class Actor : IScriptBindable, IScriptNotifyBind, ILuaTableBinding, ILuaEqualityBinding, ILuaToStringBinding, IEquatable<Actor>, IDisposable
{
/// <summary>Value used to represent an invalid token.</summary>
public const int InvalidConditionToken = -1;
internal readonly struct SyncHash
{
public readonly ISync Trait;
@@ -78,27 +81,24 @@ namespace OpenRA
public WRot Orientation => facing?.Orientation ?? WRot.None;
/// <summary>Value used to represent an invalid token.</summary>
public static readonly int InvalidConditionToken = -1;
class ConditionState
sealed class ConditionState
{
/// <summary>Delegates that have registered to be notified when this condition changes.</summary>
public readonly List<VariableObserverNotifier> Notifiers = new List<VariableObserverNotifier>();
public readonly List<VariableObserverNotifier> Notifiers = new();
/// <summary>Unique integers identifying granted instances of the condition.</summary>
public readonly HashSet<int> Tokens = new HashSet<int>();
public readonly HashSet<int> Tokens = new();
}
readonly Dictionary<string, ConditionState> conditionStates = new Dictionary<string, ConditionState>();
readonly Dictionary<string, ConditionState> conditionStates = new();
/// <summary>Each granted condition receives a unique token that is used when revoking.</summary>
readonly Dictionary<int, string> conditionTokens = new Dictionary<int, string>();
readonly Dictionary<int, string> conditionTokens = new();
int nextConditionToken = 1;
/// <summary>Cache of condition -> enabled state for quick evaluation of token counter conditions.</summary>
readonly Dictionary<string, int> conditionCache = new Dictionary<string, int>();
readonly Dictionary<string, int> conditionCache = new();
/// <summary>Read-only version of conditionCache that is passed to IConditionConsumers.</summary>
readonly IReadOnlyDictionary<string, int> readOnlyConditionCache;
@@ -587,7 +587,7 @@ namespace OpenRA
return InvalidConditionToken;
}
/// <summary>Returns whether the specified token is valid for RevokeCondition</summary>
/// <summary>Returns whether the specified token is valid for RevokeCondition.</summary>
public bool TokenValid(int token)
{
return conditionTokens.ContainsKey(token);
@@ -600,8 +600,7 @@ namespace OpenRA
Lazy<ScriptActorInterface> luaInterface;
public void OnScriptBind(ScriptContext context)
{
if (luaInterface == null)
luaInterface = Exts.Lazy(() => new ScriptActorInterface(context, this));
luaInterface ??= Exts.Lazy(() => new ScriptActorInterface(context, this));
}
public LuaValue this[LuaRuntime runtime, LuaValue keyValue]

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -41,7 +41,7 @@ namespace OpenRA
Bits = (x & 0xFFF) << 20 | (y & 0xFFF) << 8 | layer;
}
public static readonly CPos Zero = new CPos(0, 0, 0);
public static readonly CPos Zero = new(0, 0, 0);
public static explicit operator CPos(int2 a) { return new CPos(a.X, a.Y); }
@@ -56,7 +56,7 @@ namespace OpenRA
public override int GetHashCode() { return Bits.GetHashCode(); }
public bool Equals(CPos other) { return Bits == other.Bits; }
public override bool Equals(object obj) { return obj is CPos && Equals((CPos)obj); }
public override bool Equals(object obj) { return obj is CPos cell && Equals(cell); }
public override string ToString()
{

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -22,7 +22,7 @@ namespace OpenRA
public readonly int X, Y;
public CVec(int x, int y) { X = x; Y = y; }
public static readonly CVec Zero = new CVec(0, 0);
public static readonly CVec Zero = new(0, 0);
public static CVec operator +(CVec a, CVec b) { return new CVec(a.X + b.X, a.Y + b.Y); }
public static CVec operator -(CVec a, CVec b) { return new CVec(a.X - b.X, a.Y - b.Y); }
@@ -55,7 +55,7 @@ namespace OpenRA
public override int GetHashCode() { return X.GetHashCode() ^ Y.GetHashCode(); }
public bool Equals(CVec other) { return other == this; }
public override bool Equals(object obj) { return obj is CVec && Equals((CVec)obj); }
public override bool Equals(object obj) { return obj is CVec vec && Equals(vec); }
public override string ToString() { return X + "," + Y; }

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -187,8 +187,10 @@ namespace OpenRA
}
catch (Exception e)
{
Log.Write("debug", "Failed to decrypt string with exception: {0}", e);
Console.WriteLine("String decryption failed: {0}", e);
Log.Write("debug", "Failed to decrypt string with exception:");
Log.Write("debug", e);
Console.WriteLine("String decryption failed:");
Console.WriteLine(e);
return null;
}
}
@@ -211,8 +213,10 @@ namespace OpenRA
}
catch (Exception e)
{
Log.Write("debug", "Failed to sign string with exception: {0}", e);
Console.WriteLine("String signing failed: {0}", e);
Log.Write("debug", "Failed to sign string with exception");
Log.Write("debug", e);
Console.WriteLine("String signing failed:");
Console.WriteLine(e);
return null;
}
}
@@ -235,8 +239,10 @@ namespace OpenRA
}
catch (Exception e)
{
Log.Write("debug", "Failed to verify signature with exception: {0}", e);
Console.WriteLine("Signature validation failed: {0}", e);
Log.Write("debug", "Failed to verify signature with exception:");
Log.Write("debug", e);
Console.WriteLine("Signature validation failed:");
Console.WriteLine(e);
return false;
}
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of

View File

@@ -1,39 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using OpenRA.Graphics;
namespace OpenRA.Effects
{
public class AsyncAction : IEffect
{
readonly Action a;
readonly IAsyncResult ar;
public AsyncAction(IAsyncResult ar, Action a)
{
this.a = a;
this.ar = ar;
}
public void Tick(World world)
{
if (ar.IsCompleted)
{
world.AddFrameEndTask(w => { w.Remove(this); a(); });
}
}
public IEnumerable<IRenderable> Render(WorldRenderer r) { yield break; }
}
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -41,7 +41,7 @@ namespace OpenRA
public class ExternalMods : IReadOnlyDictionary<string, ExternalMod>
{
readonly Dictionary<string, ExternalMod> mods = new Dictionary<string, ExternalMod>();
readonly Dictionary<string, ExternalMod> mods = new();
readonly SheetBuilder sheetBuilder;
Sheet CreateSheet()
@@ -81,8 +81,8 @@ namespace OpenRA
}
catch (Exception e)
{
Log.Write("debug", "Failed to parse mod metadata file '{0}'", path);
Log.Write("debug", e.ToString());
Log.Write("debug", $"Failed to parse mod metadata file '{path}'");
Log.Write("debug", e);
}
}
}
@@ -174,17 +174,19 @@ namespace OpenRA
catch (Exception e)
{
Log.Write("debug", "Failed to register current mod metadata");
Log.Write("debug", e.ToString());
Log.Write("debug", e);
}
}
}
/// <summary>
/// Removes invalid mod registrations:
/// * LaunchPath no longer exists
/// * LaunchPath and mod id matches the active mod, but the version is different
/// * Filename doesn't match internal key
/// * Fails to parse as a mod registration
/// <list type="bullet">
/// <item>LaunchPath no longer exists.</item>
/// <item>LaunchPath and mod id matches the active mod, but the version is different.</item>
/// <item>Filename doesn't match internal key.</item>
/// <item>Fails to parse as a mod registration.</item>
/// </list>
/// </summary>
internal void ClearInvalidRegistrations(ModRegistration registration)
{
@@ -211,8 +213,8 @@ namespace OpenRA
}
catch (Exception e)
{
Log.Write("debug", "Failed to parse mod metadata file '{0}'", path);
Log.Write("debug", e.ToString());
Log.Write("debug", $"Failed to parse mod metadata file '{path}'");
Log.Write("debug", e);
}
// Remove from the ingame mod switcher
@@ -223,12 +225,12 @@ namespace OpenRA
try
{
File.Delete(path);
Log.Write("debug", "Removed invalid mod metadata file '{0}'", path);
Log.Write("debug", $"Removed invalid mod metadata file '{path}'");
}
catch (Exception e)
{
Log.Write("debug", "Failed to remove mod metadata file '{0}'", path);
Log.Write("debug", e.ToString());
Log.Write("debug", $"Failed to remove mod metadata file '{path}'");
Log.Write("debug", e);
}
}
}
@@ -249,13 +251,13 @@ namespace OpenRA
}
catch (Exception e)
{
Log.Write("debug", "Failed to remove mod metadata file '{0}'", path);
Log.Write("debug", e.ToString());
Log.Write("debug", $"Failed to remove mod metadata file '{path}'");
Log.Write("debug", e);
}
}
}
IEnumerable<string> GetSupportDirs(ModRegistration registration)
static IEnumerable<string> GetSupportDirs(ModRegistration registration)
{
var sources = new HashSet<string>(4);
if (registration.HasFlag(ModRegistration.System))

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -27,11 +27,6 @@ namespace OpenRA
return string.Compare(str.ToUpperInvariant(), str, false) == 0;
}
public static string F(this string fmt, params object[] args)
{
return string.Format(fmt, args);
}
public static T WithDefault<T>(T def, Func<T> f)
{
try { return f(); }
@@ -45,21 +40,16 @@ namespace OpenRA
return a.GetTypes().Select(t => t.Namespace).Distinct().Where(n => n != null);
}
public static bool HasAttribute<T>(this MemberInfo mi)
public static bool HasAttribute<TAttribute>(this MemberInfo mi)
where TAttribute : Attribute
{
return Attribute.IsDefined(mi, typeof(T));
return Attribute.IsDefined(mi, typeof(TAttribute));
}
public static T[] GetCustomAttributes<T>(this MemberInfo mi, bool inherit)
where T : class
public static TAttribute[] GetCustomAttributes<TAttribute>(this MemberInfo mi, bool inherit)
where TAttribute : Attribute
{
return (T[])mi.GetCustomAttributes(typeof(T), inherit);
}
public static T[] GetCustomAttributes<T>(this ParameterInfo mi)
where T : class
{
return (T[])mi.GetCustomAttributes(typeof(T), true);
return (TAttribute[])mi.GetCustomAttributes(typeof(TAttribute), inherit);
}
public static T Clamp<T>(this T val, T min, T max) where T : IComparable<T>
@@ -119,13 +109,23 @@ namespace OpenRA
public static V GetOrAdd<K, V>(this Dictionary<K, V> d, K k, V v)
{
#if NET5_0_OR_GREATER
// SAFETY: Dictionary cannot be modified whilst the ref is alive.
ref var value = ref System.Runtime.InteropServices.CollectionsMarshal.GetValueRefOrAddDefault(d, k, out var exists);
if (!exists)
value = v;
return value;
#else
if (!d.TryGetValue(k, out var ret))
d.Add(k, ret = v);
return ret;
#endif
}
public static V GetOrAdd<K, V>(this Dictionary<K, V> d, K k, Func<K, V> createFn)
{
// Cannot use CollectionsMarshal.GetValueRefOrAddDefault here,
// the creation function could mutate the dictionary which would invalidate the ref.
if (!d.TryGetValue(k, out var ret))
d.Add(k, ret = createFn(k));
return ret;
@@ -149,7 +149,7 @@ namespace OpenRA
static T Random<T>(IEnumerable<T> ts, MersenneTwister r, bool throws)
{
var xs = ts as ICollection<T>;
xs = xs ?? ts.ToList();
xs ??= ts.ToList();
if (xs.Count == 0)
{
if (throws)
@@ -396,8 +396,8 @@ namespace OpenRA
string debugName, Func<TKey, string> logKey = null, Func<TElement, string> logValue = null)
{
// Fall back on ToString() if null functions are provided:
logKey = logKey ?? (s => s.ToString());
logValue = logValue ?? (s => s.ToString());
logKey ??= s => s.ToString();
logValue ??= s => s.ToString();
// Try to build a dictionary and log all duplicates found (if any):
var dupKeys = new Dictionary<TKey, List<string>>();
@@ -515,7 +515,7 @@ namespace OpenRA
public static bool IsTraitEnabled<T>(this T trait)
{
return !(trait is IDisabledTrait disabledTrait) || !disabledTrait.IsTraitDisabled;
return trait is not IDisabledTrait disabledTrait || !disabledTrait.IsTraitDisabled;
}
public static T FirstEnabledTraitOrDefault<T>(this IEnumerable<T> ts)
@@ -595,8 +595,8 @@ namespace OpenRA
return true;
}
Current = span.Slice(0, index);
str = span.Slice(index + 1);
Current = span[..index];
str = span[(index + 1)..];
return true;
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -56,24 +56,20 @@ namespace OpenRA
}
public static Func<string, Type, string, object> InvalidValueAction = (s, t, f) =>
{
throw new YamlException($"FieldLoader: Cannot parse `{s}` into `{f}.{t}` ");
};
public static Action<string, Type> UnknownFieldAction = (s, f) =>
{
throw new NotImplementedException($"FieldLoader: Missing field `{s}` on `{f.Name}`");
};
static readonly ConcurrentCache<Type, FieldLoadInfo[]> TypeLoadInfo =
new ConcurrentCache<Type, FieldLoadInfo[]>(BuildTypeLoadInfo);
new(BuildTypeLoadInfo);
static readonly ConcurrentCache<string, BooleanExpression> BooleanExpressionCache =
new ConcurrentCache<string, BooleanExpression>(expression => new BooleanExpression(expression));
new(expression => new BooleanExpression(expression));
static readonly ConcurrentCache<string, IntegerExpression> IntegerExpressionCache =
new ConcurrentCache<string, IntegerExpression>(expression => new IntegerExpression(expression));
new(expression => new IntegerExpression(expression));
static readonly Dictionary<Type, Func<string, Type, string, MemberInfo, object>> TypeParsers =
new Dictionary<Type, Func<string, Type, string, MemberInfo, object>>()
new()
{
{ typeof(int), ParseInt },
{ typeof(ushort), ParseUShort },
@@ -107,7 +103,7 @@ namespace OpenRA
};
static readonly Dictionary<Type, Func<string, Type, string, MiniYaml, MemberInfo, object>> GenericTypeParsers =
new Dictionary<Type, Func<string, Type, string, MiniYaml, MemberInfo, object>>()
new()
{
{ typeof(HashSet<>), ParseHashSetOrList },
{ typeof(List<>), ParseHashSetOrList },
@@ -116,10 +112,19 @@ namespace OpenRA
{ typeof(Nullable<>), ParseNullable },
};
static readonly object BoxedTrue = true;
static readonly object BoxedFalse = false;
static readonly object[] BoxedInts = Exts.MakeArray(33, i => (object)i);
static object ParseInt(string fieldName, Type fieldType, string value, MemberInfo field)
{
if (Exts.TryParseIntegerInvariant(value, out var res))
{
if (res >= 0 && res < BoxedInts.Length)
return BoxedInts[res];
return res;
}
return InvalidValueAction(value, fieldType, fieldName);
}
@@ -159,7 +164,7 @@ namespace OpenRA
static object ParseColor(string fieldName, Type fieldType, string value, MemberInfo field)
{
if (value != null && Color.TryParse(value, out var color))
return color;
return color;
return InvalidValueAction(value, fieldType, fieldName);
}
@@ -365,7 +370,7 @@ namespace OpenRA
static object ParseBool(string fieldName, Type fieldType, string value, MemberInfo field)
{
if (bool.TryParse(value.ToLowerInvariant(), out var result))
return result;
return result ? BoxedTrue : BoxedFalse;
return InvalidValueAction(value, fieldType, fieldName);
}
@@ -473,11 +478,11 @@ namespace OpenRA
static object ParseHashSetOrList(string fieldName, Type fieldType, string value, MiniYaml yaml, MemberInfo field)
{
var set = Activator.CreateInstance(fieldType);
if (value == null)
return set;
return Activator.CreateInstance(fieldType);
var parts = value.Split(SplitComma, StringSplitOptions.RemoveEmptyEntries);
var set = Activator.CreateInstance(fieldType, parts.Length);
var arguments = fieldType.GetGenericArguments();
var addMethod = fieldType.GetMethod(nameof(List<object>.Add), arguments);
var addArgs = new object[1];
@@ -492,7 +497,10 @@ namespace OpenRA
static object ParseDictionary(string fieldName, Type fieldType, string value, MiniYaml yaml, MemberInfo field)
{
var dict = Activator.CreateInstance(fieldType);
if (yaml == null)
return Activator.CreateInstance(fieldType);
var dict = Activator.CreateInstance(fieldType, yaml.Nodes.Count);
var arguments = fieldType.GetGenericArguments();
var addMethod = fieldType.GetMethod(nameof(Dictionary<object, object>.Add), arguments);
var addArgs = new object[2];
@@ -531,7 +539,7 @@ namespace OpenRA
public static void Load(object self, MiniYaml my)
{
var loadInfo = TypeLoadInfo[self.GetType()];
var missing = new List<string>();
List<string> missing = null;
Dictionary<string, MiniYaml> md = null;
@@ -539,14 +547,14 @@ namespace OpenRA
{
object val;
if (md == null)
md = my.ToDictionary();
md ??= my.ToDictionary();
if (fli.Loader != null)
{
if (!fli.Attribute.Required || md.ContainsKey(fli.YamlName))
val = fli.Loader(my);
else
{
missing ??= new List<string>();
missing.Add(fli.YamlName);
continue;
}
@@ -556,7 +564,11 @@ namespace OpenRA
if (!TryGetValueFromYaml(fli.YamlName, fli.Field, md, out val))
{
if (fli.Attribute.Required)
{
missing ??= new List<string>();
missing.Add(fli.YamlName);
}
continue;
}
}
@@ -564,7 +576,7 @@ namespace OpenRA
fli.Field.SetValue(self, val);
}
if (missing.Count > 0)
if (missing != null)
throw new MissingFieldsException(missing.ToArray());
}
@@ -625,12 +637,17 @@ namespace OpenRA
public static object GetValue(string fieldName, Type fieldType, string value, MemberInfo field)
{
return GetValue(fieldName, fieldType, new MiniYaml(value), field);
return GetValue(fieldName, fieldType, value, null, field);
}
public static object GetValue(string fieldName, Type fieldType, MiniYaml yaml, MemberInfo field)
{
var value = yaml.Value?.Trim();
return GetValue(fieldName, fieldType, yaml.Value, yaml, field);
}
static object GetValue(string fieldName, Type fieldType, string value, MiniYaml yaml, MemberInfo field)
{
value = value?.Trim();
if (fieldType.IsGenericType)
{
if (GenericTypeParsers.TryGetValue(fieldType.GetGenericTypeDefinition(), out var parseFuncGeneric))
@@ -754,7 +771,7 @@ namespace OpenRA
[AttributeUsage(AttributeTargets.Field)]
public class SerializeAttribute : Attribute
{
public static readonly SerializeAttribute Default = new SerializeAttribute(true);
public static readonly SerializeAttribute Default = new(true);
public bool IsDefault => this == Default;

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -31,7 +31,7 @@ namespace OpenRA.FileFormats
public Color[] Palette { get; }
public byte[] Data { get; }
public SpriteFrameType Type { get; }
public Dictionary<string, string> EmbeddedData = new Dictionary<string, string>();
public Dictionary<string, string> EmbeddedData = new();
public int PixelStride => Type == SpriteFrameType.Indexed8 ? 1 : Type == SpriteFrameType.Rgb24 ? 3 : 4;
@@ -67,7 +67,7 @@ namespace OpenRA.FileFormats
Height = IPAddress.NetworkToHostOrder(ms.ReadInt32());
var bitDepth = ms.ReadUInt8();
var colorType = (PngColorType)ms.ReadByte();
var colorType = (PngColorType)ms.ReadUInt8();
if (IsPaletted(bitDepth, colorType))
Type = SpriteFrameType.Indexed8;
else if (colorType == PngColorType.Color)
@@ -75,9 +75,9 @@ namespace OpenRA.FileFormats
Data = new byte[Width * Height * PixelStride];
var compression = ms.ReadByte();
/*var filter = */ms.ReadByte();
var interlace = ms.ReadByte();
var compression = ms.ReadUInt8();
/*var filter = */ms.ReadUInt8();
var interlace = ms.ReadUInt8();
if (compression != 0)
throw new InvalidDataException("Compression method not supported");
@@ -95,7 +95,7 @@ namespace OpenRA.FileFormats
Palette = new Color[256];
for (var i = 0; i < length / 3; i++)
{
var r = ms.ReadByte(); var g = ms.ReadByte(); var b = ms.ReadByte();
var r = ms.ReadUInt8(); var g = ms.ReadUInt8(); var b = ms.ReadUInt8();
Palette[i] = Color.FromArgb(r, g, b);
}
@@ -108,7 +108,7 @@ namespace OpenRA.FileFormats
throw new InvalidDataException("Non-Palette indexed PNG are not supported.");
for (var i = 0; i < length; i++)
Palette[i] = Color.FromArgb(ms.ReadByte(), Palette[i]);
Palette[i] = Color.FromArgb(ms.ReadUInt8(), Palette[i]);
break;
}
@@ -137,21 +137,56 @@ namespace OpenRA.FileFormats
var pxStride = PixelStride;
var rowStride = Width * pxStride;
var prevLine = new byte[rowStride];
Span<byte> prevLine = new byte[rowStride];
for (var y = 0; y < Height; y++)
{
var filter = (PngFilter)ds.ReadByte();
var line = ds.ReadBytes(rowStride);
var filter = (PngFilter)ds.ReadUInt8();
ds.ReadBytes(Data, y * rowStride, rowStride);
var line = Data.AsSpan(y * rowStride, rowStride);
for (var i = 0; i < rowStride; i++)
line[i] = i < pxStride
? UnapplyFilter(filter, line[i], 0, prevLine[i], 0)
: UnapplyFilter(filter, line[i], line[i - pxStride], prevLine[i], prevLine[i - pxStride]);
Array.Copy(line, 0, Data, y * rowStride, rowStride);
switch (filter)
{
case PngFilter.None:
break;
case PngFilter.Sub:
for (var i = pxStride; i < rowStride; i++)
line[i] += line[i - pxStride];
break;
case PngFilter.Up:
for (var i = 0; i < rowStride; i++)
line[i] += prevLine[i];
break;
case PngFilter.Average:
for (var i = 0; i < pxStride; i++)
line[i] += Average(0, prevLine[i]);
for (var i = pxStride; i < rowStride; i++)
line[i] += Average(line[i - pxStride], prevLine[i]);
break;
case PngFilter.Paeth:
for (var i = 0; i < pxStride; i++)
line[i] += Paeth(0, prevLine[i], 0);
for (var i = pxStride; i < rowStride; i++)
line[i] += Paeth(line[i - pxStride], prevLine[i], prevLine[i - pxStride]);
break;
default:
throw new InvalidOperationException("Unsupported Filter");
}
prevLine = line;
}
static byte Average(byte a, byte b) => (byte)((a + b) / 2);
static byte Paeth(byte a, byte b, byte c)
{
var p = a + b - c;
var pa = Math.Abs(p - a);
var pb = Math.Abs(p - b);
var pc = Math.Abs(p - c);
return (pa <= pb && pa <= pc) ? a :
(pb <= pc) ? b : c;
}
}
}
@@ -228,34 +263,9 @@ namespace OpenRA.FileFormats
return isPng;
}
static byte UnapplyFilter(PngFilter f, byte x, byte a, byte b, byte c)
{
switch (f)
{
case PngFilter.None: return x;
case PngFilter.Sub: return (byte)(x + a);
case PngFilter.Up: return (byte)(x + b);
case PngFilter.Average: return (byte)(x + (a + b) / 2);
case PngFilter.Paeth: return (byte)(x + Paeth(a, b, c));
default:
throw new InvalidOperationException("Unsupported Filter");
}
}
static byte Paeth(byte a, byte b, byte c)
{
var p = a + b - c;
var pa = Math.Abs(p - a);
var pb = Math.Abs(p - b);
var pc = Math.Abs(p - c);
return (pa <= pb && pa <= pc) ? a :
(pb <= pc) ? b : c;
}
[Flags]
enum PngColorType { Indexed = 1, Color = 2, Alpha = 4 }
enum PngFilter { None, Sub, Up, Average, Paeth }
enum PngColorType : byte { Indexed = 1, Color = 2, Alpha = 4 }
enum PngFilter : byte { None, Sub, Up, Average, Paeth }
static bool IsPaletted(byte bitDepth, PngColorType colorType)
{
@@ -271,7 +281,7 @@ namespace OpenRA.FileFormats
throw new InvalidDataException("Unknown pixel format");
}
void WritePngChunk(Stream output, string type, Stream input)
static void WritePngChunk(Stream output, string type, Stream input)
{
input.Position = 0;

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -29,16 +29,16 @@ namespace OpenRA.FileSystem
public class FileSystem : IReadOnlyFileSystem
{
public IEnumerable<IReadOnlyPackage> MountedPackages => mountedPackages.Keys;
readonly Dictionary<IReadOnlyPackage, int> mountedPackages = new Dictionary<IReadOnlyPackage, int>();
readonly Dictionary<string, IReadOnlyPackage> explicitMounts = new Dictionary<string, IReadOnlyPackage>();
readonly Dictionary<IReadOnlyPackage, int> mountedPackages = new();
readonly Dictionary<string, IReadOnlyPackage> explicitMounts = new();
readonly string modID;
// Mod packages that should not be disposed
readonly List<IReadOnlyPackage> modPackages = new List<IReadOnlyPackage>();
readonly List<IReadOnlyPackage> modPackages = new();
readonly IReadOnlyDictionary<string, Manifest> installedMods;
readonly IPackageLoader[] packageLoaders;
Cache<string, List<IReadOnlyPackage>> fileIndex = new Cache<string, List<IReadOnlyPackage>>(_ => new List<IReadOnlyPackage>());
Cache<string, List<IReadOnlyPackage>> fileIndex = new(_ => new List<IReadOnlyPackage>());
public FileSystem(string modID, IReadOnlyDictionary<string, Manifest> installedMods, IPackageLoader[] packageLoaders)
{
@@ -85,14 +85,14 @@ namespace OpenRA.FileSystem
{
var optional = name.StartsWith("~", StringComparison.Ordinal);
if (optional)
name = name.Substring(1);
name = name[1..];
try
{
IReadOnlyPackage package;
if (name.StartsWith("$", StringComparison.Ordinal))
{
name = name.Substring(1);
name = name[1..];
if (!installedMods.TryGetValue(name, out var mod))
throw new InvalidOperationException($"Could not load mod '{name}'. Available mods: {installedMods.Keys.JoinWith(", ")}");
@@ -211,9 +211,9 @@ namespace OpenRA.FileSystem
public bool TryGetPackageContaining(string path, out IReadOnlyPackage package, out string filename)
{
var explicitSplit = path.IndexOf('|');
if (explicitSplit > 0 && explicitMounts.TryGetValue(path.Substring(0, explicitSplit), out package))
if (explicitSplit > 0 && explicitMounts.TryGetValue(path[..explicitSplit], out package))
{
filename = path.Substring(explicitSplit + 1);
filename = path[(explicitSplit + 1)..];
return true;
}
@@ -228,9 +228,9 @@ namespace OpenRA.FileSystem
var explicitSplit = filename.IndexOf('|');
if (explicitSplit > 0)
{
if (explicitMounts.TryGetValue(filename.Substring(0, explicitSplit), out var explicitPackage))
if (explicitMounts.TryGetValue(filename[..explicitSplit], out var explicitPackage))
{
s = explicitPackage.GetStream(filename.Substring(explicitSplit + 1));
s = explicitPackage.GetStream(filename[(explicitSplit + 1)..]);
if (s != null)
return true;
}
@@ -263,15 +263,15 @@ namespace OpenRA.FileSystem
{
var explicitSplit = filename.IndexOf('|');
if (explicitSplit > 0)
if (explicitMounts.TryGetValue(filename.Substring(0, explicitSplit), out var explicitPackage))
if (explicitPackage.Contains(filename.Substring(explicitSplit + 1)))
if (explicitMounts.TryGetValue(filename[..explicitSplit], out var explicitPackage))
if (explicitPackage.Contains(filename[(explicitSplit + 1)..]))
return true;
return fileIndex.ContainsKey(filename);
}
/// <summary>
/// Returns true if the given filename references an external mod via an explicit mount
/// Returns true if the given filename references an external mod via an explicit mount.
/// </summary>
public bool IsExternalModFile(string filename)
{
@@ -279,7 +279,7 @@ namespace OpenRA.FileSystem
if (explicitSplit < 0)
return false;
if (!explicitMounts.TryGetValue(filename.Substring(0, explicitSplit), out var explicitPackage))
if (!explicitMounts.TryGetValue(filename[..explicitSplit], out var explicitPackage))
return false;
if (installedMods[modID].Package == explicitPackage)
@@ -297,8 +297,8 @@ namespace OpenRA.FileSystem
var explicitSplit = path.IndexOf('|');
if (explicitSplit > 0 && !path.StartsWith("^"))
{
var parent = path.Substring(0, explicitSplit);
var filename = path.Substring(explicitSplit + 1);
var parent = path[..explicitSplit];
var filename = path[(explicitSplit + 1)..];
var parentPath = manifest.Packages.FirstOrDefault(kv => kv.Value == parent).Key;
if (parentPath == null)
@@ -306,10 +306,10 @@ namespace OpenRA.FileSystem
if (parentPath.StartsWith("$", StringComparison.Ordinal))
{
if (!installedMods.TryGetValue(parentPath.Substring(1), out var mod))
if (!installedMods.TryGetValue(parentPath[1..], out var mod))
return null;
if (!(mod.Package is Folder))
if (mod.Package is not Folder)
return null;
path = Path.Combine(mod.Package.Name, filename);
@@ -322,6 +322,28 @@ namespace OpenRA.FileSystem
return File.Exists(resolvedPath) ? resolvedPath : null;
}
public static string ResolveCaseInsensitivePath(string path)
{
var resolved = Path.GetPathRoot(path);
if (resolved == null)
return null;
foreach (var name in path[resolved.Length..].Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar))
{
// Filter out paths of the form /foo/bar/./baz
if (name == ".")
continue;
resolved = Directory.GetFileSystemEntries(resolved).FirstOrDefault(e => e.Equals(Path.Combine(resolved, name), StringComparison.InvariantCultureIgnoreCase));
if (resolved == null)
return null;
}
return resolved;
}
public string GetPrefix(IReadOnlyPackage package)
{
return explicitMounts.ContainsValue(package) ? explicitMounts.First(f => f.Value == package).Key : null;

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -18,24 +18,22 @@ namespace OpenRA.FileSystem
{
public sealed class Folder : IReadWritePackage
{
readonly string path;
public string Name { get; }
public Folder(string path)
{
this.path = path;
Name = path;
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
}
public string Name => path;
public IEnumerable<string> Contents
{
get
{
// Order may vary on different file systems and it matters for hashing.
return Directory.GetFiles(path, "*", SearchOption.TopDirectoryOnly)
.Concat(Directory.GetDirectories(path))
return Directory.GetFiles(Name, "*", SearchOption.TopDirectoryOnly)
.Concat(Directory.GetDirectories(Name))
.Select(Path.GetFileName)
.OrderBy(f => f);
}
@@ -43,14 +41,14 @@ namespace OpenRA.FileSystem
public Stream GetStream(string filename)
{
try { return File.OpenRead(Path.Combine(path, filename)); }
try { return File.OpenRead(Path.Combine(Name, filename)); }
catch { return null; }
}
public bool Contains(string filename)
{
var combined = Path.Combine(path, filename);
return combined.StartsWith(path, StringComparison.Ordinal) && File.Exists(combined);
var combined = Path.Combine(Name, filename);
return combined.StartsWith(Name, StringComparison.Ordinal) && File.Exists(combined);
}
public IReadOnlyPackage OpenPackage(string filename, FileSystem context)
@@ -82,7 +80,7 @@ namespace OpenRA.FileSystem
// in FileSystem.OpenPackage. Their internal name therefore contains the
// full parent path too. We need to be careful to not add a second path
// prefix to these hacked packages.
var filePath = filename.StartsWith(path) ? filename : Path.Combine(path, filename);
var filePath = filename.StartsWith(Name) ? filename : Path.Combine(Name, filename);
Directory.CreateDirectory(Path.GetDirectoryName(filePath));
using (var s = File.Create(filePath))
@@ -96,7 +94,7 @@ namespace OpenRA.FileSystem
// in FileSystem.OpenPackage. Their internal name therefore contains the
// full parent path too. We need to be careful to not add a second path
// prefix to these hacked packages.
var filePath = filename.StartsWith(path) ? filename : Path.Combine(path, filename);
var filePath = filename.StartsWith(Name) ? filename : Path.Combine(Name, filename);
if (Directory.Exists(filePath))
Directory.Delete(filePath, true);
else if (File.Exists(filePath))

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -19,7 +19,7 @@ namespace OpenRA.FileSystem
{
public class ZipFileLoader : IPackageLoader
{
static readonly string[] Extensions = { ".zip", ".oramap" };
const uint ZipSignature = 0x04034b50;
class ReadOnlyZipFile : IReadOnlyPackage
{
@@ -55,7 +55,8 @@ namespace OpenRA.FileSystem
get
{
foreach (ZipEntry entry in pkg)
yield return entry.Name;
if (entry.IsFile)
yield return entry.Name;
}
}
@@ -94,7 +95,7 @@ namespace OpenRA.FileSystem
sealed class ReadWriteZipFile : ReadOnlyZipFile, IReadWritePackage
{
readonly MemoryStream pkgStream = new MemoryStream();
readonly MemoryStream pkgStream = new();
public ReadWriteZipFile(string filename, bool create = false)
{
@@ -141,23 +142,22 @@ namespace OpenRA.FileSystem
sealed class ZipFolder : IReadOnlyPackage
{
public string Name => path;
public string Name { get; }
public ReadOnlyZipFile Parent { get; }
readonly string path;
public ZipFolder(ReadOnlyZipFile parent, string path)
{
if (path.EndsWith("/", StringComparison.Ordinal))
path = path.Substring(0, path.Length - 1);
path = path[..^1];
Name = path;
Parent = parent;
this.path = path;
}
public Stream GetStream(string filename)
{
// Zip files use '/' as a path separator
return Parent.GetStream(path + '/' + filename);
return Parent.GetStream(Name + '/' + filename);
}
public IEnumerable<string> Contents
@@ -166,9 +166,9 @@ namespace OpenRA.FileSystem
{
foreach (var entry in Parent.Contents)
{
if (entry.StartsWith(path, StringComparison.Ordinal) && entry != path)
if (entry.StartsWith(Name, StringComparison.Ordinal) && entry != Name)
{
var filename = entry.Substring(path.Length + 1);
var filename = entry[(Name.Length + 1)..];
var dirLevels = filename.Split('/').Count(c => !string.IsNullOrEmpty(c));
if (dirLevels == 1)
yield return filename;
@@ -179,18 +179,18 @@ namespace OpenRA.FileSystem
public bool Contains(string filename)
{
return Parent.Contains(path + '/' + filename);
return Parent.Contains(Name + '/' + filename);
}
public IReadOnlyPackage OpenPackage(string filename, FileSystem context)
{
return Parent.OpenPackage(path + '/' + filename, context);
return Parent.OpenPackage(Name + '/' + filename, context);
}
public void Dispose() { /* nothing to do */ }
}
class StaticStreamDataSource : IStaticDataSource
sealed class StaticStreamDataSource : IStaticDataSource
{
readonly Stream s;
public StaticStreamDataSource(Stream s)
@@ -206,7 +206,10 @@ namespace OpenRA.FileSystem
public bool TryParsePackage(Stream s, string filename, FileSystem context, out IReadOnlyPackage package)
{
if (!Extensions.Any(e => filename.EndsWith(e, StringComparison.InvariantCultureIgnoreCase)))
var readSignature = s.ReadUInt32();
s.Position -= 4;
if (readSignature != ZipSignature)
{
package = null;
return false;
@@ -218,10 +221,13 @@ namespace OpenRA.FileSystem
public static bool TryParseReadWritePackage(string filename, out IReadWritePackage package)
{
if (!Extensions.Any(e => filename.EndsWith(e, StringComparison.InvariantCultureIgnoreCase)))
using (var s = File.OpenRead(filename))
{
package = null;
return false;
if (s.ReadUInt32() != ZipSignature)
{
package = null;
return false;
}
}
package = new ReadWriteZipFile(filename);

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -29,6 +29,9 @@ namespace OpenRA
{
public static class Game
{
[TranslationReference("filename")]
const string SavedScreenshot = "notification-saved-screenshot";
public const int TimestepJankThreshold = 250; // Don't catch up for delays larger than 250ms
public static InstalledMods Mods { get; private set; }
@@ -45,7 +48,7 @@ namespace OpenRA
internal static OrderManager OrderManager;
static Server.Server server;
public static MersenneTwister CosmeticRandom = new MersenneTwister(); // not synced
public static MersenneTwister CosmeticRandom = new(); // not synced
public static Renderer Renderer;
public static Sound Sound;
@@ -62,7 +65,7 @@ namespace OpenRA
{
var newConnection = new NetworkConnection(endpoint);
if (recordReplay)
newConnection.StartRecording(() => { return TimestampedFilename(); });
newConnection.StartRecording(() => TimestampedFilename());
var om = new OrderManager(newConnection);
JoinInner(om);
@@ -184,13 +187,8 @@ namespace OpenRA
Cursor.SetCursor(null);
BeforeGameStart();
Map map;
using (new PerfTimer("PrepareMap"))
map = ModData.PrepareMap(mapUID);
using (new PerfTimer("NewWorld"))
OrderManager.World = new World(ModData, map, OrderManager, type);
OrderManager.World = new World(mapUID, ModData, OrderManager, type);
OrderManager.World.GameOver += FinishBenchmark;
@@ -267,15 +265,14 @@ namespace OpenRA
{
OrderManager om = null;
Action lobbyReady = null;
lobbyReady = () =>
void LobbyReady()
{
LobbyInfoChanged -= lobbyReady;
LobbyInfoChanged -= LobbyReady;
foreach (var o in setupOrders)
om.IssueOrder(o);
};
}
LobbyInfoChanged += lobbyReady;
LobbyInfoChanged += LobbyReady;
om = JoinServer(CreateLocalServer(mapUID), "");
}
@@ -318,7 +315,7 @@ namespace OpenRA
if (!string.IsNullOrEmpty(supportDirArg))
Platform.OverrideSupportDir(supportDirArg);
Console.WriteLine($"Platform is {Platform.CurrentPlatform}");
Console.WriteLine($"Platform is {Platform.CurrentPlatform} ({Platform.CurrentArchitecture})");
// Load the engine version as early as possible so it can be written to exception logs
try
@@ -419,7 +416,7 @@ namespace OpenRA
// Sanitize input from platform-specific launchers
// Process.Start requires paths to not be quoted, even if they contain spaces
if (launchPath != null && launchPath.First() == '"' && launchPath.Last() == '"')
launchPath = launchPath.Substring(1, launchPath.Length - 2);
launchPath = launchPath[1..^1];
// Metadata registration requires an explicit launch path
if (launchPath != null)
@@ -566,14 +563,11 @@ namespace OpenRA
// Note: These delayed actions should only be used by widgets or disposing objects
// - things that depend on a particular world should be queuing them on the world actor.
static volatile ActionQueue delayedActions = new ActionQueue();
static volatile ActionQueue delayedActions = new();
public static void RunAfterTick(Action a) { delayedActions.Add(a, RunTime); }
public static void RunAfterDelay(int delayMilliseconds, Action a) { delayedActions.Add(a, RunTime + delayMilliseconds); }
[TranslationReference("filename")]
static readonly string SavedScreenshot = "saved-screenshot";
static void TakeScreenshotInner()
{
using (new PerfTimer("Renderer.SaveScreenshot"))
@@ -587,7 +581,7 @@ namespace OpenRA
Log.Write("debug", "Taking screenshot " + path);
Renderer.SaveScreenshot(path);
TextNotificationsManager.Debug(ModData.Translation.GetString(SavedScreenshot, Translation.Arguments("filename", filename)));
TextNotificationsManager.Debug(TranslationProvider.GetString(SavedScreenshot, Translation.Arguments("filename", filename)));
}
}
@@ -619,10 +613,7 @@ namespace OpenRA
if (orderManager.TryTick())
{
Sync.RunUnsynced(world, () =>
{
world.OrderGenerator.Tick(world);
});
Sync.RunUnsynced(world, () => world.OrderGenerator.Tick(world));
world.Tick();

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -36,7 +36,7 @@ namespace OpenRA
public TimeSpan Duration => EndTimeUtc > StartTimeUtc ? EndTimeUtc - StartTimeUtc : TimeSpan.Zero;
public IList<Player> Players { get; }
public HashSet<int> DisabledSpawnPoints = new HashSet<int>();
public HashSet<int> DisabledSpawnPoints = new();
public MapPreview MapPreview => Game.ModData.MapCache[MapUid];
public IEnumerable<Player> HumanPlayers { get { return Players.Where(p => p.IsHuman); } }
public bool IsSinglePlayer => HumanPlayers.Count() == 1;

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -32,7 +32,7 @@ namespace OpenRA
/// You can remove inherited traits by adding a - in front of them as in -TraitName: to inherit everything, but this trait.
/// </summary>
public readonly string Name;
readonly TypeDictionary traits = new TypeDictionary();
readonly TypeDictionary traits = new();
List<TraitInfo> constructOrderCache = null;
public ActorInfo(ObjectCreator creator, string name, MiniYaml node)
@@ -115,39 +115,46 @@ namespace OpenRA
}).ToList();
var resolved = source.Where(s => s.Dependencies.Count == 0 && s.OptionalDependencies.Count == 0).ToList();
var unresolved = source.Except(resolved);
var unresolved = source.ToHashSet();
unresolved.ExceptWith(resolved);
var testResolve = new Func<Type, Type, bool>((a, b) => a == b || a.IsAssignableFrom(b));
static bool AreResolvable(Type a, Type b) => a.IsAssignableFrom(b);
// This query detects which unresolved traits can be immediately resolved as all their direct dependencies are met.
var more = unresolved.Where(u =>
u.Dependencies.All(d => // To be resolvable, all dependencies must be satisfied according to the following conditions:
resolved.Exists(r => testResolve(d, r.Type)) && // There must exist a resolved trait that meets the dependency.
!unresolved.Any(u1 => testResolve(d, u1.Type))) && // All matching traits that meet this dependency must be resolved first.
resolved.Exists(r => AreResolvable(d, r.Type)) && // There must exist a resolved trait that meets the dependency.
!unresolved.Any(u1 => AreResolvable(d, u1.Type))) && // All matching traits that meet this dependency must be resolved first.
u.OptionalDependencies.All(d => // To be resolvable, all optional dependencies must be satisfied according to the following condition:
!unresolved.Any(u1 => testResolve(d, u1.Type)))); // All matching traits that meet this optional dependencies must be resolved first.
!unresolved.Any(u1 => AreResolvable(d, u1.Type)))); // All matching traits that meet this optional dependencies must be resolved first.
// Continue resolving traits as long as possible.
// Each time we resolve some traits, this means dependencies for other traits may then be possible to satisfy in the next pass.
while (more.Any())
resolved.AddRange(more);
if (unresolved.Any())
var readyToResolve = more.ToList();
while (readyToResolve.Count != 0)
{
var exceptionString = "ActorInfo(\"" + Name + "\") failed to initialize because of the following:\r\n";
var missing = unresolved.SelectMany(u => u.Dependencies.Where(d => !source.Any(s => testResolve(d, s.Type)))).Distinct();
resolved.AddRange(readyToResolve);
unresolved.ExceptWith(readyToResolve);
readyToResolve.Clear();
readyToResolve.AddRange(more);
}
exceptionString += "Missing:\r\n";
if (unresolved.Count != 0)
{
var exceptionString = "ActorInfo(\"" + Name + "\") failed to initialize because of the following:\n";
var missing = unresolved.SelectMany(u => u.Dependencies.Where(d => !source.Any(s => AreResolvable(d, s.Type)))).Distinct();
exceptionString += "Missing:\n";
foreach (var m in missing)
exceptionString += m + " \r\n";
exceptionString += m + " \n";
exceptionString += "Unresolved:\r\n";
exceptionString += "Unresolved:\n";
foreach (var u in unresolved)
{
var deps = u.Dependencies.Where(d => !resolved.Exists(r => r.Type == d));
var optDeps = u.OptionalDependencies.Where(d => !resolved.Exists(r => r.Type == d));
var allDeps = string.Join(", ", deps.Select(o => o.ToString()).Concat(optDeps.Select(o => $"[{o}]")));
exceptionString += $"{u.Type}: {{ {allDeps} }}\r\n";
exceptionString += $"{u.Type}: {{ {allDeps} }}\n";
}
throw new YamlException(exceptionString);

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -28,14 +28,14 @@ namespace OpenRA.GameRules
Title = value.Value;
var nd = value.ToDictionary();
if (nd.ContainsKey("Hidden"))
bool.TryParse(nd["Hidden"].Value, out Hidden);
if (nd.TryGetValue("Hidden", out var yaml))
bool.TryParse(yaml.Value, out Hidden);
if (nd.ContainsKey("VolumeModifier"))
VolumeModifier = FieldLoader.GetValue<float>("VolumeModifier", nd["VolumeModifier"].Value);
if (nd.TryGetValue("VolumeModifier", out yaml))
VolumeModifier = FieldLoader.GetValue<float>("VolumeModifier", yaml.Value);
var ext = nd.ContainsKey("Extension") ? nd["Extension"].Value : "aud";
Filename = (nd.ContainsKey("Filename") ? nd["Filename"].Value : key) + "." + ext;
var ext = nd.TryGetValue("Extension", out yaml) ? yaml.Value : "aud";
Filename = (nd.TryGetValue("Filename", out yaml) ? yaml.Value : key) + "." + ext;
}
public void Load(IReadOnlyFileSystem fileSystem)

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -15,7 +15,6 @@ using System.Linq;
using System.Threading.Tasks;
using OpenRA.FileSystem;
using OpenRA.GameRules;
using OpenRA.Graphics;
using OpenRA.Traits;
namespace OpenRA
@@ -28,7 +27,6 @@ namespace OpenRA
public readonly IReadOnlyDictionary<string, SoundInfo> Notifications;
public readonly IReadOnlyDictionary<string, MusicInfo> Music;
public readonly ITerrainInfo TerrainInfo;
public readonly SequenceProvider Sequences;
public readonly IReadOnlyDictionary<string, MiniYamlNode> ModelSequences;
public Ruleset(
@@ -38,7 +36,6 @@ namespace OpenRA
IReadOnlyDictionary<string, SoundInfo> notifications,
IReadOnlyDictionary<string, MusicInfo> music,
ITerrainInfo terrainInfo,
SequenceProvider sequences,
IReadOnlyDictionary<string, MiniYamlNode> modelSequences)
{
Actors = new ActorInfoDictionary(actors);
@@ -47,7 +44,6 @@ namespace OpenRA
Notifications = notifications;
Music = music;
TerrainInfo = terrainInfo;
Sequences = sequences;
ModelSequences = modelSequences;
foreach (var a in Actors.Values)
@@ -124,7 +120,7 @@ namespace OpenRA
var fs = modData.DefaultFileSystem;
Ruleset ruleset = null;
Action f = () =>
void LoadRuleset()
{
var actors = MergeOrDefault("Manifest,Rules", fs, m.Rules, null, null,
k => new ActorInfo(modData.ObjectCreator, k.Key.ToLowerInvariant(), k.Value),
@@ -145,15 +141,15 @@ namespace OpenRA
var modelSequences = MergeOrDefault("Manifest,ModelSequences", fs, m.ModelSequences, null, null,
k => k);
// The default ruleset does not include a preferred tileset or sequence set
ruleset = new Ruleset(actors, weapons, voices, notifications, music, null, null, modelSequences);
};
// The default ruleset does not include a preferred tileset
ruleset = new Ruleset(actors, weapons, voices, notifications, music, null, modelSequences);
}
if (modData.IsOnMainThread)
{
modData.HandleLoadingProgress();
var loader = new Task(f);
var loader = new Task(LoadRuleset);
loader.Start();
// Animate the loadscreen while we wait
@@ -161,7 +157,7 @@ namespace OpenRA
modData.HandleLoadingProgress();
}
else
f();
LoadRuleset();
return ruleset;
}
@@ -170,20 +166,19 @@ namespace OpenRA
{
var dr = modData.DefaultRules;
var terrainInfo = modData.DefaultTerrainInfo[tileSet];
var sequences = modData.DefaultSequences[tileSet];
return new Ruleset(dr.Actors, dr.Weapons, dr.Voices, dr.Notifications, dr.Music, terrainInfo, sequences, dr.ModelSequences);
return new Ruleset(dr.Actors, dr.Weapons, dr.Voices, dr.Notifications, dr.Music, terrainInfo, dr.ModelSequences);
}
public static Ruleset Load(ModData modData, IReadOnlyFileSystem fileSystem, string tileSet,
MiniYaml mapRules, MiniYaml mapWeapons, MiniYaml mapVoices, MiniYaml mapNotifications,
MiniYaml mapMusic, MiniYaml mapSequences, MiniYaml mapModelSequences)
MiniYaml mapMusic, MiniYaml mapModelSequences)
{
var m = modData.Manifest;
var dr = modData.DefaultRules;
Ruleset ruleset = null;
Action f = () =>
void LoadRuleset()
{
var actors = MergeOrDefault("Rules", fileSystem, m.Rules, mapRules, dr.Actors,
k => new ActorInfo(modData.ObjectCreator, k.Key.ToLowerInvariant(), k.Value),
@@ -204,23 +199,19 @@ namespace OpenRA
// TODO: Add support for merging custom terrain modifications
var terrainInfo = modData.DefaultTerrainInfo[tileSet];
// TODO: Top-level dictionary should be moved into the Ruleset instead of in its own object
var sequences = mapSequences == null ? modData.DefaultSequences[tileSet] :
new SequenceProvider(fileSystem, modData, tileSet, mapSequences);
var modelSequences = dr.ModelSequences;
if (mapModelSequences != null)
modelSequences = MergeOrDefault("ModelSequences", fileSystem, m.ModelSequences, mapModelSequences, dr.ModelSequences,
k => k);
ruleset = new Ruleset(actors, weapons, voices, notifications, music, terrainInfo, sequences, modelSequences);
};
ruleset = new Ruleset(actors, weapons, voices, notifications, music, terrainInfo, modelSequences);
}
if (modData.IsOnMainThread)
{
modData.HandleLoadingProgress();
var loader = new Task(f);
var loader = new Task(LoadRuleset);
loader.Start();
// Animate the loadscreen while we wait
@@ -228,7 +219,7 @@ namespace OpenRA
modData.HandleLoadingProgress();
}
else
f();
LoadRuleset();
return ruleset;
}
@@ -253,7 +244,7 @@ namespace OpenRA
}
catch (Exception ex)
{
Log.Write("debug", "Error in AnyFlaggedTraits\r\n" + ex.ToString());
Log.Write("debug", "Error in AnyFlaggedTraits\n" + ex.ToString());
}
}
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -17,14 +17,14 @@ namespace OpenRA.GameRules
{
public class SoundInfo
{
public readonly Dictionary<string, string[]> Variants = new Dictionary<string, string[]>();
public readonly Dictionary<string, string[]> Prefixes = new Dictionary<string, string[]>();
public readonly Dictionary<string, string[]> Voices = new Dictionary<string, string[]>();
public readonly Dictionary<string, string[]> Notifications = new Dictionary<string, string[]>();
public readonly Dictionary<string, string[]> Variants = new();
public readonly Dictionary<string, string[]> Prefixes = new();
public readonly Dictionary<string, string[]> Voices = new();
public readonly Dictionary<string, string[]> Notifications = new();
public readonly string DefaultVariant = ".aud";
public readonly string DefaultPrefix = "";
public readonly HashSet<string> DisableVariants = new HashSet<string>();
public readonly HashSet<string> DisablePrefixes = new HashSet<string>();
public readonly HashSet<string> DisableVariants = new();
public readonly HashSet<string> DisablePrefixes = new();
public readonly Lazy<Dictionary<string, SoundPool>> VoicePools;
public readonly Lazy<Dictionary<string, SoundPool>> NotificationsPools;
@@ -69,7 +69,7 @@ namespace OpenRA.GameRules
public readonly float VolumeModifier;
public readonly InterruptType Type;
readonly string[] clips;
readonly List<string> liveclips = new List<string>();
readonly List<string> liveclips = new();
public SoundPool(float volumeModifier, InterruptType interruptType, params string[] clips)
{

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -99,17 +99,20 @@ namespace OpenRA.GameRules
[Desc("Number of shots in a single ammo magazine.")]
public readonly int Burst = 1;
[Desc("Can this weapon target the attacker itself?")]
public readonly bool CanTargetSelf = false;
[Desc("What types of targets are affected.")]
public readonly BitSet<TargetableType> ValidTargets = new BitSet<TargetableType>("Ground", "Water");
public readonly BitSet<TargetableType> ValidTargets = new("Ground", "Water");
[Desc("What types of targets are unaffected.", "Overrules ValidTargets.")]
public readonly BitSet<TargetableType> InvalidTargets;
static readonly BitSet<TargetableType> TargetTypeAir = new BitSet<TargetableType>("Air");
static readonly BitSet<TargetableType> TargetTypeAir = new("Air");
[Desc("If weapon is not directly targeting an actor and targeted position is above this altitude,",
"the weapon will ignore terrain target types and only check TargetTypeAir for validity.")]
public readonly WDist AirThreshold = new WDist(128);
public readonly WDist AirThreshold = new(128);
[Desc("Delay in ticks between firing shots from the same ammo magazine. If one entry, it will be used for all bursts.",
"If multiple entries, their number needs to match Burst - 1.")]
@@ -125,10 +128,10 @@ namespace OpenRA.GameRules
public readonly IProjectileInfo Projectile;
[FieldLoader.LoadUsing(nameof(LoadWarheads))]
public readonly List<IWarhead> Warheads = new List<IWarhead>();
public readonly List<IWarhead> Warheads = new();
/// <summary>
/// This constructor is used solely for documentation generation!
/// This constructor is used solely for documentation generation.
/// </summary>
public WeaponInfo() { }
@@ -206,29 +209,24 @@ namespace OpenRA.GameRules
/// <summary>Checks if the weapon is valid against (can target) the actor.</summary>
public bool IsValidAgainst(Actor victim, Actor firedBy)
{
var targetTypes = victim.GetEnabledTargetTypes();
if (!IsValidTarget(targetTypes))
if (!CanTargetSelf && victim == firedBy)
return false;
// PERF: Avoid LINQ.
foreach (var warhead in Warheads)
if (warhead.IsValidAgainst(victim, firedBy))
return true;
var targetTypes = victim.GetEnabledTargetTypes();
return false;
return IsValidTarget(targetTypes);
}
/// <summary>Checks if the weapon is valid against (can target) the frozen actor.</summary>
public bool IsValidAgainst(FrozenActor victim, Actor firedBy)
{
if (!IsValidTarget(victim.TargetTypes))
if (!victim.IsValid)
return false;
if (!Warheads.Any(w => w.IsValidAgainst(victim, firedBy)))
if (!CanTargetSelf && victim.Actor == firedBy)
return false;
return true;
return IsValidTarget(victim.TargetTypes);
}
/// <summary>Applies all the weapon's warheads to the target.</summary>

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -16,6 +16,7 @@ namespace OpenRA
{
public class GameSpeed
{
[TranslationReference]
[FieldLoader.Require]
public readonly string Name;

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -22,7 +22,7 @@ namespace OpenRA.Graphics
public string Name { get; private set; }
public bool IsDecoration { get; set; }
readonly SequenceProvider sequenceProvider;
readonly SequenceSet sequences;
readonly Func<WAngle> facingFunc;
readonly Func<bool> paused;
@@ -30,7 +30,7 @@ namespace OpenRA.Graphics
bool backwards;
bool tickAlways;
int timeUntilNextFrame;
Action tickFunc = () => { };
Action tickFunc;
public Animation(World world, string name)
: this(world, name, () => WAngle.Zero) { }
@@ -43,7 +43,7 @@ namespace OpenRA.Graphics
public Animation(World world, string name, Func<WAngle> facingFunc, Func<bool> paused)
{
sequenceProvider = world.Map.Rules.Sequences;
sequences = world.Map.Sequences;
Name = name.ToLowerInvariant();
this.facingFunc = facingFunc;
this.paused = paused;
@@ -61,9 +61,9 @@ namespace OpenRA.Graphics
var imageRenderable = new SpriteRenderable(image, pos, offset, CurrentSequence.ZOffset + zOffset, palette, CurrentSequence.Scale, alpha, float3.Ones, tintModifiers, IsDecoration,
rotation);
if (CurrentSequence.ShadowStart >= 0)
var shadow = CurrentSequence.GetShadow(CurrentFrame, facingFunc());
if (shadow != null)
{
var shadow = CurrentSequence.GetShadow(CurrentFrame, facingFunc());
var shadowRenderable = new SpriteRenderable(shadow, pos, offset, CurrentSequence.ShadowZOffset + zOffset, palette, CurrentSequence.Scale, 1f, float3.Ones, tintModifiers,
true, rotation);
return new IRenderable[] { shadowRenderable, imageRenderable };
@@ -80,9 +80,9 @@ namespace OpenRA.Graphics
var alpha = CurrentSequence.GetAlpha(CurrentFrame);
var imageRenderable = new UISpriteRenderable(Image, WPos.Zero + offset, imagePos, CurrentSequence.ZOffset + zOffset, palette, scale, alpha, rotation);
if (CurrentSequence.ShadowStart >= 0)
var shadow = CurrentSequence.GetShadow(CurrentFrame, facingFunc());
if (shadow != null)
{
var shadow = CurrentSequence.GetShadow(CurrentFrame, facingFunc());
var shadowPos = pos - new int2((int)(scale * shadow.Size.X / 2), (int)(scale * shadow.Size.Y / 2));
var shadowRenderable = new UISpriteRenderable(shadow, WPos.Zero + offset, shadowPos, CurrentSequence.ShadowZOffset + zOffset, palette, scale, 1f, rotation);
return new IRenderable[] { shadowRenderable, imageRenderable };
@@ -164,7 +164,7 @@ namespace OpenRA.Graphics
if (frame >= CurrentSequence.Length)
{
frame = CurrentSequence.Length - 1;
tickFunc = () => { };
tickFunc = null;
after?.Invoke();
}
};
@@ -212,13 +212,13 @@ namespace OpenRA.Graphics
public void Tick(int t)
{
if (tickAlways)
tickFunc();
tickFunc?.Invoke();
else
{
timeUntilNextFrame -= t;
while (timeUntilNextFrame <= 0)
{
tickFunc();
tickFunc?.Invoke();
timeUntilNextFrame += CurrentSequenceTickOrDefault();
}
}
@@ -236,11 +236,11 @@ namespace OpenRA.Graphics
}
}
public bool HasSequence(string seq) { return sequenceProvider.HasSequence(Name, seq); }
public bool HasSequence(string seq) { return sequences.HasSequence(Name, seq); }
public ISpriteSequence GetSequence(string sequenceName)
{
return sequenceProvider.GetSequence(Name, sequenceName);
return sequences.GetSequence(Name, sequenceName);
}
public string GetRandomExistingSequence(string[] sequences, MersenneTwister random)

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -49,7 +49,7 @@ namespace OpenRA.Graphics
public readonly int[] PanelRegion = null;
public readonly PanelSides PanelSides = PanelSides.All;
public readonly Dictionary<string, Rectangle> Regions = new Dictionary<string, Rectangle>();
public readonly Dictionary<string, Rectangle> Regions = new();
}
public static IReadOnlyDictionary<string, Collection> Collections => collections;
@@ -100,9 +100,7 @@ namespace OpenRA.Graphics
static void LoadCollection(string name, MiniYaml yaml)
{
if (Game.ModData.LoadScreen != null)
Game.ModData.LoadScreen.Display();
Game.ModData.LoadScreen?.Display();
collections.Add(name, FieldLoader.Load<Collection>(yaml));
}
@@ -265,13 +263,13 @@ namespace OpenRA.Graphics
if (!collections.TryGetValue(collectionName, out var collection))
{
Log.Write("debug", "Could not find collection '{0}'", collectionName);
Log.Write("debug", $"Could not find collection '{collectionName}'");
return new Size(0, 0);
}
if (collection.PanelRegion == null || collection.PanelRegion.Length != 8)
{
Log.Write("debug", "Collection '{0}' does not define a valid PanelRegion", collectionName);
Log.Write("debug", $"Collection '{collectionName}' does not define a valid PanelRegion");
return new Size(0, 0);
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -17,7 +17,7 @@ namespace OpenRA.Graphics
{
public sealed class CursorManager
{
class Cursor
sealed class Cursor
{
public string Name;
public int2 PaddedSize;
@@ -28,7 +28,7 @@ namespace OpenRA.Graphics
public IHardwareCursor[] Cursors;
}
readonly Dictionary<string, Cursor> cursors = new Dictionary<string, Cursor>();
readonly Dictionary<string, Cursor> cursors = new();
readonly SheetBuilder sheetBuilder;
readonly GraphicSettings graphicSettings;
@@ -111,8 +111,7 @@ namespace OpenRA.Graphics
var template = kv.Value;
for (var i = 0; i < template.Sprites.Length; i++)
{
if (template.Cursors[i] != null)
template.Cursors[i].Dispose();
template.Cursors[i]?.Dispose();
// Calculate the padding to position the frame within sequenceBounds
var paddingTL = -(template.Bounds.Location - template.Sprites[i].Offset.XY.ToInt2());

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -31,28 +31,36 @@ namespace OpenRA.Graphics
Palette = palette;
Name = name;
Frames = cache[cursorSrc].Skip(Start).ToArray();
var cursorSprites = cache[cursorSrc];
Frames = cursorSprites.Skip(Start).ToArray();
if ((d.ContainsKey("Length") && d["Length"].Value == "*") || (d.ContainsKey("End") && d["End"].Value == "*"))
if ((d.TryGetValue("Length", out var yaml) && yaml.Value == "*") ||
(d.TryGetValue("End", out yaml) && yaml.Value == "*"))
Length = Frames.Length;
else if (d.ContainsKey("Length"))
Length = Exts.ParseIntegerInvariant(d["Length"].Value);
else if (d.ContainsKey("End"))
Length = Exts.ParseIntegerInvariant(d["End"].Value) - Start;
else if (d.TryGetValue("Length", out yaml))
Length = Exts.ParseIntegerInvariant(yaml.Value);
else if (d.TryGetValue("End", out yaml))
Length = Exts.ParseIntegerInvariant(yaml.Value) - Start;
else
Length = 1;
Frames = Frames.Take(Length).ToArray();
if (d.ContainsKey("X"))
if (Start > cursorSprites.Length)
throw new YamlException($"Cursor {name}: {nameof(Start)} is greater than the length of the sprite sequence.");
if (Length > cursorSprites.Length)
throw new YamlException($"Cursor {name}: {nameof(Length)} is greater than the length of the sprite sequence.");
if (d.TryGetValue("X", out yaml))
{
Exts.TryParseIntegerInvariant(d["X"].Value, out var x);
Exts.TryParseIntegerInvariant(yaml.Value, out var x);
Hotspot = Hotspot.WithX(x);
}
if (d.ContainsKey("Y"))
if (d.TryGetValue("Y", out yaml))
{
Exts.TryParseIntegerInvariant(d["Y"].Value, out var y);
Exts.TryParseIntegerInvariant(yaml.Value, out var y);
Hotspot = Hotspot.WithY(y);
}
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -21,9 +21,9 @@ namespace OpenRA.Graphics
public ITexture ColorShifts { get; }
public int Height { get; private set; }
readonly Dictionary<string, ImmutablePalette> palettes = new Dictionary<string, ImmutablePalette>();
readonly Dictionary<string, MutablePalette> mutablePalettes = new Dictionary<string, MutablePalette>();
readonly Dictionary<string, int> indices = new Dictionary<string, int>();
readonly Dictionary<string, ImmutablePalette> palettes = new();
readonly Dictionary<string, MutablePalette> mutablePalettes = new();
readonly Dictionary<string, int> indices = new();
byte[] buffer = Array.Empty<byte>();
float[] colorShiftBuffer = Array.Empty<float>();
@@ -70,7 +70,7 @@ namespace OpenRA.Graphics
{
Height = Exts.NextPowerOf2(index + 1);
Array.Resize(ref buffer, Height * Palette.Size * 4);
Array.Resize(ref colorShiftBuffer, Height * 4);
Array.Resize(ref colorShiftBuffer, Height * 8);
}
if (allowModifiers)
@@ -79,6 +79,9 @@ namespace OpenRA.Graphics
CopyPaletteToBuffer(index, p);
}
[System.Diagnostics.CodeAnalysis.SuppressMessage(
"Performance", "CA1854:Prefer the 'IDictionary.TryGetValue(TKey, out TValue)' method",
Justification = "False positive - indexer is a set not a get.")]
public void ReplacePalette(string name, IPalette p)
{
if (mutablePalettes.ContainsKey(name))
@@ -90,19 +93,20 @@ namespace OpenRA.Graphics
CopyBufferToTexture();
}
public void SetColorShift(string name, float hueOffset, float satOffset, float minHue, float maxHue)
public void SetColorShift(string name, float hueOffset, float satOffset, float valueMultiplier, float minHue, float maxHue)
{
var index = GetPaletteIndex(name);
colorShiftBuffer[4 * index + 0] = hueOffset;
colorShiftBuffer[4 * index + 1] = satOffset;
colorShiftBuffer[4 * index + 2] = minHue;
colorShiftBuffer[4 * index + 3] = maxHue;
colorShiftBuffer[8 * index + 0] = minHue;
colorShiftBuffer[8 * index + 1] = maxHue;
colorShiftBuffer[8 * index + 4] = hueOffset;
colorShiftBuffer[8 * index + 5] = satOffset;
colorShiftBuffer[8 * index + 6] = valueMultiplier;
}
public bool HasColorShift(string name)
{
var index = GetPaletteIndex(name);
return colorShiftBuffer[4 * index + 2] != 0 || colorShiftBuffer[4 * index + 3] != 0;
return colorShiftBuffer[8 * index] != 0 || colorShiftBuffer[8 * index + 1] != 0;
}
public void Initialize()
@@ -125,7 +129,7 @@ namespace OpenRA.Graphics
void CopyBufferToTexture()
{
Texture.SetData(buffer, Palette.Size, Height);
ColorShifts.SetFloatData(colorShiftBuffer, 1, Height);
ColorShifts.SetFloatData(colorShiftBuffer, 2, Height);
}
public void ApplyModifiers(IEnumerable<IPaletteModifier> paletteMods)

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -26,7 +26,7 @@ namespace OpenRA.Graphics
float[] Bounds(uint frame);
ModelRenderData RenderData(uint section);
/// <summary>Returns the smallest rectangle that covers all rotations of all frames in a model</summary>
/// <summary>Returns the smallest rectangle that covers all rotations of all frames in a model.</summary>
Rectangle AggregateBounds { get; }
}
@@ -62,7 +62,7 @@ namespace OpenRA.Graphics
{
public Action<string> OnMissingModelError { get; set; }
class PlaceholderModelCache : IModelCache
sealed class PlaceholderModelCache : IModelCache
{
public IVertexBuffer<Vertex> VertexBuffer => throw new NotImplementedException();
@@ -84,7 +84,6 @@ namespace OpenRA.Graphics
}
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "IDE0060:Remove unused parameter", Justification = "Load game API")]
public PlaceholderModelSequenceLoader(ModData modData) { }
public IModelCache CacheModels(IReadOnlyFileSystem fileSystem, ModData modData, IReadOnlyDictionary<string, MiniYamlNode> modelDefinitions)

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -37,7 +37,7 @@ namespace OpenRA.Graphics
// Static constants
static readonly float[] ShadowDiffuse = new float[] { 0, 0, 0 };
static readonly float[] ShadowAmbient = new float[] { 1, 1, 1 };
static readonly float2 SpritePadding = new float2(2, 2);
static readonly float2 SpritePadding = new(2, 2);
static readonly float[] ZeroVector = new float[] { 0, 0, 0, 1 };
static readonly float[] ZVector = new float[] { 0, 0, 1, 1 };
static readonly float[] FlipMtx = Util.ScaleMatrix(1, -1, 1);
@@ -47,9 +47,9 @@ namespace OpenRA.Graphics
readonly Renderer renderer;
readonly IShader shader;
readonly Dictionary<Sheet, IFrameBuffer> mappedBuffers = new Dictionary<Sheet, IFrameBuffer>();
readonly Stack<KeyValuePair<Sheet, IFrameBuffer>> unmappedBuffers = new Stack<KeyValuePair<Sheet, IFrameBuffer>>();
readonly List<(Sheet Sheet, Action Func)> doRender = new List<(Sheet, Action)>();
readonly Dictionary<Sheet, IFrameBuffer> mappedBuffers = new();
readonly Stack<KeyValuePair<Sheet, IFrameBuffer>> unmappedBuffers = new();
readonly List<(Sheet Sheet, Action Func)> doRender = new();
SheetBuilder sheetBuilderForFrame;
bool isInFrame;
@@ -167,8 +167,7 @@ namespace OpenRA.Graphics
CalculateSpriteGeometry(tl, br, 1, out var spriteSize, out var spriteOffset);
CalculateSpriteGeometry(stl, sbr, 2, out var shadowSpriteSize, out var shadowSpriteOffset);
if (sheetBuilderForFrame == null)
sheetBuilderForFrame = new SheetBuilder(SheetType.BGRA, AllocateSheet);
sheetBuilderForFrame ??= new SheetBuilder(SheetType.BGRA, AllocateSheet);
var sprite = sheetBuilderForFrame.Allocate(spriteSize, 0, spriteOffset);
var shadowSprite = sheetBuilderForFrame.Allocate(shadowSpriteSize, 0, shadowSpriteOffset);
@@ -303,7 +302,7 @@ namespace OpenRA.Graphics
return fbo;
}
void DisableFrameBuffer(IFrameBuffer fbo)
static void DisableFrameBuffer(IFrameBuffer fbo)
{
Game.Renderer.Flush();
Game.Renderer.Context.DisableDepthBuffer();

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -40,7 +40,7 @@ namespace OpenRA.Graphics
return new ReadOnlyPalette(palette);
}
class ReadOnlyPalette : IPalette
sealed class ReadOnlyPalette : IPalette
{
readonly IPalette palette;
public ReadOnlyPalette(IPalette palette) { this.palette = palette; }
@@ -108,7 +108,7 @@ namespace OpenRA.Graphics
public ImmutablePalette(IPalette p)
{
for (int i = 0; i < Palette.Size; i++)
for (var i = 0; i < Palette.Size; i++)
colors[i] = p[i];
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -20,12 +20,14 @@ namespace OpenRA.Graphics
readonly int[] remapIndices;
readonly float hue;
readonly float saturation;
readonly float value;
public PlayerColorRemap(int[] remapIndices, float hue, float saturation)
public PlayerColorRemap(int[] remapIndices, Color color)
{
this.remapIndices = remapIndices;
this.hue = hue;
this.saturation = saturation;
var (r, g, b) = color.ToLinear();
(hue, saturation, value) = Color.RgbToHsv(r, g, b);
}
public Color GetRemappedColor(Color original, int index)
@@ -42,7 +44,7 @@ namespace OpenRA.Graphics
var value = Math.Max(Math.Max(r, g), b);
// Construct the new RGB color
(r, g, b) = Color.HsvToRgb(hue, saturation, value);
(r, g, b) = Color.HsvToRgb(hue, saturation, value * this.value);
// Convert linear back to SRGB and pre-multiply by the alpha
return Color.FromLinear(original.A, r, g, b);

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -18,7 +18,7 @@ namespace OpenRA.Graphics
{
public class RgbaColorRenderer
{
static readonly float3 Offset = new float3(0.5f, 0.5f, 0f);
static readonly float3 Offset = new(0.5f, 0.5f, 0f);
readonly SpriteRenderer parent;
readonly Vertex[] vertices = new Vertex[6];
@@ -78,9 +78,9 @@ namespace OpenRA.Graphics
/// <summary>
/// Calculate the 2D intersection of two lines.
/// Will behave badly if the lines are parallel.
/// Z position is the average of a and b (ignores actual intersection point if it exists)
/// Z position is the average of a and b (ignores actual intersection point if it exists).
/// </summary>
float3 IntersectionOf(in float3 a, in float3 da, in float3 b, in float3 db)
static float3 IntersectionOf(in float3 a, in float3 da, in float3 b, in float3 db)
{
var crossA = a.X * (a.Y + da.Y) - a.Y * (a.X + da.X);
var crossB = b.X * (b.Y + db.Y) - b.Y * (b.X + db.X);
@@ -138,7 +138,7 @@ namespace OpenRA.Graphics
// Segment is part of closed loop
if (closed)
{
var prev = points[points.Length - 1];
var prev = points[^1];
var prevDir = (start - prev) / (start - prev).XY.Length;
var prevCorner = width / 2 * new float3(-prevDir.Y, prevDir.X, prevDir.Z);
ca = IntersectionOf(start - prevCorner, prevDir, start - corner, dir);

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of

View File

@@ -1,152 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using OpenRA.FileSystem;
using OpenRA.Primitives;
namespace OpenRA.Graphics
{
using Sequences = IReadOnlyDictionary<string, Lazy<IReadOnlyDictionary<string, ISpriteSequence>>>;
using UnitSequences = Lazy<IReadOnlyDictionary<string, ISpriteSequence>>;
public interface ISpriteSequence
{
string Name { get; }
int Start { get; }
int Length { get; }
int Stride { get; }
int Facings { get; }
int InterpolatedFacings { get; }
int Tick { get; }
int ZOffset { get; }
int ShadowStart { get; }
int ShadowZOffset { get; }
int[] Frames { get; }
Rectangle Bounds { get; }
bool IgnoreWorldTint { get; }
float Scale { get; }
Sprite GetSprite(int frame);
Sprite GetSprite(int frame, WAngle facing);
(Sprite, WAngle) GetSpriteWithRotation(int frame, WAngle facing);
Sprite GetShadow(int frame, WAngle facing);
float GetAlpha(int frame);
}
public interface ISpriteSequenceLoader
{
IReadOnlyDictionary<string, ISpriteSequence> ParseSequences(ModData modData, string tileSet, SpriteCache cache, MiniYamlNode node);
}
public class SequenceProvider : IDisposable
{
readonly ModData modData;
readonly string tileSet;
readonly Lazy<Sequences> sequences;
readonly Lazy<SpriteCache> spriteCache;
public SpriteCache SpriteCache => spriteCache.Value;
readonly Dictionary<string, UnitSequences> sequenceCache = new Dictionary<string, UnitSequences>();
public SequenceProvider(IReadOnlyFileSystem fileSystem, ModData modData, string tileSet, MiniYaml additionalSequences)
{
this.modData = modData;
this.tileSet = tileSet;
sequences = Exts.Lazy(() =>
{
using (new Support.PerfTimer("LoadSequences"))
return Load(fileSystem, additionalSequences);
});
spriteCache = Exts.Lazy(() => new SpriteCache(fileSystem, modData.SpriteLoaders));
}
public ISpriteSequence GetSequence(string unitName, string sequenceName)
{
if (!sequences.Value.TryGetValue(unitName, out var unitSeq))
throw new InvalidOperationException($"Unit `{unitName}` does not have any sequences defined.");
if (!unitSeq.Value.TryGetValue(sequenceName, out var seq))
throw new InvalidOperationException($"Unit `{unitName}` does not have a sequence named `{sequenceName}`");
return seq;
}
public IEnumerable<string> Images => sequences.Value.Keys;
public bool HasSequence(string unitName)
{
return sequences.Value.ContainsKey(unitName);
}
public bool HasSequence(string unitName, string sequenceName)
{
if (!sequences.Value.TryGetValue(unitName, out var unitSeq))
throw new InvalidOperationException($"Unit `{unitName}` does not have any sequences defined.");
return unitSeq.Value.ContainsKey(sequenceName);
}
public IEnumerable<string> Sequences(string unitName)
{
if (!sequences.Value.TryGetValue(unitName, out var unitSeq))
throw new InvalidOperationException($"Unit `{unitName}` does not have any sequences defined.");
return unitSeq.Value.Keys;
}
Sequences Load(IReadOnlyFileSystem fileSystem, MiniYaml additionalSequences)
{
var nodes = MiniYaml.Load(fileSystem, modData.Manifest.Sequences, additionalSequences);
var items = new Dictionary<string, UnitSequences>();
foreach (var node in nodes)
{
// Nodes starting with ^ are inheritable but never loaded directly
if (node.Key.StartsWith(ActorInfo.AbstractActorPrefix, StringComparison.Ordinal))
continue;
var key = node.Value.ToLines(node.Key).JoinWith("|");
if (sequenceCache.TryGetValue(key, out var t))
items.Add(node.Key, t);
else
{
t = Exts.Lazy(() => modData.SpriteSequenceLoader.ParseSequences(modData, tileSet, SpriteCache, node));
sequenceCache.Add(key, t);
items.Add(node.Key, t);
}
}
return items;
}
public void Preload()
{
foreach (var sb in SpriteCache.SheetBuilders.Values)
sb.Current.CreateBuffer();
foreach (var unitSeq in sequences.Value.Values)
foreach (var seq in unitSeq.Value.Values) { }
foreach (var sb in SpriteCache.SheetBuilders.Values)
sb.Current.ReleaseBuffer();
}
public void Dispose()
{
if (spriteCache.IsValueCreated)
foreach (var sb in SpriteCache.SheetBuilders.Values)
sb.Dispose();
}
}
}

View File

@@ -0,0 +1,119 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using OpenRA.FileSystem;
using OpenRA.Primitives;
namespace OpenRA.Graphics
{
public interface ISpriteSequence
{
string Name { get; }
int Length { get; }
int Facings { get; }
int Tick { get; }
int ZOffset { get; }
int ShadowZOffset { get; }
Rectangle Bounds { get; }
bool IgnoreWorldTint { get; }
float Scale { get; }
void ResolveSprites(SpriteCache cache);
Sprite GetSprite(int frame);
Sprite GetSprite(int frame, WAngle facing);
(Sprite Sprite, WAngle Rotation) GetSpriteWithRotation(int frame, WAngle facing);
Sprite GetShadow(int frame, WAngle facing);
float GetAlpha(int frame);
}
public interface ISpriteSequenceLoader
{
int BgraSheetSize { get; }
int IndexedSheetSize { get; }
IReadOnlyDictionary<string, ISpriteSequence> ParseSequences(ModData modData, string tileSet, SpriteCache cache, MiniYamlNode node);
}
public sealed class SequenceSet : IDisposable
{
readonly ModData modData;
readonly string tileSet;
readonly IReadOnlyDictionary<string, IReadOnlyDictionary<string, ISpriteSequence>> images;
public SpriteCache SpriteCache { get; }
public SequenceSet(IReadOnlyFileSystem fileSystem, ModData modData, string tileSet, MiniYaml additionalSequences)
{
this.modData = modData;
this.tileSet = tileSet;
SpriteCache = new SpriteCache(fileSystem, modData.SpriteLoaders, modData.SpriteSequenceLoader.BgraSheetSize, modData.SpriteSequenceLoader.IndexedSheetSize);
using (new Support.PerfTimer("LoadSequences"))
images = Load(fileSystem, additionalSequences);
}
public ISpriteSequence GetSequence(string image, string sequence)
{
if (!images.TryGetValue(image, out var sequences))
throw new InvalidOperationException($"Image `{image}` does not have any sequences defined.");
if (!sequences.TryGetValue(sequence, out var seq))
throw new InvalidOperationException($"Image `{image}` does not have a sequence named `{sequence}`.");
return seq;
}
public IEnumerable<string> Images => images.Keys;
public bool HasSequence(string image, string sequence)
{
if (!images.TryGetValue(image, out var sequences))
throw new InvalidOperationException($"Image `{image}` does not have any sequences defined.");
return sequences.ContainsKey(sequence);
}
public IEnumerable<string> Sequences(string image)
{
if (!images.TryGetValue(image, out var sequences))
throw new InvalidOperationException($"Image `{image}` does not have any sequences defined.");
return sequences.Keys;
}
IReadOnlyDictionary<string, IReadOnlyDictionary<string, ISpriteSequence>> Load(IReadOnlyFileSystem fileSystem, MiniYaml additionalSequences)
{
var nodes = MiniYaml.Load(fileSystem, modData.Manifest.Sequences, additionalSequences);
var images = new Dictionary<string, IReadOnlyDictionary<string, ISpriteSequence>>();
foreach (var node in nodes)
{
// Nodes starting with ^ are inheritable but never loaded directly
if (node.Key.StartsWith(ActorInfo.AbstractActorPrefix, StringComparison.Ordinal))
continue;
images[node.Key] = modData.SpriteSequenceLoader.ParseSequences(modData, tileSet, SpriteCache, node);
}
return images;
}
public void LoadSprites()
{
SpriteCache.LoadReservations(modData);
foreach (var sequences in images.Values)
foreach (var sequence in sequences)
sequence.Value.ResolveSprites(SpriteCache);
}
public void Dispose()
{
SpriteCache.Dispose();
}
}
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -34,15 +34,16 @@ namespace OpenRA.Graphics
public sealed class SheetBuilder : IDisposable
{
public readonly SheetType Type;
readonly List<Sheet> sheets = new List<Sheet>();
readonly List<Sheet> sheets = new();
readonly Func<Sheet> allocateSheet;
readonly int margin;
Sheet current;
TextureChannel channel;
int rowHeight = 0;
int2 p;
public Sheet Current { get; private set; }
public TextureChannel CurrentChannel { get; private set; }
public IEnumerable<Sheet> AllSheets => sheets;
public static Sheet AllocateSheet(SheetType type, int sheetSize)
{
return new Sheet(type, new Size(sheetSize, sheetSize));
@@ -73,10 +74,10 @@ namespace OpenRA.Graphics
public SheetBuilder(SheetType t, Func<Sheet> allocateSheet, int margin = 1)
{
channel = t == SheetType.Indexed ? TextureChannel.Red : TextureChannel.RGBA;
CurrentChannel = t == SheetType.Indexed ? TextureChannel.Red : TextureChannel.RGBA;
Type = t;
current = allocateSheet();
sheets.Add(current);
Current = allocateSheet();
sheets.Add(Current);
this.allocateSheet = allocateSheet;
this.margin = margin;
}
@@ -87,19 +88,19 @@ namespace OpenRA.Graphics
{
// Don't bother allocating empty sprites
if (size.Width == 0 || size.Height == 0)
return new Sprite(current, Rectangle.Empty, 0, spriteOffset, channel, BlendMode.Alpha);
return new Sprite(Current, Rectangle.Empty, 0, spriteOffset, CurrentChannel, BlendMode.Alpha);
var rect = Allocate(size, zRamp, spriteOffset);
Util.FastCopyIntoChannel(rect, src, type);
current.CommitBufferedData();
Current.CommitBufferedData();
return rect;
}
public Sprite Add(Png src, float scale = 1f)
{
var rect = Allocate(new Size(src.Width, src.Height), scale);
var rect = Allocate(new Size(src.Width, src.Height), scale);
Util.FastCopyIntoSprite(rect, src);
current.CommitBufferedData();
Current.CommitBufferedData();
return rect;
}
@@ -115,7 +116,7 @@ namespace OpenRA.Graphics
public Sprite Allocate(Size imageSize, float scale = 1f) { return Allocate(imageSize, 0, float3.Zero, scale); }
public Sprite Allocate(Size imageSize, float zRamp, in float3 spriteOffset, float scale = 1f)
{
if (imageSize.Width + p.X + margin > current.Size.Width)
if (imageSize.Width + p.X + margin > Current.Size.Width)
{
p = new int2(0, p.Y + rowHeight + margin);
rowHeight = imageSize.Height;
@@ -124,33 +125,29 @@ namespace OpenRA.Graphics
if (imageSize.Height > rowHeight)
rowHeight = imageSize.Height;
if (p.Y + imageSize.Height + margin > current.Size.Height)
if (p.Y + imageSize.Height + margin > Current.Size.Height)
{
var next = NextChannel(channel);
var next = NextChannel(CurrentChannel);
if (next == null)
{
current.ReleaseBuffer();
current = allocateSheet();
sheets.Add(current);
channel = Type == SheetType.Indexed ? TextureChannel.Red : TextureChannel.RGBA;
Current.ReleaseBuffer();
Current = allocateSheet();
sheets.Add(Current);
CurrentChannel = Type == SheetType.Indexed ? TextureChannel.Red : TextureChannel.RGBA;
}
else
channel = next.Value;
CurrentChannel = next.Value;
rowHeight = imageSize.Height;
p = int2.Zero;
}
var rect = new Sprite(current, new Rectangle(p.X + margin, p.Y + margin, imageSize.Width, imageSize.Height), zRamp, spriteOffset, channel, BlendMode.Alpha, scale);
var rect = new Sprite(Current, new Rectangle(p.X + margin, p.Y + margin, imageSize.Width, imageSize.Height), zRamp, spriteOffset, CurrentChannel, BlendMode.Alpha, scale);
p += new int2(imageSize.Width + margin, 0);
return rect;
}
public Sheet Current => current;
public TextureChannel CurrentChannel => channel;
public IEnumerable<Sheet> AllSheets => sheets;
public void Dispose()
{
foreach (var sheet in sheets)

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of

View File

@@ -0,0 +1,174 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using OpenRA.FileSystem;
using OpenRA.Primitives;
namespace OpenRA.Graphics
{
public sealed class SpriteCache : IDisposable
{
public readonly Dictionary<SheetType, SheetBuilder> SheetBuilders;
readonly ISpriteLoader[] loaders;
readonly IReadOnlyFileSystem fileSystem;
readonly Dictionary<int, (int[] Frames, MiniYamlNode.SourceLocation Location)> spriteReservations = new();
readonly Dictionary<int, (int[] Frames, MiniYamlNode.SourceLocation Location)> frameReservations = new();
readonly Dictionary<string, List<int>> reservationsByFilename = new();
readonly Dictionary<int, ISpriteFrame[]> resolvedFrames = new();
readonly Dictionary<int, Sprite[]> resolvedSprites = new();
readonly Dictionary<int, (string Filename, MiniYamlNode.SourceLocation Location)> missingFiles = new();
int nextReservationToken = 1;
public SpriteCache(IReadOnlyFileSystem fileSystem, ISpriteLoader[] loaders, int bgraSheetSize, int indexedSheetSize, int bgraSheetMargin = 1, int indexedSheetMargin = 1)
{
SheetBuilders = new Dictionary<SheetType, SheetBuilder>
{
{ SheetType.Indexed, new SheetBuilder(SheetType.Indexed, indexedSheetSize, indexedSheetMargin) },
{ SheetType.BGRA, new SheetBuilder(SheetType.BGRA, bgraSheetSize, bgraSheetMargin) }
};
this.fileSystem = fileSystem;
this.loaders = loaders;
}
public int ReserveSprites(string filename, IEnumerable<int> frames, MiniYamlNode.SourceLocation location)
{
var token = nextReservationToken++;
spriteReservations[token] = (frames?.ToArray(), location);
reservationsByFilename.GetOrAdd(filename, _ => new List<int>()).Add(token);
return token;
}
public int ReserveFrames(string filename, IEnumerable<int> frames, MiniYamlNode.SourceLocation location)
{
var token = nextReservationToken++;
frameReservations[token] = (frames?.ToArray(), location);
reservationsByFilename.GetOrAdd(filename, _ => new List<int>()).Add(token);
return token;
}
static ISpriteFrame[] GetFrames(IReadOnlyFileSystem fileSystem, string filename, ISpriteLoader[] loaders, out TypeDictionary metadata)
{
metadata = null;
if (!fileSystem.TryOpen(filename, out var stream))
return null;
using (stream)
{
foreach (var loader in loaders)
if (loader.TryParseSprite(stream, filename, out var frames, out metadata))
return frames;
return null;
}
}
public void LoadReservations(ModData modData)
{
foreach (var sb in SheetBuilders.Values)
sb.Current.CreateBuffer();
var spriteCache = new Dictionary<int, Sprite>();
foreach (var (filename, tokens) in reservationsByFilename)
{
modData.LoadScreen?.Display();
var loadedFrames = GetFrames(fileSystem, filename, loaders, out _);
foreach (var token in tokens)
{
if (frameReservations.TryGetValue(token, out var r))
{
if (loadedFrames != null)
{
if (r.Frames != null)
{
var resolved = new ISpriteFrame[loadedFrames.Length];
foreach (var i in r.Frames)
resolved[i] = loadedFrames[i];
resolvedFrames[token] = resolved;
}
else
resolvedFrames[token] = loadedFrames;
}
else
{
resolvedFrames[token] = null;
missingFiles[token] = (filename, r.Location);
}
}
if (spriteReservations.TryGetValue(token, out r))
{
if (loadedFrames != null)
{
var resolved = new Sprite[loadedFrames.Length];
var frames = r.Frames ?? Enumerable.Range(0, loadedFrames.Length);
foreach (var i in frames)
resolved[i] = spriteCache.GetOrAdd(i,
f => SheetBuilders[SheetBuilder.FrameTypeToSheetType(loadedFrames[f].Type)].Add(loadedFrames[f]));
resolvedSprites[token] = resolved;
}
else
{
resolvedSprites[token] = null;
missingFiles[token] = (filename, r.Location);
}
}
}
spriteCache.Clear();
}
spriteReservations.Clear();
frameReservations.Clear();
reservationsByFilename.Clear();
foreach (var sb in SheetBuilders.Values)
sb.Current.ReleaseBuffer();
}
public Sprite[] ResolveSprites(int token)
{
var resolved = resolvedSprites[token];
resolvedSprites.Remove(token);
if (missingFiles.TryGetValue(token, out var r))
throw new FileNotFoundException($"{r.Location}: {r.Filename} not found", r.Filename);
return resolved;
}
public ISpriteFrame[] ResolveFrames(int token)
{
var resolved = resolvedFrames[token];
resolvedFrames.Remove(token);
if (missingFiles.TryGetValue(token, out var r))
throw new FileNotFoundException($"{r.Location}: {r.Filename} not found", r.Filename);
return resolved;
}
public IEnumerable<(string Filename, MiniYamlNode.SourceLocation Location)> MissingFiles => missingFiles.Values.ToHashSet();
public void Dispose()
{
foreach (var sb in SheetBuilders.Values)
sb.Dispose();
}
}
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -123,7 +123,7 @@ namespace OpenRA.Graphics
}
}
float2 Rotate(float2 v, float sina, float cosa, float2 offset)
static float2 Rotate(float2 v, float sina, float cosa, float2 offset)
{
return new float2(
v.X * cosa - v.Y * sina + offset.X,
@@ -427,7 +427,7 @@ namespace OpenRA.Graphics
}
}
class GlyphInfo
sealed class GlyphInfo
{
public float Advance;
public int2 Offset;

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -9,10 +9,7 @@
*/
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using OpenRA.FileSystem;
using OpenRA.Primitives;
@@ -21,7 +18,7 @@ namespace OpenRA.Graphics
/// <summary>
/// Describes the format of the pixel data in a ISpriteFrame.
/// Note that the channel order is defined for little-endian bytes, so BGRA corresponds
/// to a 32bit ARGB value, such as that returned by Color.ToArgb()!
/// to a 32bit ARGB value, such as that returned by Color.ToArgb().
/// </summary>
public enum SpriteFrameType
{
@@ -67,94 +64,6 @@ namespace OpenRA.Graphics
bool DisableExportPadding { get; }
}
public class SpriteCache
{
public readonly Cache<SheetType, SheetBuilder> SheetBuilders;
readonly ISpriteLoader[] loaders;
readonly IReadOnlyFileSystem fileSystem;
readonly Dictionary<string, List<Sprite[]>> sprites = new Dictionary<string, List<Sprite[]>>();
readonly Dictionary<string, ISpriteFrame[]> unloadedFrames = new Dictionary<string, ISpriteFrame[]>();
readonly Dictionary<string, TypeDictionary> metadata = new Dictionary<string, TypeDictionary>();
public SpriteCache(IReadOnlyFileSystem fileSystem, ISpriteLoader[] loaders)
{
SheetBuilders = new Cache<SheetType, SheetBuilder>(t => new SheetBuilder(t));
this.fileSystem = fileSystem;
this.loaders = loaders;
}
/// <summary>
/// Returns the first set of sprites with the given filename.
/// If getUsedFrames is defined then the indices returned by the function call
/// are guaranteed to be loaded. The value of other indices in the returned
/// array are undefined and should never be accessed.
/// </summary>
public Sprite[] this[string filename, Func<int, IEnumerable<int>> getUsedFrames = null]
{
get
{
var allSprites = sprites.GetOrAdd(filename);
var sprite = allSprites.FirstOrDefault();
if (!unloadedFrames.TryGetValue(filename, out var unloaded))
unloaded = null;
// This is the first time that the file has been requested
// Load all of the frames into the unused buffer and initialize
// the loaded cache (initially empty)
if (sprite == null)
{
unloaded = FrameLoader.GetFrames(fileSystem, filename, loaders, out var fileMetadata);
unloadedFrames[filename] = unloaded;
metadata[filename] = fileMetadata;
sprite = new Sprite[unloaded.Length];
allSprites.Add(sprite);
}
// HACK: The sequence code relies on side-effects from getUsedFrames
var indices = getUsedFrames != null ? getUsedFrames(sprite.Length) :
Enumerable.Range(0, sprite.Length);
// Load any unused frames into the SheetBuilder
if (unloaded != null)
{
foreach (var i in indices)
{
if (unloaded[i] != null)
{
sprite[i] = SheetBuilders[SheetBuilder.FrameTypeToSheetType(unloaded[i].Type)].Add(unloaded[i]);
unloaded[i] = null;
}
}
// All frames have been loaded
if (unloaded.All(f => f == null))
unloadedFrames.Remove(filename);
}
return sprite;
}
}
/// <summary>
/// Returns a TypeDictionary containing any metadata defined by the frame
/// or null if the frame does not define metadata.
/// </summary>
public TypeDictionary FrameMetadata(string filename)
{
if (!metadata.TryGetValue(filename, out var fileMetadata))
{
FrameLoader.GetFrames(fileSystem, filename, loaders, out fileMetadata);
metadata[filename] = fileMetadata;
}
return fileMetadata;
}
}
public class FrameCache
{
readonly Cache<string, ISpriteFrame[]> frames;

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -21,102 +21,95 @@ namespace OpenRA.Graphics
readonly Sprite sprite;
readonly WPos pos;
readonly WVec offset;
readonly int zOffset;
readonly PaletteReference palette;
readonly float scale;
readonly WAngle rotation = WAngle.Zero;
readonly float3 tint;
readonly TintModifiers tintModifiers;
readonly float alpha;
readonly bool isDecoration;
public SpriteRenderable(Sprite sprite, WPos pos, WVec offset, int zOffset, PaletteReference palette, float scale, float alpha,
float3 tint, TintModifiers tintModifiers, bool isDecoration, WAngle rotation)
{
this.sprite = sprite;
this.pos = pos;
this.offset = offset;
this.zOffset = zOffset;
this.palette = palette;
Offset = offset;
ZOffset = zOffset;
Palette = palette;
this.scale = scale;
this.rotation = rotation;
this.tint = tint;
this.isDecoration = isDecoration;
this.tintModifiers = tintModifiers;
this.alpha = alpha;
Tint = tint;
IsDecoration = isDecoration;
TintModifiers = tintModifiers;
Alpha = alpha;
// PERF: Remove useless palette assignments for RGBA sprites
// HACK: This is working around the fact that palettes are defined on traits rather than sequences
// and can be removed once this has been fixed
if (sprite.Channel == TextureChannel.RGBA && !(palette?.HasColorShift ?? false))
this.palette = null;
Palette = null;
}
public SpriteRenderable(Sprite sprite, WPos pos, WVec offset, int zOffset, PaletteReference palette, float scale, float alpha,
float3 tint, TintModifiers tintModifiers, bool isDecoration)
: this(sprite, pos, offset, zOffset, palette, scale, alpha, tint, tintModifiers, isDecoration, WAngle.Zero) { }
public WPos Pos => pos + offset;
public WVec Offset => offset;
public PaletteReference Palette => palette;
public int ZOffset => zOffset;
public bool IsDecoration => isDecoration;
public WPos Pos => pos + Offset;
public WVec Offset { get; }
public PaletteReference Palette { get; }
public int ZOffset { get; }
public bool IsDecoration { get; }
public float Alpha => alpha;
public float3 Tint => tint;
public TintModifiers TintModifiers => tintModifiers;
public float Alpha { get; }
public float3 Tint { get; }
public TintModifiers TintModifiers { get; }
public IPalettedRenderable WithPalette(PaletteReference newPalette)
{
return new SpriteRenderable(sprite, pos, offset, zOffset, newPalette, scale, alpha, tint, tintModifiers, isDecoration, rotation);
return new SpriteRenderable(sprite, pos, Offset, ZOffset, newPalette, scale, Alpha, Tint, TintModifiers, IsDecoration, rotation);
}
public IRenderable WithZOffset(int newOffset)
{
return new SpriteRenderable(sprite, pos, offset, newOffset, palette, scale, alpha, tint, tintModifiers, isDecoration, rotation);
return new SpriteRenderable(sprite, pos, Offset, newOffset, Palette, scale, Alpha, Tint, TintModifiers, IsDecoration, rotation);
}
public IRenderable OffsetBy(in WVec vec)
{
return new SpriteRenderable(sprite, pos + vec, offset, zOffset, palette, scale, alpha, tint, tintModifiers, isDecoration, rotation);
return new SpriteRenderable(sprite, pos + vec, Offset, ZOffset, Palette, scale, Alpha, Tint, TintModifiers, IsDecoration, rotation);
}
public IRenderable AsDecoration()
{
return new SpriteRenderable(sprite, pos, offset, zOffset, palette, scale, alpha, tint, tintModifiers, true, rotation);
return new SpriteRenderable(sprite, pos, Offset, ZOffset, Palette, scale, Alpha, Tint, TintModifiers, true, rotation);
}
public IModifyableRenderable WithAlpha(float newAlpha)
{
return new SpriteRenderable(sprite, pos, offset, zOffset, palette, scale, newAlpha, tint, tintModifiers, isDecoration, rotation);
return new SpriteRenderable(sprite, pos, Offset, ZOffset, Palette, scale, newAlpha, Tint, TintModifiers, IsDecoration, rotation);
}
public IModifyableRenderable WithTint(in float3 newTint, TintModifiers newTintModifiers)
{
return new SpriteRenderable(sprite, pos, offset, zOffset, palette, scale, alpha, newTint, newTintModifiers, isDecoration, rotation);
return new SpriteRenderable(sprite, pos, Offset, ZOffset, Palette, scale, Alpha, newTint, newTintModifiers, IsDecoration, rotation);
}
float3 ScreenPosition(WorldRenderer wr)
{
var s = 0.5f * scale * sprite.Size;
return wr.Screen3DPxPosition(pos) + wr.ScreenPxOffset(offset) - new float3((int)s.X, (int)s.Y, s.Z);
return wr.Screen3DPxPosition(pos) + wr.ScreenPxOffset(Offset) - new float3((int)s.X, (int)s.Y, s.Z);
}
public IFinalizedRenderable PrepareRender(WorldRenderer wr) { return this; }
public void Render(WorldRenderer wr)
{
var wsr = Game.Renderer.WorldSpriteRenderer;
var t = alpha * tint;
if (wr.TerrainLighting != null && (tintModifiers & TintModifiers.IgnoreWorldTint) == 0)
var t = Alpha * Tint;
if (wr.TerrainLighting != null && (TintModifiers & TintModifiers.IgnoreWorldTint) == 0)
t *= wr.TerrainLighting.TintAt(pos);
// Shader interprets negative alpha as a flag to use the tint colour directly instead of multiplying the sprite colour
var a = alpha;
if ((tintModifiers & TintModifiers.ReplaceColor) != 0)
var a = Alpha;
if ((TintModifiers & TintModifiers.ReplaceColor) != 0)
a *= -1;
wsr.DrawSprite(sprite, palette, ScreenPosition(wr), scale, t, a, rotation.RendererRadians());
wsr.DrawSprite(sprite, Palette, ScreenPosition(wr), scale, t, a, rotation.RendererRadians());
}
public void RenderDebugGeometry(WorldRenderer wr)

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -114,7 +114,7 @@ namespace OpenRA.Graphics
return new int2(sheetIndex, secondarySheetIndex);
}
float ResolveTextureIndex(Sprite s, PaletteReference pal)
static float ResolveTextureIndex(Sprite s, PaletteReference pal)
{
if (pal == null)
return 0;

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -27,7 +27,7 @@ namespace OpenRA.Graphics
readonly IVertexBuffer<Vertex> vertexBuffer;
readonly Vertex[] vertices;
readonly bool[] ignoreTint;
readonly HashSet<int> dirtyRows = new HashSet<int>();
readonly HashSet<int> dirtyRows = new();
readonly int rowStride;
readonly bool restrictToBounds;
@@ -114,7 +114,7 @@ namespace OpenRA.Graphics
// transparent for isometric tiles
var tl = worldRenderer.TerrainLighting;
var pos = map.CenterOfCell(uv.ToCPos(map));
var step = map.Grid.Type == MapGridType.RectangularIsometric ? 724 : 512;
var step = map.Grid.TileScale / 2;
var weights = new[]
{
tl.TintAt(pos + new WVec(-step, -step, 0)),

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -16,10 +16,7 @@ namespace OpenRA.Graphics
public class UISpriteRenderable : IRenderable, IPalettedRenderable, IFinalizedRenderable
{
readonly Sprite sprite;
readonly WPos effectiveWorldPos;
readonly int2 screenPos;
readonly int zOffset;
readonly PaletteReference palette;
readonly float scale;
readonly float alpha;
readonly float rotation = 0f;
@@ -27,10 +24,10 @@ namespace OpenRA.Graphics
public UISpriteRenderable(Sprite sprite, WPos effectiveWorldPos, int2 screenPos, int zOffset, PaletteReference palette, float scale = 1f, float alpha = 1f, float rotation = 0f)
{
this.sprite = sprite;
this.effectiveWorldPos = effectiveWorldPos;
Pos = effectiveWorldPos;
this.screenPos = screenPos;
this.zOffset = zOffset;
this.palette = palette;
ZOffset = zOffset;
Palette = palette;
this.scale = scale;
this.alpha = alpha;
this.rotation = rotation;
@@ -39,18 +36,18 @@ namespace OpenRA.Graphics
// HACK: This is working around the fact that palettes are defined on traits rather than sequences
// and can be removed once this has been fixed
if (sprite.Channel == TextureChannel.RGBA && !(palette?.HasColorShift ?? false))
this.palette = null;
Palette = null;
}
// Does not exist in the world, so a world positions don't make sense
public WPos Pos => effectiveWorldPos;
public WPos Pos { get; }
public WVec Offset => WVec.Zero;
public bool IsDecoration => true;
public PaletteReference Palette => palette;
public int ZOffset => zOffset;
public PaletteReference Palette { get; }
public int ZOffset { get; }
public IPalettedRenderable WithPalette(PaletteReference newPalette) { return new UISpriteRenderable(sprite, effectiveWorldPos, screenPos, zOffset, newPalette, scale, alpha, rotation); }
public IPalettedRenderable WithPalette(PaletteReference newPalette) { return new UISpriteRenderable(sprite, Pos, screenPos, ZOffset, newPalette, scale, alpha, rotation); }
public IRenderable WithZOffset(int newOffset) { return this; }
public IRenderable OffsetBy(in WVec vec) { return this; }
public IRenderable AsDecoration() { return this; }
@@ -58,7 +55,7 @@ namespace OpenRA.Graphics
public IFinalizedRenderable PrepareRender(WorldRenderer wr) { return this; }
public void Render(WorldRenderer wr)
{
Game.Renderer.SpriteRenderer.DrawSprite(sprite, palette, screenPos, scale, float3.Ones, alpha, rotation);
Game.Renderer.SpriteRenderer.DrawSprite(sprite, Palette, screenPos, scale, float3.Ones, alpha, rotation);
}
public void RenderDebugGeometry(WorldRenderer wr)

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -224,10 +224,10 @@ namespace OpenRA.Graphics
}
/// <summary>Rotates a quad about its center in the x-y plane.</summary>
/// <param name="tl">The top left vertex of the quad</param>
/// <param name="size">A float3 containing the X, Y, and Z lengths of the quad</param>
/// <param name="rotation">The number of radians to rotate by</param>
/// <returns>An array of four vertices representing the rotated quad (top-left, top-right, bottom-right, bottom-left)</returns>
/// <param name="tl">The top left vertex of the quad.</param>
/// <param name="size">A float3 containing the X, Y, and Z lengths of the quad.</param>
/// <param name="rotation">The number of radians to rotate by.</param>
/// <returns>An array of four vertices representing the rotated quad (top-left, top-right, bottom-right, bottom-left).</returns>
public static float3[] RotateQuad(float3 tl, float3 size, float rotation)
{
var center = tl + 0.5f * size;
@@ -258,15 +258,15 @@ namespace OpenRA.Graphics
/// <summary>
/// Returns the bounds of an object. Used for determining which objects need to be rendered on screen, and which do not.
/// </summary>
/// <param name="offset">The top left vertex of the object</param>
/// <param name="size">A float 3 containing the X, Y, and Z lengths of the object</param>
/// <param name="rotation">The angle to rotate the object by (use 0f if there is no rotation)</param>
/// <param name="offset">The top left vertex of the object.</param>
/// <param name="size">A float 3 containing the X, Y, and Z lengths of the object.</param>
/// <param name="rotation">The angle to rotate the object by (use 0f if there is no rotation).</param>
public static Rectangle BoundingRectangle(float3 offset, float3 size, float rotation)
{
if (rotation == 0f)
return new Rectangle((int)offset.X, (int)offset.Y, (int)size.X, (int)size.Y);
var rotatedQuad = Util.RotateQuad(offset, size, rotation);
var rotatedQuad = RotateQuad(offset, size, rotation);
var minX = rotatedQuad[0].X;
var maxX = rotatedQuad[0].X;
var minY = rotatedQuad[0].Y;

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -53,7 +53,7 @@ namespace OpenRA.Graphics
public WPos CenterPosition => worldRenderer.ProjectedPosition(CenterLocation);
public Rectangle Rectangle => new Rectangle(TopLeft, new Size(viewportSize.X, viewportSize.Y));
public Rectangle Rectangle => new(TopLeft, new Size(viewportSize.X, viewportSize.Y));
public int2 TopLeft => CenterLocation - viewportSize / 2;
public int2 BottomRight => CenterLocation + viewportSize / 2;
int2 viewportSize;
@@ -66,9 +66,6 @@ namespace OpenRA.Graphics
WorldViewport lastViewportDistance;
float zoom = 1f;
float minZoom = 1f;
float maxZoom = 2f;
bool unlockMinZoom;
float unlockedMinZoomScale;
float unlockedMinZoom = 1f;
@@ -86,12 +83,13 @@ namespace OpenRA.Graphics
}
}
public float MinZoom => minZoom;
public float MinZoom { get; private set; } = 1f;
public float MaxZoom { get; private set; } = 2f;
public void AdjustZoom(float dz)
{
// Exponential ensures that equal positive and negative steps have the same effect
Zoom = (zoom * (float)Math.Exp(dz)).Clamp(unlockMinZoom ? unlockedMinZoom : minZoom, maxZoom);
Zoom = (zoom * (float)Math.Exp(dz)).Clamp(unlockMinZoom ? unlockedMinZoom : MinZoom, MaxZoom);
}
public void AdjustZoom(float dz, int2 center)
@@ -105,10 +103,10 @@ namespace OpenRA.Graphics
public void ToggleZoom()
{
// Unlocked zooms always reset to the default zoom
if (zoom < minZoom)
Zoom = minZoom;
if (zoom < MinZoom)
Zoom = MinZoom;
else
Zoom = zoom > minZoom ? minZoom : maxZoom;
Zoom = zoom > MinZoom ? MinZoom : MaxZoom;
}
public void UnlockMinimumZoom(float scale)
@@ -121,23 +119,6 @@ namespace OpenRA.Graphics
public static long LastMoveRunTime = 0;
public static int2 LastMousePos;
float ClosestTo(float[] collection, float target)
{
var closestValue = collection.First();
var subtractResult = Math.Abs(closestValue - target);
foreach (var element in collection)
{
if (Math.Abs(element - target) < subtractResult)
{
subtractResult = Math.Abs(element - target);
closestValue = element;
}
}
return closestValue;
}
public ScrollDirection GetBlockedDirections()
{
var ret = ScrollDirection.None;
@@ -191,7 +172,7 @@ namespace OpenRA.Graphics
UpdateViewportZooms();
}
float CalculateMinimumZoom(float minHeight, float maxHeight)
static float CalculateMinimumZoom(float minHeight, float maxHeight)
{
var h = Game.Renderer.NativeResolution.Height;
@@ -227,14 +208,14 @@ namespace OpenRA.Graphics
var vd = graphicSettings.ViewportDistance;
if (viewportSizes.AllowNativeZoom && vd == WorldViewport.Native)
minZoom = 1;
MinZoom = viewportSizes.DefaultScale;
else
{
var range = viewportSizes.GetSizeRange(vd);
minZoom = CalculateMinimumZoom(range.X, range.Y);
MinZoom = CalculateMinimumZoom(range.X, range.Y) * viewportSizes.DefaultScale;
}
maxZoom = Math.Min(minZoom * viewportSizes.MaxZoomScale, Game.Renderer.NativeResolution.Height * 1f / viewportSizes.MaxZoomWindowHeight);
MaxZoom = Math.Min(MinZoom * viewportSizes.MaxZoomScale, Game.Renderer.NativeResolution.Height * viewportSizes.DefaultScale / viewportSizes.MaxZoomWindowHeight);
if (unlockMinZoom)
{
@@ -242,19 +223,19 @@ namespace OpenRA.Graphics
// TODO: Allow zooming out until the full map is visible
// We need to improve our viewport scroll handling to center the map as we zoom out
// before this will work well enough to enable
unlockedMinZoom = minZoom * unlockedMinZoomScale;
unlockedMinZoom = MinZoom * unlockedMinZoomScale;
}
if (resetCurrentZoom)
Zoom = minZoom;
Zoom = MinZoom;
else
Zoom = Zoom.Clamp(minZoom, maxZoom);
Zoom = Zoom.Clamp(MinZoom, MaxZoom);
var maxSize = (1f / (unlockMinZoom ? unlockedMinZoom : minZoom) * new float2(Game.Renderer.NativeResolution));
var maxSize = 1f / (unlockMinZoom ? unlockedMinZoom : MinZoom) * new float2(Game.Renderer.NativeResolution);
Game.Renderer.SetMaximumViewportSize(new Size((int)maxSize.X, (int)maxSize.Y));
foreach (var t in worldRenderer.World.WorldActor.TraitsImplementing<INotifyViewportZoomExtentsChanged>())
t.ViewportZoomExtentsChanged(minZoom, maxZoom);
t.ViewportZoomExtentsChanged(MinZoom, MaxZoom);
}
public CPos ViewToWorld(int2 view)
@@ -297,7 +278,7 @@ namespace OpenRA.Graphics
return worldRenderer.World.Map.CellContaining(worldRenderer.ProjectedPosition(ViewToWorldPx(view)));
}
/// <summary> Returns an unfiltered list of all cells that could potentially contain the mouse cursor</summary>
/// <summary>Returns an unfiltered list of all cells that could potentially contain the mouse cursor.</summary>
IEnumerable<MPos> CandidateMouseoverCells(int2 world)
{
var map = worldRenderer.World.Map;
@@ -313,8 +294,8 @@ namespace OpenRA.Graphics
}
public int2 ViewToWorldPx(int2 view) { return (graphicSettings.UIScale / Zoom * view.ToFloat2()).ToInt2() + TopLeft; }
public int2 WorldToViewPx(int2 world) { return ((Zoom / graphicSettings.UIScale) * (world - TopLeft).ToFloat2()).ToInt2(); }
public int2 WorldToViewPx(in float3 world) { return ((Zoom / graphicSettings.UIScale) * (world - TopLeft).XY).ToInt2(); }
public int2 WorldToViewPx(int2 world) { return (Zoom / graphicSettings.UIScale * (world - TopLeft).ToFloat2()).ToInt2(); }
public int2 WorldToViewPx(in float3 world) { return (Zoom / graphicSettings.UIScale * (world - TopLeft).XY).ToInt2(); }
public void Center(IEnumerable<Actor> actors)
{

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -31,25 +31,25 @@ namespace OpenRA.Graphics
public event Action PaletteInvalidated = null;
readonly HashSet<Actor> onScreenActors = new HashSet<Actor>();
readonly HardwarePalette palette = new HardwarePalette();
readonly Dictionary<string, PaletteReference> palettes = new Dictionary<string, PaletteReference>();
readonly HashSet<Actor> onScreenActors = new();
readonly HardwarePalette palette = new();
readonly Dictionary<string, PaletteReference> palettes = new();
readonly IRenderTerrain terrainRenderer;
readonly Lazy<DebugVisualizations> debugVis;
readonly Func<string, PaletteReference> createPaletteReference;
readonly bool enableDepthBuffer;
readonly List<IFinalizedRenderable> preparedRenderables = new List<IFinalizedRenderable>();
readonly List<IFinalizedRenderable> preparedOverlayRenderables = new List<IFinalizedRenderable>();
readonly List<IFinalizedRenderable> preparedAnnotationRenderables = new List<IFinalizedRenderable>();
readonly List<IFinalizedRenderable> preparedRenderables = new();
readonly List<IFinalizedRenderable> preparedOverlayRenderables = new();
readonly List<IFinalizedRenderable> preparedAnnotationRenderables = new();
readonly List<IRenderable> renderablesBuffer = new List<IRenderable>();
readonly List<IRenderable> renderablesBuffer = new();
internal WorldRenderer(ModData modData, World world)
{
World = world;
TileSize = World.Map.Grid.TileSize;
TileScale = World.Map.Grid.Type == MapGridType.RectangularIsometric ? 1448 : 1024;
TileScale = World.Map.Grid.TileScale;
Viewport = new Viewport(this, world.Map);
createPaletteReference = CreatePaletteReference;
@@ -87,7 +87,7 @@ namespace OpenRA.Graphics
{
// HACK: This is working around the fact that palettes are defined on traits rather than sequences
// and can be removed once this has been fixed.
return name == null ? null : palettes.GetOrAdd(name, createPaletteReference);
return string.IsNullOrEmpty(name) ? null : palettes.GetOrAdd(name, createPaletteReference);
}
public void AddPalette(string name, ImmutablePalette pal, bool allowModifiers = false, bool allowOverwrite = false)
@@ -109,13 +109,13 @@ namespace OpenRA.Graphics
palette.ReplacePalette(name, pal);
// Update cached PlayerReference if one exists
if (palettes.ContainsKey(name))
palettes[name].Palette = pal;
if (palettes.TryGetValue(name, out var paletteReference))
paletteReference.Palette = pal;
}
public void SetPaletteColorShift(string name, float hueOffset, float satOffset, float minHue, float maxHue)
public void SetPaletteColorShift(string name, float hueOffset, float satOffset, float valueModifier, float minHue, float maxHue)
{
palette.SetColorShift(name, hueOffset, satOffset, minHue, maxHue);
palette.SetColorShift(name, hueOffset, satOffset, valueModifier, minHue, maxHue);
}
// PERF: Avoid LINQ.
@@ -177,7 +177,7 @@ namespace OpenRA.Graphics
foreach (var e in World.Effects)
{
if (!(e is IEffectAboveShroud ea))
if (e is not IEffectAboveShroud ea)
continue;
foreach (var renderable in ea.RenderAboveShroud(this))
@@ -218,7 +218,7 @@ namespace OpenRA.Graphics
foreach (var e in World.Effects)
{
if (!(e is IEffectAnnotation ea))
if (e is not IEffectAnnotation ea)
continue;
foreach (var renderAnnotation in ea.RenderAnnotation(this))

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -19,8 +19,8 @@ namespace OpenRA
public readonly string Name;
public readonly Hotkey Default = Hotkey.Invalid;
public readonly string Description = "";
public readonly HashSet<string> Types = new HashSet<string>();
public readonly HashSet<string> Contexts = new HashSet<string>();
public readonly HashSet<string> Types = new();
public readonly HashSet<string> Contexts = new();
public readonly bool Readonly = false;
public bool HasDuplicates { get; internal set; }

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -18,8 +18,8 @@ namespace OpenRA
public sealed class HotkeyManager
{
readonly Dictionary<string, Hotkey> settings;
readonly Dictionary<string, HotkeyDefinition> definitions = new Dictionary<string, HotkeyDefinition>();
readonly Dictionary<string, Hotkey> keys = new Dictionary<string, Hotkey>();
readonly Dictionary<string, HotkeyDefinition> definitions = new();
readonly Dictionary<string, Hotkey> keys = new();
public HotkeyManager(IReadOnlyFileSystem fileSystem, Dictionary<string, Hotkey> settings, Manifest manifest)
{
@@ -35,7 +35,7 @@ namespace OpenRA
foreach (var kv in settings)
{
if (definitions.ContainsKey(kv.Key) && !definitions[kv.Key].Readonly)
if (definitions.TryGetValue(kv.Key, out var definition) && !definition.Readonly)
keys[kv.Key] = kv.Value;
}
@@ -43,6 +43,9 @@ namespace OpenRA
hd.Value.HasDuplicates = GetFirstDuplicate(hd.Value, this[hd.Value.Name].GetValue()) != null;
}
[System.Diagnostics.CodeAnalysis.SuppressMessage(
"Performance", "CA1854:Prefer the 'IDictionary.TryGetValue(TKey, out TValue)' method",
Justification = "Func must perform a live lookup in the collection, as the lookup value can change.")]
internal Func<Hotkey> GetHotkeyReference(string name)
{
// Is this a mod-defined hotkey?
@@ -102,7 +105,7 @@ namespace OpenRA
return null;
}
public HotkeyReference this[string name] => new HotkeyReference(GetHotkeyReference(name));
public HotkeyReference this[string name] => new(GetHotkeyReference(name));
public IEnumerable<HotkeyDefinition> Definitions => definitions.Values;
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -9,12 +9,41 @@
*/
#endregion
using System;
using System.Reflection;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA
{
public class Utility
{
static readonly ConcurrentCache<Type, FieldInfo[]> TypeFields =
new(type => type.GetFields());
static readonly ConcurrentCache<(MemberInfo Member, Type AttributeType), bool> MemberHasAttribute =
new(x => Attribute.IsDefined(x.Member, x.AttributeType));
static readonly ConcurrentCache<(MemberInfo Member, Type AttributeType, bool Inherit), object[]> MemberCustomAttributes =
new(x => x.Member.GetCustomAttributes(x.AttributeType, x.Inherit));
public static FieldInfo[] GetFields(Type type)
{
return TypeFields[type];
}
public static bool HasAttribute<TAttribute>(MemberInfo member)
where TAttribute : Attribute
{
return MemberHasAttribute[(member, typeof(TAttribute))];
}
public static TAttribute[] GetCustomAttributes<TAttribute>(MemberInfo member, bool inherit)
where TAttribute : Attribute
{
return (TAttribute[])MemberCustomAttributes[(member, typeof(TAttribute), inherit)];
}
public readonly ModData ModData;
public readonly InstalledMods Mods;

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -15,7 +15,7 @@ namespace OpenRA
{
public readonly struct Hotkey : IEquatable<Hotkey>
{
public static Hotkey Invalid = new Hotkey(Keycode.UNKNOWN, Modifiers.None);
public static Hotkey Invalid = new(Keycode.UNKNOWN, Modifiers.None);
public bool IsValid()
{
return Key != Keycode.UNKNOWN;
@@ -42,7 +42,7 @@ namespace OpenRA
var mods = Modifiers.None;
if (parts.Length >= 2)
{
var modString = s.Substring(s.IndexOf(' '));
var modString = s[s.IndexOf(' ')..];
if (!Enum<Modifiers>.TryParse(modString, true, out mods))
return false;
}
@@ -84,7 +84,7 @@ namespace OpenRA
return obj is Hotkey o && (Hotkey?)o == this;
}
public override string ToString() { return $"{Key} {Modifiers.ToString("F")}"; }
public override string ToString() { return $"{Key} {Modifiers:F}"; }
public string DisplayString()
{

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -14,7 +14,7 @@ using System;
namespace OpenRA
{
/// <summary>
/// A reference to either a named hotkey (defined in the game settings) or a statically assigned hotkey
/// A reference to either a named hotkey (defined in the game settings) or a statically assigned hotkey.
/// </summary>
public class HotkeyReference
{

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -13,7 +13,8 @@ using System.Collections.Generic;
namespace OpenRA
{
// List of keycodes, duplicated from SDL 2.0.1
// List of keycodes. Duplicated from SDL 2.0.1, with the addition
// of MOUSE4 and MOUSE5.
public enum Keycode
{
UNKNOWN = 0,
@@ -252,11 +253,13 @@ namespace OpenRA
KBDILLUMUP = 280 | (1 << 30),
EJECT = 281 | (1 << 30),
SLEEP = 282 | (1 << 30),
MOUSE4 = 283 | (1 << 30),
MOUSE5 = 284 | (1 << 30)
}
public static class KeycodeExts
{
static readonly Dictionary<Keycode, string> KeyNames = new Dictionary<Keycode, string>
static readonly Dictionary<Keycode, string> KeyNames = new()
{
{ Keycode.UNKNOWN, "Undefined" },
{ Keycode.RETURN, "Return" },
@@ -494,6 +497,8 @@ namespace OpenRA
{ Keycode.KBDILLUMUP, "KBDIllumUp" },
{ Keycode.EJECT, "Eject" },
{ Keycode.SLEEP, "Sleep" },
{ Keycode.MOUSE4, "Mouse 4" },
{ Keycode.MOUSE5, "Mouse 5" },
};
public static string DisplayString(Keycode k)

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -54,7 +54,7 @@ namespace OpenRA
return mods;
}
Manifest LoadMod(string id, string path)
static Manifest LoadMod(string id, string path)
{
IReadOnlyPackage package = null;
try
@@ -79,7 +79,7 @@ namespace OpenRA
return null;
}
Dictionary<string, Manifest> GetInstalledMods(IEnumerable<string> searchPaths, IEnumerable<string> explicitPaths)
static Dictionary<string, Manifest> GetInstalledMods(IEnumerable<string> searchPaths, IEnumerable<string> explicitPaths)
{
var ret = new Dictionary<string, Manifest>();
var candidates = GetCandidateMods(searchPaths)

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -66,8 +66,10 @@ namespace OpenRA
}
catch (Exception e)
{
Console.WriteLine("Failed to load keys: {0}", e);
Log.Write("debug", "Failed to load player keypair from `{0}` with exception: {1}", filePath, e);
Console.WriteLine("Failed to load keys:");
Console.WriteLine(e);
Log.Write("debug", $"Failed to load player keypair from `{filePath}` with exception:");
Log.Write("debug", e);
}
}
@@ -91,7 +93,7 @@ namespace OpenRA
innerData = FieldLoader.Load<PlayerProfile>(yaml.Value);
if (innerData.KeyRevoked)
{
Log.Write("debug", "Revoking key with fingerprint {0}", Fingerprint);
Log.Write("debug", $"Revoking key with fingerprint {Fingerprint}");
DeleteKeypair();
}
else
@@ -102,7 +104,8 @@ namespace OpenRA
}
catch (Exception e)
{
Log.Write("debug", "Failed to parse player data result with exception: {0}", e);
Log.Write("debug", "Failed to parse player data result with exception:");
Log.Write("debug", e);
innerState = LinkState.ConnectionFailed;
}
finally
@@ -136,8 +139,10 @@ namespace OpenRA
}
catch (Exception e)
{
Log.Write("debug", "Failed to generate keypair with exception: {1}", e);
Console.WriteLine("Key generation failed: {0}", e);
Log.Write("debug", "Failed to generate keypair with exception:");
Log.Write("debug", e);
Console.WriteLine("Key generation failed:");
Console.WriteLine(e);
innerState = LinkState.Uninitialized;
}
@@ -152,8 +157,10 @@ namespace OpenRA
}
catch (Exception e)
{
Log.Write("debug", "Failed to delete keypair with exception: {1}", e);
Console.WriteLine("Key deletion failed: {0}", e);
Log.Write("debug", "Failed to delete keypair with exception:");
Log.Write("debug", e);
Console.WriteLine("Key deletion failed:");
Console.WriteLine(e);
}
innerState = LinkState.Uninitialized;

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -19,7 +19,7 @@ namespace OpenRA
public readonly int U, V;
public MPos(int u, int v) { U = u; V = v; }
public static readonly MPos Zero = new MPos(0, 0);
public static readonly MPos Zero = new(0, 0);
public static bool operator ==(MPos me, MPos other) { return me.U == other.U && me.V == other.V; }
public static bool operator !=(MPos me, MPos other) { return !(me == other); }
@@ -27,7 +27,7 @@ namespace OpenRA
public override int GetHashCode() { return U.GetHashCode() ^ V.GetHashCode(); }
public bool Equals(MPos other) { return other == this; }
public override bool Equals(object obj) { return obj is MPos && Equals((MPos)obj); }
public override bool Equals(object obj) { return obj is MPos uv && Equals(uv); }
public MPos Clamp(Rectangle r)
{
@@ -64,14 +64,14 @@ namespace OpenRA
}
/// <summary>
/// Projected map position
/// Projected map position.
/// </summary>
public readonly struct PPos : IEquatable<PPos>
{
public readonly int U, V;
public PPos(int u, int v) { U = u; V = v; }
public static readonly PPos Zero = new PPos(0, 0);
public static readonly PPos Zero = new(0, 0);
public static bool operator ==(PPos me, PPos other) { return me.U == other.U && me.V == other.V; }
public static bool operator !=(PPos me, PPos other) { return !(me == other); }
@@ -88,7 +88,7 @@ namespace OpenRA
public override int GetHashCode() { return U.GetHashCode() ^ V.GetHashCode(); }
public bool Equals(PPos other) { return other == this; }
public override bool Equals(object obj) { return obj is PPos && Equals((PPos)obj); }
public override bool Equals(object obj) { return obj is PPos puv && Equals(puv); }
public override string ToString() { return U + "," + V; }
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -64,8 +64,8 @@ namespace OpenRA
public bool Hidden;
}
/// <summary> Describes what is to be loaded in order to run a mod. </summary>
public class Manifest : IDisposable
/// <summary>Describes what is to be loaded in order to run a mod.</summary>
public sealed class Manifest : IDisposable
{
public readonly string Id;
public readonly IReadOnlyPackage Package;
@@ -95,7 +95,7 @@ namespace OpenRA
"RequiresMods", "PackageFormats"
};
readonly TypeDictionary modules = new TypeDictionary();
readonly TypeDictionary modules = new();
readonly Dictionary<string, MiniYaml> yaml;
bool customDataLoaded;
@@ -157,25 +157,25 @@ namespace OpenRA
// Allow inherited mods to import parent maps.
var compat = new List<string> { Id };
if (yaml.ContainsKey("SupportsMapsFrom"))
compat.AddRange(yaml["SupportsMapsFrom"].Value.Split(',').Select(c => c.Trim()));
if (yaml.TryGetValue("SupportsMapsFrom", out var entry))
compat.AddRange(entry.Value.Split(',').Select(c => c.Trim()));
MapCompatibility = compat.ToArray();
if (yaml.ContainsKey("DefaultOrderGenerator"))
DefaultOrderGenerator = yaml["DefaultOrderGenerator"].Value;
if (yaml.TryGetValue("DefaultOrderGenerator", out entry))
DefaultOrderGenerator = entry.Value;
if (yaml.ContainsKey("PackageFormats"))
PackageFormats = FieldLoader.GetValue<string[]>("PackageFormats", yaml["PackageFormats"].Value);
if (yaml.TryGetValue("PackageFormats", out entry))
PackageFormats = FieldLoader.GetValue<string[]>("PackageFormats", entry.Value);
if (yaml.ContainsKey("SoundFormats"))
SoundFormats = FieldLoader.GetValue<string[]>("SoundFormats", yaml["SoundFormats"].Value);
if (yaml.TryGetValue("SoundFormats", out entry))
SoundFormats = FieldLoader.GetValue<string[]>("SoundFormats", entry.Value);
if (yaml.ContainsKey("SpriteFormats"))
SpriteFormats = FieldLoader.GetValue<string[]>("SpriteFormats", yaml["SpriteFormats"].Value);
if (yaml.TryGetValue("SpriteFormats", out entry))
SpriteFormats = FieldLoader.GetValue<string[]>("SpriteFormats", entry.Value);
if (yaml.ContainsKey("VideoFormats"))
VideoFormats = FieldLoader.GetValue<string[]>("VideoFormats", yaml["VideoFormats"].Value);
if (yaml.TryGetValue("VideoFormats", out entry))
VideoFormats = FieldLoader.GetValue<string[]>("VideoFormats", entry.Value);
}
public void LoadCustomData(ObjectCreator oc)

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -54,7 +54,7 @@ namespace OpenRA
// Traits tagged with an instance name prefer inits with the same name.
// If a more specific init is not available, fall back to an unnamed init.
// If duplicate inits are defined, take the last to match standard yaml override expectations
if (info != null && !string.IsNullOrEmpty(info.InstanceName))
if (!string.IsNullOrEmpty(info?.InstanceName))
return inits.LastOrDefault(i => i.InstanceName == info.InstanceName) ??
inits.LastOrDefault(i => string.IsNullOrEmpty(i.InstanceName));
@@ -159,9 +159,9 @@ namespace OpenRA
public virtual void Initialize(T value)
{
var field = typeof(ValueActorInit<T>).GetField(nameof(value), BindingFlags.NonPublic | BindingFlags.Instance);
if (field != null)
field.SetValue(this, value);
typeof(ValueActorInit<T>)
.GetField(nameof(value), BindingFlags.NonPublic | BindingFlags.Instance)
?.SetValue(this, value);
}
public override MiniYaml Save()
@@ -246,16 +246,16 @@ namespace OpenRA
public void Initialize(MiniYaml yaml)
{
var field = typeof(OwnerInit).GetField(nameof(InternalName), BindingFlags.Public | BindingFlags.Instance);
if (field != null)
field.SetValue(this, yaml.Value);
typeof(OwnerInit)
.GetField(nameof(InternalName), BindingFlags.Public | BindingFlags.Instance)
?.SetValue(this, yaml.Value);
}
public void Initialize(Player player)
{
var field = typeof(OwnerInit).GetField(nameof(value), BindingFlags.NonPublic | BindingFlags.Instance);
if (field != null)
field.SetValue(this, player);
typeof(OwnerInit)
.GetField(nameof(value), BindingFlags.NonPublic | BindingFlags.Instance)
?.SetValue(this, player);
}
public override MiniYaml Save()

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -22,7 +22,7 @@ namespace OpenRA
{
public interface ISuppressInitExport { }
public class ActorReference : IEnumerable
public class ActorReference : IEnumerable<object>
{
public string Type;
readonly Lazy<TypeDictionary> initDict;
@@ -87,14 +87,14 @@ namespace OpenRA
var ret = new MiniYaml(Type);
foreach (var o in initDict.Value)
{
if (!(o is ActorInit init) || o is ISuppressInitExport)
if (o is not ActorInit init || o is ISuppressInitExport)
continue;
if (initFilter != null && !initFilter(init))
continue;
var initTypeName = init.GetType().Name;
var initName = initTypeName.Substring(0, initTypeName.Length - 4);
var initName = initTypeName[..^4];
if (!string.IsNullOrEmpty(init.InstanceName))
initName += ActorInfo.TraitInstanceSeparator + init.InstanceName;
@@ -104,7 +104,9 @@ namespace OpenRA
return ret;
}
public IEnumerator GetEnumerator() { return initDict.Value.GetEnumerator(); }
public IEnumerator<object> GetEnumerator() { return initDict.Value.GetEnumerator(); }
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
public ActorReference Clone()
{

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -72,7 +72,7 @@ namespace OpenRA
return uv.V * Size.Width + uv.U;
}
/// <summary>Gets or sets the <see cref="CellLayer"/> using cell coordinates</summary>
/// <summary>Gets or sets the <see cref="CellLayer"/> using cell coordinates.</summary>
public T this[CPos cell]
{
get => Entries[Index(cell)];
@@ -85,7 +85,7 @@ namespace OpenRA
}
}
/// <summary>Gets or sets the layer contents using raw map coordinates (not CPos!)</summary>
/// <summary>Gets or sets the layer contents using raw map coordinates (not CPos!).</summary>
public T this[MPos uv]
{
get => Entries[Index(uv)];

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -24,10 +24,10 @@ namespace OpenRA
protected readonly T[] Entries;
protected readonly Rectangle Bounds;
public CellLayerBase(Map map)
protected CellLayerBase(Map map)
: this(map.Grid.Type, new Size(map.MapSize.X, map.MapSize.Y)) { }
public CellLayerBase(MapGridType gridType, Size size)
protected CellLayerBase(MapGridType gridType, Size size)
{
Size = size;
Bounds = new Rectangle(0, 0, Size.Width, Size.Height);
@@ -43,13 +43,13 @@ namespace OpenRA
Array.Copy(anotherLayer.Entries, Entries, Entries.Length);
}
/// <summary>Clears the layer contents with their default value</summary>
/// <summary>Clears the layer contents with their default value.</summary>
public virtual void Clear()
{
Array.Clear(Entries, 0, Entries.Length);
}
/// <summary>Clears the layer contents with a known value</summary>
/// <summary>Clears the layer contents with a known value.</summary>
public virtual void Clear(T clearValue)
{
Array.Fill(Entries, clearValue);

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -102,7 +102,7 @@ namespace OpenRA
return uv.U >= mapTopLeft.U && uv.U <= mapBottomRight.U && uv.V >= mapTopLeft.V && uv.V <= mapBottomRight.V;
}
public MapCoordsRegion MapCoords => new MapCoordsRegion(mapTopLeft, mapBottomRight);
public MapCoordsRegion MapCoords => new(mapTopLeft, mapBottomRight);
public CellRegionEnumerator GetEnumerator()
{
@@ -126,15 +126,12 @@ namespace OpenRA
// Current position, in map coordinates
int u, v;
// Current position, in cell coordinates
CPos current;
public CellRegionEnumerator(CellRegion region)
: this()
{
r = region;
Reset();
current = new MPos(u, v).ToCPos(r.gridType);
Current = new MPos(u, v).ToCPos(r.gridType);
}
public bool MoveNext()
@@ -152,7 +149,8 @@ namespace OpenRA
return false;
}
current = new MPos(u, v).ToCPos(r.gridType);
// Current position, in cell coordinates
Current = new MPos(u, v).ToCPos(r.gridType);
return true;
}
@@ -163,7 +161,7 @@ namespace OpenRA
v = r.mapTopLeft.V;
}
public CPos Current => current;
public CPos Current { get; private set; }
object IEnumerator.Current => Current;
public void Dispose() { }
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -65,7 +65,7 @@ namespace OpenRA
MissionSelector = 4
}
class MapField
sealed class MapField
{
enum Type { Normal, NodeList, MiniYaml }
readonly FieldInfo field;
@@ -149,13 +149,13 @@ namespace OpenRA
}
}
public class Map : IReadOnlyFileSystem
public sealed class Map : IReadOnlyFileSystem, IDisposable
{
public const int SupportedMapFormat = 11;
public const int CurrentMapFormat = 12;
const short InvalidCachedTerrainIndex = -1;
/// <summary>Defines the order of the fields in map.yaml</summary>
/// <summary>Defines the order of the fields in map.yaml.</summary>
static readonly MapField[] YamlFields =
{
new MapField("MapFormat"),
@@ -167,11 +167,11 @@ namespace OpenRA
new MapField("Bounds"),
new MapField("Visibility"),
new MapField("Categories"),
new MapField("Translations", required: false, ignoreIfValue: ""),
new MapField("LockPreview", required: false, ignoreIfValue: "False"),
new MapField("Players", "PlayerDefinitions"),
new MapField("Actors", "ActorDefinitions"),
new MapField("Rules", "RuleDefinitions", required: false),
new MapField("Translations", "TranslationDefinitions", required: false),
new MapField("Sequences", "SequenceDefinitions", required: false),
new MapField("ModelSequences", "ModelSequenceDefinitions", required: false),
new MapField("Weapons", "WeaponDefinitions", required: false),
@@ -193,16 +193,16 @@ namespace OpenRA
public Rectangle Bounds;
public MapVisibility Visibility = MapVisibility.Lobby;
public string[] Categories = { "Conquest" };
public string[] Translations;
public int2 MapSize { get; private set; }
// Player and actor yaml. Public for access by the map importers and lint checks.
public List<MiniYamlNode> PlayerDefinitions = new List<MiniYamlNode>();
public List<MiniYamlNode> ActorDefinitions = new List<MiniYamlNode>();
public List<MiniYamlNode> PlayerDefinitions = new();
public List<MiniYamlNode> ActorDefinitions = new();
// Custom map yaml. Public for access by the map importers and lint checks
public readonly MiniYaml RuleDefinitions;
public readonly MiniYaml TranslationDefinitions;
public readonly MiniYaml SequenceDefinitions;
public readonly MiniYaml ModelSequenceDefinitions;
public readonly MiniYaml WeaponDefinitions;
@@ -210,7 +210,7 @@ namespace OpenRA
public readonly MiniYaml MusicDefinitions;
public readonly MiniYaml NotificationDefinitions;
public readonly Dictionary<CPos, TerrainTile> ReplacedInvalidTerrainTiles = new Dictionary<CPos, TerrainTile>();
public readonly Dictionary<CPos, TerrainTile> ReplacedInvalidTerrainTiles = new();
// Generated data
public readonly MapGrid Grid;
@@ -218,6 +218,8 @@ namespace OpenRA
public string Uid { get; private set; }
public Ruleset Rules { get; private set; }
public SequenceSet Sequences { get; private set; }
public bool InvalidCustomRules { get; private set; }
public Exception InvalidCustomRulesException { get; private set; }
@@ -243,6 +245,8 @@ namespace OpenRA
public CellRegion AllCells { get; private set; }
public List<CPos> AllEdgeCells { get; private set; }
public event Action<CPos> CellProjectionChanged;
// Internal data
readonly ModData modData;
CellLayer<short> cachedTerrainIndexes;
@@ -252,8 +256,6 @@ namespace OpenRA
CellLayer<byte> projectedHeight;
Rectangle projectionSafeBounds;
internal Translation Translation;
public static string ComputeUID(IReadOnlyPackage package)
{
return ComputeUID(package, GetMapFormat(package));
@@ -336,11 +338,12 @@ namespace OpenRA
Height = new CellLayer<byte>(Grid.Type, size);
Ramp = new CellLayer<byte>(Grid.Type, size);
Tiles.Clear(terrainInfo.DefaultTerrainTile);
if (Grid.MaximumTerrainHeight > 0)
{
Height.CellEntryChanged += UpdateProjection;
Tiles.CellEntryChanged += UpdateProjection;
Tiles.CellEntryChanged += UpdateRamp;
Tiles.CellEntryChanged += UpdateProjection;
Height.CellEntryChanged += UpdateProjection;
}
PostInit();
@@ -387,7 +390,7 @@ namespace OpenRA
// TODO: Remember to remove this when rewriting tile variants / PickAny
if (index == byte.MaxValue)
index = (byte)(i % 4 + (j % 4) * 4);
index = (byte)(i % 4 + j % 4 * 4);
Tiles[new MPos(i, j)] = new TerrainTile(tile, index);
}
@@ -434,19 +437,18 @@ namespace OpenRA
try
{
Rules = Ruleset.Load(modData, this, Tileset, RuleDefinitions, WeaponDefinitions,
VoiceDefinitions, NotificationDefinitions, MusicDefinitions, SequenceDefinitions, ModelSequenceDefinitions);
VoiceDefinitions, NotificationDefinitions, MusicDefinitions, ModelSequenceDefinitions);
}
catch (Exception e)
{
Log.Write("debug", "Failed to load rules for {0} with error {1}", Title, e);
Log.Write("debug", $"Failed to load rules for {Title} with error");
Log.Write("debug", e);
InvalidCustomRules = true;
InvalidCustomRulesException = e;
Rules = Ruleset.LoadDefaultsForTileSet(modData, Tileset);
}
Rules.Sequences.Preload();
Translation = new Translation(Game.Settings.Player.Language, Translations, this);
Sequences = new SequenceSet(this, modData, Tileset, SequenceDefinitions);
var tl = new MPos(0, 0).ToCPos(this);
var br = new MPos(MapSize.X - 1, MapSize.Y - 1).ToCPos(this);
@@ -477,17 +479,17 @@ namespace OpenRA
AllEdgeCells = UpdateEdgeCells();
// Invalidate the entry for a cell if anything could cause the terrain index to change.
Action<CPos> invalidateTerrainIndex = c =>
void InvalidateTerrainIndex(CPos c)
{
if (cachedTerrainIndexes != null)
cachedTerrainIndexes[c] = InvalidCachedTerrainIndex;
};
}
// Even though the cache is lazily initialized, we must attach these event handlers on init.
// This ensures our handler to invalidate the cache runs first,
// so other listeners to these same events will get correct data when calling GetTerrainIndex.
CustomTerrain.CellEntryChanged += invalidateTerrainIndex;
Tiles.CellEntryChanged += invalidateTerrainIndex;
CustomTerrain.CellEntryChanged += InvalidateTerrainIndex;
Tiles.CellEntryChanged += InvalidateTerrainIndex;
}
void UpdateRamp(CPos cell)
@@ -530,6 +532,7 @@ namespace OpenRA
var inverse = inverseCellProjection[uv];
inverse.Clear();
inverse.Add(uv);
CellProjectionChanged?.Invoke(cell);
return;
}
@@ -567,6 +570,8 @@ namespace OpenRA
projectedHeight[temp] = height;
}
}
CellProjectionChanged?.Invoke(cell);
}
byte ProjectedCellHeightInner(PPos puv)
@@ -642,12 +647,21 @@ namespace OpenRA
toPackage.Update(file, Package.GetStream(file).ReadAllBytes());
if (!LockPreview)
toPackage.Update("map.png", SavePreview());
{
var previewData = SavePreview();
if (Package != toPackage || !Enumerable.SequenceEqual(previewData, Package.GetStream("map.png").ReadAllBytes()))
toPackage.Update("map.png", previewData);
}
// Update the package with the new map data
var s = root.WriteToString();
toPackage.Update("map.yaml", Encoding.UTF8.GetBytes(s));
toPackage.Update("map.bin", SaveBinaryData());
var textData = Encoding.UTF8.GetBytes(root.WriteToString());
if (Package != toPackage || !Enumerable.SequenceEqual(textData, Package.GetStream("map.yaml").ReadAllBytes()))
toPackage.Update("map.yaml", textData);
var binaryData = SaveBinaryData();
if (Package != toPackage || !Enumerable.SequenceEqual(binaryData, Package.GetStream("map.bin").ReadAllBytes()))
toPackage.Update("map.bin", binaryData);
Package = toPackage;
// Update UID to match the newly saved data
@@ -733,8 +747,8 @@ namespace OpenRA
public byte[] SavePreview()
{
var actorTypes = Rules.Actors.Values.Where(a => a.HasTraitInfo<IMapPreviewSignatureInfo>());
var actors = ActorDefinitions.Where(a => actorTypes.Where(ai => ai.Name == a.Value.Value).Any());
var positions = new List<(MPos Position, Color Color)>();
var actors = ActorDefinitions.Where(a => actorTypes.Any(ai => ai.Name == a.Value.Value));
var positions = new List<(MPos Uv, Color Color)>();
foreach (var actor in actors)
{
var s = new ActorReference(actor.Value.Value, actor.Value.ToDictionary());
@@ -791,14 +805,17 @@ namespace OpenRA
var minimapData = new byte[stride * height];
(Color Left, Color Right) terrainColor = default;
var colorsByPosition = positions
.GroupBy(p => p.Uv)
.ToDictionary(g => g.Key, g => g.First().Color);
for (var y = 0; y < height; y++)
{
for (var x = 0; x < width; x++)
{
var uv = new MPos(x + Bounds.Left, y + top);
// FirstOrDefault will return a (MPos.Zero, Color.Transparent) if positions is empty
var actorColor = positions.FirstOrDefault(ap => ap.Position == uv).Color;
// TryGetValue will return Color.Transparent if not found
colorsByPosition.TryGetValue(uv, out var actorColor);
if (actorColor.A == 0)
terrainColor = GetTerrainColorPair(uv);
@@ -977,11 +994,11 @@ namespace OpenRA
}
/// <summary>
/// The size of the map Height step in world units
/// The size of the map Height step in world units.
/// </summary>
/// RectangularIsometric defines 1024 units along the diagonal axis,
/// giving a half-tile height step of sqrt(2) * 512
public WDist CellHeightStep => new WDist(Grid.Type == MapGridType.RectangularIsometric ? 724 : 512);
public WDist CellHeightStep => new(Grid.Type == MapGridType.RectangularIsometric ? 724 : 512);
public CPos CellContaining(WPos pos)
{
@@ -1191,7 +1208,7 @@ namespace OpenRA
// This shouldn't happen. But if it does, return the original value and hope the caller doesn't explode.
if (unProjected.Count == 0)
{
Log.Write("debug", "Failed to clamp map cell {0} to map bounds", uv);
Log.Write("debug", $"Failed to clamp map cell {uv} to map bounds");
return uv;
}
}
@@ -1233,8 +1250,8 @@ namespace OpenRA
if (allProjected.Length > 0)
{
var puv = allProjected.First();
var horizontalBound = ((puv.U - Bounds.Left) < Bounds.Width / 2) ? Bounds.Left : Bounds.Right;
var verticalBound = ((puv.V - Bounds.Top) < Bounds.Height / 2) ? Bounds.Top : Bounds.Bottom;
var horizontalBound = (puv.U - Bounds.Left < Bounds.Width / 2) ? Bounds.Left : Bounds.Right;
var verticalBound = (puv.V - Bounds.Top < Bounds.Height / 2) ? Bounds.Top : Bounds.Bottom;
var du = Math.Abs(horizontalBound - puv.U);
var dv = Math.Abs(verticalBound - puv.V);
@@ -1263,7 +1280,7 @@ namespace OpenRA
// This shouldn't happen. But if it does, return the original value and hope the caller doesn't explode.
if (unProjected.Count == 0)
{
Log.Write("debug", "Failed to find closest edge for map cell {0}", uv);
Log.Write("debug", $"Failed to find closest edge for map cell {uv}");
return uv;
}
}
@@ -1394,12 +1411,9 @@ namespace OpenRA
return false;
}
public string Translate(string key, IDictionary<string, object> args = null)
public void Dispose()
{
if (Translation.TryGetString(key, out var message, args))
return message;
return modData.Translation.GetString(key, args);
Sequences.Dispose();
}
}
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -25,32 +25,33 @@ namespace OpenRA
{
public sealed class MapCache : IEnumerable<MapPreview>, IDisposable
{
public static readonly MapPreview UnknownMap = new MapPreview(null, null, MapGridType.Rectangular, null);
public static readonly MapPreview UnknownMap = new(null, null, MapGridType.Rectangular, null);
public IReadOnlyDictionary<IReadOnlyPackage, MapClassification> MapLocations => mapLocations;
readonly Dictionary<IReadOnlyPackage, MapClassification> mapLocations = new Dictionary<IReadOnlyPackage, MapClassification>();
readonly Dictionary<IReadOnlyPackage, MapClassification> mapLocations = new();
public bool LoadPreviewImages = true;
readonly Cache<string, MapPreview> previews;
readonly ModData modData;
readonly SheetBuilder sheetBuilder;
Thread previewLoaderThread;
bool previewLoaderThreadShutDown = true;
readonly object syncRoot = new object();
readonly Queue<MapPreview> generateMinimap = new Queue<MapPreview>();
readonly object syncRoot = new();
readonly Queue<MapPreview> generateMinimap = new();
public Dictionary<string, string> StringPool { get; } = new Dictionary<string, string>();
readonly List<MapDirectoryTracker> mapDirectoryTrackers = new List<MapDirectoryTracker>();
readonly List<MapDirectoryTracker> mapDirectoryTrackers = new();
/// <summary>
/// The most recenly modified or loaded map at runtime
/// The most recently modified or loaded map at runtime.
/// </summary>
public string LastModifiedMap { get; private set; } = null;
readonly Dictionary<string, string> mapUpdates = new Dictionary<string, string>();
readonly Dictionary<string, string> mapUpdates = new();
string lastLoadedLastModifiedMap;
/// <summary>
/// If LastModifiedMap was picked already, returns a null
/// If LastModifiedMap was picked already, returns a null.
/// </summary>
public string PickLastModifiedMap(MapVisibility visibility)
{
@@ -98,7 +99,7 @@ namespace OpenRA
IReadOnlyPackage package;
var optional = name.StartsWith("~", StringComparison.Ordinal);
if (optional)
name = name.Substring(1);
name = name[1..];
try
{
@@ -122,10 +123,12 @@ namespace OpenRA
mapDirectoryTrackers.Add(new MapDirectoryTracker(mapGrid, package, classification));
}
// PERF: Load the mod YAML once outside the loop, and reuse it when resolving each maps custom YAML.
var modDataRules = modData.GetRulesYaml();
foreach (var kv in MapLocations)
{
foreach (var map in kv.Key.Contents)
LoadMap(map, kv.Key, kv.Value, mapGrid, null);
LoadMapInternal(map, kv.Key, kv.Value, mapGrid, null, modDataRules);
}
// We only want to track maps in runtime, not at loadtime
@@ -133,17 +136,22 @@ namespace OpenRA
}
public void LoadMap(string map, IReadOnlyPackage package, MapClassification classification, MapGrid mapGrid, string oldMap)
{
LoadMapInternal(map, package, classification, mapGrid, oldMap, null);
}
void LoadMapInternal(string map, IReadOnlyPackage package, MapClassification classification, MapGrid mapGrid, string oldMap, IEnumerable<List<MiniYamlNode>> modDataRules)
{
IReadOnlyPackage mapPackage = null;
try
{
using (new Support.PerfTimer(map))
using (new PerfTimer(map))
{
mapPackage = package.OpenPackage(map, modData.ModFiles);
if (mapPackage != null)
{
var uid = Map.ComputeUID(mapPackage);
previews[uid].UpdateFromMap(mapPackage, package, classification, modData.Manifest.MapCompatibility, mapGrid.Type);
previews[uid].UpdateFromMap(mapPackage, package, classification, modData.Manifest.MapCompatibility, mapGrid.Type, modDataRules);
if (oldMap != uid)
{
@@ -157,10 +165,12 @@ namespace OpenRA
catch (Exception e)
{
mapPackage?.Dispose();
Console.WriteLine("Failed to load map: {0}", map);
Console.WriteLine("Details: {0}", e);
Log.Write("debug", "Failed to load map: {0}", map);
Log.Write("debug", "Details: {0}", e);
Console.WriteLine($"Failed to load map: {map}");
Console.WriteLine("Details:");
Console.WriteLine(e);
Log.Write("debug", $"Failed to load map: {map}");
Log.Write("debug", "Details:");
Log.Write("debug", e);
}
}
@@ -182,7 +192,7 @@ namespace OpenRA
var name = kv.Key;
var optional = name.StartsWith("~", StringComparison.Ordinal);
if (optional)
name = name.Substring(1);
name = name[1..];
// Don't try to open the map directory in the support directory if it doesn't exist
var resolved = Platform.ResolvePath(name);
@@ -194,7 +204,7 @@ namespace OpenRA
}
}
public IEnumerable<(IReadWritePackage package, string map)> EnumerateMapDirPackagesAndNames(MapClassification classification = MapClassification.System)
public IEnumerable<(IReadWritePackage Package, string Map)> EnumerateMapDirPackagesAndNames(MapClassification classification = MapClassification.System)
{
var mapDirPackages = EnumerateMapDirPackages(classification);
@@ -213,12 +223,6 @@ namespace OpenRA
yield return mapPackage;
}
public IEnumerable<Map> EnumerateMapsWithoutCaching(MapClassification classification = MapClassification.System)
{
foreach (var mapPackage in EnumerateMapPackagesWithoutCaching(classification))
yield return new Map(modData, mapPackage);
}
public void QueryRemoteMapDetails(string repositoryUrl, IEnumerable<string> uids, Action<MapPreview> mapDetailsReceived = null, Action<MapPreview> mapQueryFailed = null)
{
var queryUids = uids.Distinct()
@@ -258,8 +262,9 @@ namespace OpenRA
}
catch (Exception e)
{
Log.Write("debug", "Remote map query failed with error: {0}", e);
Log.Write("debug", "URL was: {0}", url);
Log.Write("debug", "Remote map query failed with error:");
Log.Write("debug", e);
Log.Write("debug", $"URL was: {url}");
foreach (var uid in batchUids)
{
@@ -320,7 +325,8 @@ namespace OpenRA
}
catch (Exception e)
{
Log.Write("debug", "Failed to load minimap with exception: {0}", e);
Log.Write("debug", "Failed to load minimap with exception:");
Log.Write("debug", e);
}
});
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -19,7 +19,6 @@ namespace OpenRA
public struct MapCoordsEnumerator : IEnumerator<MPos>
{
readonly MapCoordsRegion r;
MPos current;
public MapCoordsEnumerator(MapCoordsRegion region)
: this()
@@ -30,41 +29,38 @@ namespace OpenRA
public bool MoveNext()
{
var u = current.U + 1;
var v = current.V;
var u = Current.U + 1;
var v = Current.V;
// Check for column overflow
if (u > r.bottomRight.U)
if (u > r.BottomRight.U)
{
v += 1;
u = r.topLeft.U;
u = r.TopLeft.U;
// Check for row overflow
if (v > r.bottomRight.V)
if (v > r.BottomRight.V)
return false;
}
current = new MPos(u, v);
Current = new MPos(u, v);
return true;
}
public void Reset()
{
current = new MPos(r.topLeft.U - 1, r.topLeft.V);
Current = new MPos(r.TopLeft.U - 1, r.TopLeft.V);
}
public MPos Current => current;
public MPos Current { get; private set; }
object IEnumerator.Current => Current;
public void Dispose() { }
}
readonly MPos topLeft;
readonly MPos bottomRight;
public MapCoordsRegion(MPos mapTopLeft, MPos mapBottomRight)
{
topLeft = mapTopLeft;
bottomRight = mapBottomRight;
TopLeft = mapTopLeft;
BottomRight = mapBottomRight;
}
public override string ToString()
@@ -87,7 +83,7 @@ namespace OpenRA
return GetEnumerator();
}
public MPos TopLeft => topLeft;
public MPos BottomRight => bottomRight;
public MPos TopLeft { get; }
public MPos BottomRight { get; }
}
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -25,7 +25,7 @@ namespace OpenRA
readonly MapClassification classification;
enum MapAction { Add, Delete, Update }
readonly Dictionary<string, MapAction> mapActionQueue = new Dictionary<string, MapAction>();
readonly Dictionary<string, MapAction> mapActionQueue = new();
bool dirty = false;

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -29,7 +29,10 @@ namespace OpenRA
public readonly WVec[][] Polygons;
public readonly WRot Orientation;
public CellRamp(MapGridType type, WRot orientation, RampCornerHeight tl = RampCornerHeight.Low, RampCornerHeight tr = RampCornerHeight.Low, RampCornerHeight br = RampCornerHeight.Low, RampCornerHeight bl = RampCornerHeight.Low, RampSplit split = RampSplit.Flat)
public CellRamp(MapGridType type, WRot orientation,
RampCornerHeight tl = RampCornerHeight.Low, RampCornerHeight tr = RampCornerHeight.Low,
RampCornerHeight br = RampCornerHeight.Low, RampCornerHeight bl = RampCornerHeight.Low,
RampSplit split = RampSplit.Flat)
{
Orientation = orientation;
if (type == MapGridType.RectangularIsometric)
@@ -103,7 +106,7 @@ namespace OpenRA
public class MapGrid : IGlobalModData
{
public readonly MapGridType Type = MapGridType.Rectangular;
public readonly Size TileSize = new Size(24, 24);
public readonly Size TileSize = new(24, 24);
public readonly byte MaximumTerrainHeight = 0;
public readonly SubCell DefaultSubCell = (SubCell)byte.MaxValue;
@@ -125,10 +128,14 @@ namespace OpenRA
internal readonly CVec[][] TilesByDistance;
public int TileScale { get; }
public MapGrid(MiniYaml yaml)
{
FieldLoader.Load(this, yaml);
TileScale = Type == MapGridType.RectangularIsometric ? 1448 : 1024;
// The default subcell index defaults to the middle entry
var defaultSubCellIndex = (byte)DefaultSubCell;
if (defaultSubCellIndex == byte.MaxValue)

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
@@ -61,10 +61,10 @@ namespace OpenRA
public readonly int mapformat;
}
public class MapPreview : IDisposable, IReadOnlyFileSystem
public sealed class MapPreview : IDisposable, IReadOnlyFileSystem
{
/// <summary>Wrapper that enables map data to be replaced in an atomic fashion</summary>
class InnerData
/// <summary>Wrapper that enables map data to be replaced in an atomic fashion.</summary>
sealed class InnerData
{
public int MapFormat;
public string Title;
@@ -90,6 +90,7 @@ namespace OpenRA
public MiniYaml SequenceDefinitions;
public MiniYaml ModelSequenceDefinitions;
public Translation Translation { get; private set; }
public ActorInfo WorldActorInfo { get; private set; }
public ActorInfo PlayerActorInfo { get; private set; }
@@ -110,7 +111,7 @@ namespace OpenRA
return key == "world" || key == "player";
}
public void SetCustomRules(ModData modData, IReadOnlyFileSystem fileSystem, Dictionary<string, MiniYaml> yaml)
public void SetCustomRules(ModData modData, IReadOnlyFileSystem fileSystem, Dictionary<string, MiniYaml> yaml, IEnumerable<List<MiniYamlNode>> modDataRules)
{
RuleDefinitions = LoadRuleSection(yaml, "Rules");
WeaponDefinitions = LoadRuleSection(yaml, "Weapons");
@@ -120,20 +121,27 @@ namespace OpenRA
SequenceDefinitions = LoadRuleSection(yaml, "Sequences");
ModelSequenceDefinitions = LoadRuleSection(yaml, "ModelSequences");
Translation = yaml.TryGetValue("Translations", out var node) && node != null
? new Translation(Game.Settings.Player.Language, FieldLoader.GetValue<string[]>("value", node.Value), fileSystem)
: null;
try
{
// PERF: Implement a minimal custom loader for custom world and player actors to minimize loading time
// This assumes/enforces that these actor types can only inherit abstract definitions (starting with ^)
if (RuleDefinitions != null)
{
var files = modData.Manifest.Rules.AsEnumerable();
modDataRules ??= modData.GetRulesYaml();
var files = Enumerable.Empty<string>();
if (RuleDefinitions.Value != null)
{
var mapFiles = FieldLoader.GetValue<string[]>("value", RuleDefinitions.Value);
files = files.Append(mapFiles);
}
var sources = files.Select(s => MiniYaml.FromStream(fileSystem.Open(s), s).Where(IsLoadableRuleDefinition).ToList());
var sources =
modDataRules.Select(x => x.Where(IsLoadableRuleDefinition).ToList())
.Concat(files.Select(s => MiniYaml.FromStream(fileSystem.Open(s), s).Where(IsLoadableRuleDefinition).ToList()));
if (RuleDefinitions.Nodes.Count > 0)
sources = sources.Append(RuleDefinitions.Nodes.Where(IsLoadableRuleDefinition).ToList());
@@ -145,7 +153,8 @@ namespace OpenRA
}
catch (Exception e)
{
Log.Write("debug", "Failed to load rules for `{0}` with error: {1}", Title, e.Message);
Log.Write("debug", $"Failed to load rules for `{Title}` with error:");
Log.Write("debug", e);
}
WorldActorInfo = modData.DefaultRules.Actors[SystemActors.World];
@@ -185,6 +194,7 @@ namespace OpenRA
public MiniYaml RuleDefinitions => innerData.RuleDefinitions;
public MiniYaml WeaponDefinitions => innerData.WeaponDefinitions;
public MiniYaml SequenceDefinitions => innerData.SequenceDefinitions;
public ActorInfo WorldActorInfo => innerData.WorldActorInfo;
public ActorInfo PlayerActorInfo => innerData.PlayerActorInfo;
@@ -193,6 +203,19 @@ namespace OpenRA
public long DownloadBytes { get; private set; }
public int DownloadPercentage { get; private set; }
/// <summary>
/// Functionality mirrors <see cref="TranslationProvider.GetString"/>, except instead of using
/// loaded <see cref="Map"/>'s translations as backup, we use this <see cref="MapPreview"/>'s.
/// </summary>
public string GetLocalisedString(string key, IDictionary<string, object> args = null)
{
// PERF: instead of loading mod level Translation per each MapPreview, reuse the already loaded one in TranslationProvider.
if (TranslationProvider.TryGetModString(key, out var message, args))
return message;
return innerData.Translation?.GetString(key, args) ?? key;
}
Sprite minimap;
bool generatingMinimap;
public Sprite GetMinimap()
@@ -226,7 +249,7 @@ namespace OpenRA
{
return Ruleset.Load(modData, this, TileSet, innerData.RuleDefinitions,
innerData.WeaponDefinitions, innerData.VoiceDefinitions, innerData.NotificationDefinitions,
innerData.MusicDefinitions, innerData.SequenceDefinitions, innerData.ModelSequenceDefinitions);
innerData.MusicDefinitions, innerData.ModelSequenceDefinitions);
}
public MapPreview(ModData modData, string uid, MapGridType gridType, MapCache cache)
@@ -292,16 +315,17 @@ namespace OpenRA
innerData.SetCustomRules(modData, this, new Dictionary<string, MiniYaml>()
{
{ "Rules", map.RuleDefinitions },
{ "Translations", map.TranslationDefinitions },
{ "Weapons", map.WeaponDefinitions },
{ "Voices", map.VoiceDefinitions },
{ "Music", map.MusicDefinitions },
{ "Notifications", map.NotificationDefinitions },
{ "Sequences", map.SequenceDefinitions },
{ "ModelSequences", map.ModelSequenceDefinitions }
});
}, null);
}
public void UpdateFromMap(IReadOnlyPackage p, IReadOnlyPackage parent, MapClassification classification, string[] mapCompatibility, MapGridType gridType)
public void UpdateFromMap(IReadOnlyPackage p, IReadOnlyPackage parent, MapClassification classification, string[] mapCompatibility, MapGridType gridType, IEnumerable<List<MiniYamlNode>> modDataRules)
{
Dictionary<string, MiniYaml> yaml;
using (var yamlStream = p.GetStream("map.yaml"))
@@ -344,7 +368,7 @@ namespace OpenRA
if (yaml.TryGetValue("Visibility", out temp))
newData.Visibility = FieldLoader.GetValue<MapVisibility>("Visibility", temp.Value);
string requiresMod = string.Empty;
var requiresMod = string.Empty;
if (yaml.TryGetValue("RequiresMod", out temp))
requiresMod = temp.Value;
@@ -391,9 +415,9 @@ namespace OpenRA
newData.Status = MapStatus.Unavailable;
}
newData.SetCustomRules(modData, this, yaml);
newData.SetCustomRules(modData, this, yaml, modDataRules);
if (p.Contains("map.png"))
if (cache.LoadPreviewImages && p.Contains("map.png"))
using (var dataStream = p.GetStream("map.png"))
newData.Preview = new Png(dataStream);
@@ -435,14 +459,18 @@ namespace OpenRA
spawns[j / 2] = new CPos(r.spawnpoints[j], r.spawnpoints[j + 1]);
newData.SpawnPoints = spawns;
newData.GridType = r.map_grid_type;
try
if (cache.LoadPreviewImages)
{
newData.Preview = new Png(new MemoryStream(Convert.FromBase64String(r.minimap)));
}
catch (Exception e)
{
Log.Write("debug", "Failed parsing mapserver minimap response: {0}", e);
newData.Preview = null;
try
{
newData.Preview = new Png(new MemoryStream(Convert.FromBase64String(r.minimap)));
}
catch (Exception e)
{
Log.Write("debug", "Failed parsing mapserver minimap response:");
Log.Write("debug", e);
newData.Preview = null;
}
}
var playersString = Encoding.UTF8.GetString(Convert.FromBase64String(r.players_block));
@@ -450,11 +478,12 @@ namespace OpenRA
var rulesString = Encoding.UTF8.GetString(Convert.FromBase64String(r.rules));
var rulesYaml = new MiniYaml("", MiniYaml.FromString(rulesString)).ToDictionary();
newData.SetCustomRules(modData, this, rulesYaml);
newData.SetCustomRules(modData, this, rulesYaml, null);
}
catch (Exception e)
{
Log.Write("debug", "Failed parsing mapserver response: {0}", e);
Log.Write("debug", "Failed parsing mapserver response:");
Log.Write("debug", e);
}
// Commit updated data before running the callbacks
@@ -477,7 +506,7 @@ namespace OpenRA
innerData.Status = MapStatus.Downloading;
var installLocation = cache.MapLocations.FirstOrDefault(p => p.Value == MapClassification.User);
if (!(installLocation.Key is IReadWritePackage mapInstallPackage))
if (installLocation.Key is not IReadWritePackage mapInstallPackage)
{
Log.Write("debug", "Map install directory not found");
innerData.Status = MapStatus.DownloadError;
@@ -521,20 +550,21 @@ namespace OpenRA
await response.ReadAsStreamWithProgress(fileStream, OnDownloadProgress, CancellationToken.None);
mapInstallPackage.Update(mapFilename, fileStream.ToArray());
Log.Write("debug", "Downloaded map to '{0}'", mapFilename);
Log.Write("debug", $"Downloaded map to '{mapFilename}'");
var package = mapInstallPackage.OpenPackage(mapFilename, modData.ModFiles);
if (package == null)
innerData.Status = MapStatus.DownloadError;
else
{
UpdateFromMap(package, mapInstallPackage, MapClassification.User, null, GridType);
UpdateFromMap(package, mapInstallPackage, MapClassification.User, null, GridType, null);
Game.RunAfterTick(onSuccess);
}
}
catch (Exception e)
{
Log.Write("debug", "Map installation failed with error: {0}", e);
Log.Write("debug", "Map installation failed with error:");
Log.Write("debug", e);
innerData.Status = MapStatus.DownloadError;
}
});

Some files were not shown because too many files have changed in this diff Show More